AzureMonitor: Ensure original query properties are included unchanged (#49642)

* Ensure original query properties are included unchanged

* Remove refId from pseudo datasources

* Include testing for interpolateVariablesInQueries

- Add util function to create template variables
- Update mock query with missing props
- Additional tests on each ds for template variables

* Correct typo and add explicit check for datasource
This commit is contained in:
Andreas Christou 2022-05-26 14:53:12 +01:00 committed by GitHub
parent 1308e28d8b
commit c8094b33cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 176 additions and 8 deletions

View File

@ -7,10 +7,12 @@ export default function createMockQuery(): AzureMonitorQuery {
'//change this example to create your own time series query\n<table name> //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full charts time range, choose the datetime column here\n| summarize count() by <group by column>, bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc', '//change this example to create your own time series query\n<table name> //the table to query (e.g. Usage, Heartbeat, Perf)\n| where $__timeFilter(TimeGenerated) //this is a macro used to show the full charts time range, choose the datetime column here\n| summarize count() by <group by column>, bin(TimeGenerated, $__interval) //change “group by column” to a column in your table, such as “Computer”. The $__interval macro is used to auto-select the time grain. Can also use 1h, 5m etc.\n| order by TimeGenerated asc',
resultFormat: 'time_series', resultFormat: 'time_series',
workspace: 'e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2', workspace: 'e3fe4fde-ad5e-4d60-9974-e2f3562ffdf2',
resource: 'test-resource',
}, },
azureResourceGraph: { azureResourceGraph: {
query: 'Resources | summarize count()', query: 'Resources | summarize count()',
resultFormat: 'table',
}, },
azureMonitor: { azureMonitor: {
@ -36,5 +38,9 @@ export default function createMockQuery(): AzureMonitorQuery {
refId: 'A', refId: 'A',
subscription: '99999999-cccc-bbbb-aaaa-9106972f9572', subscription: '99999999-cccc-bbbb-aaaa-9106972f9572',
subscriptions: ['99999999-cccc-bbbb-aaaa-9106972f9572'], subscriptions: ['99999999-cccc-bbbb-aaaa-9106972f9572'],
datasource: {
type: 'grafana-azure-monitor-datasource',
uid: 'AAAAA11111BBBBB22222CCCC',
},
}; };
} }

View File

@ -0,0 +1,40 @@
import { VariableType } from '@grafana/data';
import { LoadingState } from '@grafana/data/src/types/data';
import { VariableWithOptions } from 'app/features/variables/types';
interface TemplateableValue {
variableName: string;
templateVariable: VariableWithOptions;
}
export function createTemplateVariables(templateableProps: string[]): Map<string, TemplateableValue> {
const templateVariables = new Map<string, TemplateableValue>();
templateableProps.map((prop) => {
const variableName = prop.replace(/[\[\].]/g, '');
const templateVariable = {
current: {
selected: false,
text: `${variableName}-template-variable`,
value: `${variableName}-template-variable`,
},
id: variableName,
name: variableName,
type: 'textbox' as VariableType,
options: [],
query: '',
rootStateKey: null,
global: false,
hide: 0,
skipUrlSync: false,
index: 0,
state: 'Done' as LoadingState,
error: null,
description: null,
};
templateVariables.set(prop, {
variableName,
templateVariable,
});
});
return templateVariables;
}

View File

@ -1,7 +1,10 @@
import { get, set } from 'lodash';
import { toUtc } from '@grafana/data'; import { toUtc } from '@grafana/data';
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import createMockQuery from '../__mocks__/query'; import createMockQuery from '../__mocks__/query';
import { createTemplateVariables } from '../__mocks__/utils';
import { singleVariable } from '../__mocks__/variables'; import { singleVariable } from '../__mocks__/variables';
import AzureMonitorDatasource from '../datasource'; import AzureMonitorDatasource from '../datasource';
import { AzureMonitorQuery, AzureQueryType, DatasourceValidationResult } from '../types'; import { AzureMonitorQuery, AzureQueryType, DatasourceValidationResult } from '../types';
@ -369,4 +372,38 @@ describe('AzureLogAnalyticsDatasource', () => {
expect(laDatasource.filterQuery(query)).toBeFalsy(); expect(laDatasource.filterQuery(query)).toBeFalsy();
}); });
}); });
describe('When performing interpolateVariablesInQueries for azure_log_analytics', () => {
beforeEach(() => {
templateSrv.init([]);
});
it('should return a query unchanged if no template variables are provided', () => {
const query = createMockQuery();
query.queryType = AzureQueryType.LogAnalytics;
const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {});
expect(templatedQuery[0]).toEqual(query);
});
it('should return a query with any template variables replaced', () => {
const templateableProps = ['resource', 'workspace', 'query'];
const templateVariables = createTemplateVariables(templateableProps);
templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable));
const query = createMockQuery();
const azureLogAnalytics: { [index: string]: any } = {};
for (const [path, templateVariable] of templateVariables.entries()) {
set(azureLogAnalytics, path, `$${templateVariable.variableName}`);
}
query.queryType = AzureQueryType.LogAnalytics;
query.azureLogAnalytics = {
...query.azureLogAnalytics,
...azureLogAnalytics,
};
const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {});
expect(templatedQuery[0]).toHaveProperty('datasource');
for (const [path, templateVariable] of templateVariables.entries()) {
expect(get(templatedQuery[0].azureLogAnalytics, path)).toEqual(templateVariable.templateVariable.current.value);
}
});
});
}); });

View File

@ -134,7 +134,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
const query = templateSrv.replace(item.query, scopedVars, interpolateVariable); const query = templateSrv.replace(item.query, scopedVars, interpolateVariable);
return { return {
refId: target.refId, ...target,
queryType: AzureQueryType.LogAnalytics, queryType: AzureQueryType.LogAnalytics,
azureLogAnalytics: { azureLogAnalytics: {

View File

@ -1,9 +1,10 @@
import { startsWith } from 'lodash'; import { startsWith, get, set } from 'lodash';
import { DataSourceInstanceSettings } from '@grafana/data'; import { DataSourceInstanceSettings } from '@grafana/data';
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import createMockQuery from '../__mocks__/query'; import createMockQuery from '../__mocks__/query';
import { createTemplateVariables } from '../__mocks__/utils';
import { singleVariable, subscriptionsVariable } from '../__mocks__/variables'; import { singleVariable, subscriptionsVariable } from '../__mocks__/variables';
import AzureMonitorDatasource from '../datasource'; import AzureMonitorDatasource from '../datasource';
import { AzureDataSourceJsonData, AzureQueryType, DatasourceValidationResult } from '../types'; import { AzureDataSourceJsonData, AzureQueryType, DatasourceValidationResult } from '../types';
@ -259,6 +260,50 @@ describe('AzureMonitorDatasource', () => {
}); });
}); });
describe('When performing interpolateVariablesInQueries for azure_monitor_metrics', () => {
beforeEach(() => {
templateSrv.init([]);
});
it('should return a query unchanged if no template variables are provided', () => {
const query = createMockQuery();
const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {});
expect(templatedQuery[0]).toEqual(query);
});
it('should return a query with any template variables replaced', () => {
const templateableProps = [
'resourceUri',
'resourceGroup',
'resourceName',
'metricNamespace',
'metricDefinition',
'timeGrain',
'aggregation',
'top',
'dimensionFilters[0].dimension',
'dimensionFilters[0].filters[0]',
];
const templateVariables = createTemplateVariables(templateableProps);
templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable));
const query = createMockQuery();
const azureMonitorQuery: { [index: string]: any } = {};
for (const [path, templateVariable] of templateVariables.entries()) {
set(azureMonitorQuery, path, `$${templateVariable.variableName}`);
}
query.azureMonitor = {
...query.azureMonitor,
...azureMonitorQuery,
};
const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {});
expect(templatedQuery[0]).toHaveProperty('datasource');
for (const [path, templateVariable] of templateVariables.entries()) {
expect(get(templatedQuery[0].azureMonitor, path)).toEqual(templateVariable.templateVariable.current.value);
}
});
});
describe('Legacy Azure Monitor Query Object data fetchers', () => { describe('Legacy Azure Monitor Query Object data fetchers', () => {
describe('When performing getSubscriptions', () => { describe('When performing getSubscriptions', () => {
const response = { const response = {

View File

@ -114,7 +114,7 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
}); });
return { return {
refId: target.refId, ...target,
subscription: subscriptionId, subscription: subscriptionId,
queryType: AzureQueryType.AzureMonitor, queryType: AzureQueryType.AzureMonitor,
azureMonitor: { azureMonitor: {

View File

@ -1,7 +1,10 @@
import { set, get } from 'lodash';
import { backendSrv } from 'app/core/services/backend_srv'; import { backendSrv } from 'app/core/services/backend_srv';
import { TemplateSrv } from 'app/features/templating/template_srv'; import { TemplateSrv } from 'app/features/templating/template_srv';
import createMockQuery from '../__mocks__/query'; import createMockQuery from '../__mocks__/query';
import { createTemplateVariables } from '../__mocks__/utils';
import { multiVariable, singleVariable, subscriptionsVariable } from '../__mocks__/variables'; import { multiVariable, singleVariable, subscriptionsVariable } from '../__mocks__/variables';
import AzureMonitorDatasource from '../datasource'; import AzureMonitorDatasource from '../datasource';
import { AzureQueryType } from '../types'; import { AzureQueryType } from '../types';
@ -13,7 +16,6 @@ const templateSrv = new TemplateSrv({
getVariableWithName: jest.fn(), getVariableWithName: jest.fn(),
getFilteredVariables: jest.fn(), getFilteredVariables: jest.fn(),
}); });
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', () => ({
@ -41,7 +43,48 @@ describe('AzureResourceGraphDatasource', () => {
ctx.ds = new AzureResourceGraphDatasource(ctx.instanceSettings); ctx.ds = new AzureResourceGraphDatasource(ctx.instanceSettings);
}); });
describe('When performing interpolateVariablesInQueries for azure_resource_graph', () => {
beforeEach(() => {
templateSrv.init([]);
});
it('should return a query unchanged if no template variables are provided', () => {
const query = createMockQuery();
query.queryType = AzureQueryType.AzureResourceGraph;
const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {});
expect(templatedQuery[0]).toEqual(query);
});
it('should return a query with any template variables replaced', () => {
const templateableProps = ['query'];
const templateVariables = createTemplateVariables(templateableProps);
templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable));
const query = createMockQuery();
const azureResourceGraph: { [index: string]: any } = {};
for (const [path, templateVariable] of templateVariables.entries()) {
set(azureResourceGraph, path, `$${templateVariable.variableName}`);
}
query.queryType = AzureQueryType.AzureResourceGraph;
query.azureResourceGraph = {
...query.azureResourceGraph,
...azureResourceGraph,
};
const templatedQuery = ctx.ds.interpolateVariablesInQueries([query], {});
expect(templatedQuery[0]).toHaveProperty('datasource');
for (const [path, templateVariable] of templateVariables.entries()) {
expect(get(templatedQuery[0].azureResourceGraph, path)).toEqual(
templateVariable.templateVariable.current.value
);
}
});
});
describe('When applying template variables', () => { describe('When applying template variables', () => {
beforeEach(() => {
templateSrv.init([subscriptionsVariable, singleVariable, multiVariable]);
});
it('should expand single value template variable', () => { it('should expand single value template variable', () => {
const target = { const target = {
azureResourceGraph: { azureResourceGraph: {
@ -52,7 +95,6 @@ describe('AzureResourceGraphDatasource', () => {
expect(ctx.ds.applyTemplateVariables(target)).toStrictEqual({ expect(ctx.ds.applyTemplateVariables(target)).toStrictEqual({
azureResourceGraph: { query: 'Resources | var1-foo', resultFormat: 'table' }, azureResourceGraph: { query: 'Resources | var1-foo', resultFormat: 'table' },
queryType: 'Azure Resource Graph', queryType: 'Azure Resource Graph',
refId: undefined,
subscriptions: [], subscriptions: [],
}); });
}); });
@ -70,7 +112,6 @@ describe('AzureResourceGraphDatasource', () => {
resultFormat: 'table', resultFormat: 'table',
}, },
queryType: 'Azure Resource Graph', queryType: 'Azure Resource Graph',
refId: undefined,
subscriptions: [], subscriptions: [],
}); });
}); });
@ -90,7 +131,6 @@ describe('AzureResourceGraphDatasource', () => {
resultFormat: 'table', resultFormat: 'table',
}, },
queryType: 'Azure Resource Graph', queryType: 'Azure Resource Graph',
refId: undefined,
subscriptions: ['sub-foo', 'sub-baz'], subscriptions: ['sub-foo', 'sub-baz'],
}); });
}); });

View File

@ -35,7 +35,7 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend<
const query = templateSrv.replace(item.query, scopedVars, interpolateVariable); const query = templateSrv.replace(item.query, scopedVars, interpolateVariable);
return { return {
refId: target.refId, ...target,
queryType: AzureQueryType.AzureResourceGraph, queryType: AzureQueryType.AzureResourceGraph,
subscriptions, subscriptions,
azureResourceGraph: { azureResourceGraph: {