AzureMonitor: Remove dependencies on Grafana frontend (#75595)
* remove local graf templateSrv and use @grafana/runtime * tests broken still 0.0 * wip * moving around tests and fixing * template var test using mocking and stuff * AM ds tests fixed * fix filter tests * all tests work * remove comment * Update azure_log_analytics_datasource.ts * remove deferred util - not used * mock instead of spying * not calling getTemplateSrv each time * pass range down to filter instead of doing werid stuff * use default time range if no time range * prettierer * remove note
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import { DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { ContextSrv } from 'app/core/services/context_srv';
|
||||
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import Datasource from '../datasource';
|
||||
import { AzureDataSourceJsonData } from '../types';
|
||||
@@ -20,15 +18,12 @@ export function createContext(overrides?: DeepPartial<Context>): Context {
|
||||
const instanceSettings = createMockInstanceSetttings(overrides?.instanceSettings);
|
||||
return {
|
||||
instanceSettings,
|
||||
templateSrv: new TemplateSrv(),
|
||||
templateSrv: getTemplateSrv(),
|
||||
datasource: new Datasource(instanceSettings),
|
||||
getResource: jest.fn(),
|
||||
};
|
||||
}
|
||||
|
||||
const contextSrv = new ContextSrv();
|
||||
const timeSrv = new TimeSrv(contextSrv);
|
||||
|
||||
export default function createMockDatasource(overrides?: DeepPartial<Datasource>) {
|
||||
// We make this a partial so we get _some_ kind of type safety when making this, rather than
|
||||
// having it be any or casted immediately to Datasource
|
||||
@@ -71,7 +66,6 @@ export default function createMockDatasource(overrides?: DeepPartial<Datasource>
|
||||
azureLogAnalyticsDatasource: {
|
||||
getKustoSchema: () => Promise.resolve(),
|
||||
getDeprecatedDefaultWorkSpace: () => 'defaultWorkspaceId',
|
||||
timeSrv,
|
||||
},
|
||||
resourcePickerData: {
|
||||
getSubscriptions: () => jest.fn().mockResolvedValue([]),
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { VariableType } from '@grafana/data';
|
||||
import { VariableType, VariableWithOptions } from '@grafana/data';
|
||||
import { LoadingState } from '@grafana/data/src/types/data';
|
||||
import { VariableWithOptions } from 'app/features/variables/types';
|
||||
|
||||
interface TemplateableValue {
|
||||
variableName: string;
|
||||
|
||||
@@ -1,4 +1,18 @@
|
||||
import { CustomVariableModel, initialVariableModelState, VariableHide } from 'app/features/variables/types';
|
||||
import { BaseVariableModel, CustomVariableModel, LoadingState, VariableHide } from '@grafana/data';
|
||||
|
||||
const initialVariableModelState: BaseVariableModel = {
|
||||
id: '00000000-0000-0000-0000-000000000000',
|
||||
rootStateKey: null,
|
||||
name: '',
|
||||
type: 'query',
|
||||
global: false,
|
||||
index: -1,
|
||||
hide: VariableHide.dontHide,
|
||||
skipUrlSync: false,
|
||||
state: LoadingState.NotStarted,
|
||||
error: null,
|
||||
description: null,
|
||||
};
|
||||
|
||||
export const subscriptionsVariable: CustomVariableModel = {
|
||||
...initialVariableModelState,
|
||||
|
||||
+66
-71
@@ -1,37 +1,43 @@
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { CustomVariableModel } from '@grafana/data';
|
||||
|
||||
import { Context, createContext } from '../__mocks__/datasource';
|
||||
import createMockQuery from '../__mocks__/query';
|
||||
import { createTemplateVariables } from '../__mocks__/utils';
|
||||
import { singleVariable } from '../__mocks__/variables';
|
||||
import AzureMonitorDatasource from '../datasource';
|
||||
import { AzureLogsQuery, AzureMonitorQuery, AzureQueryType, AzureTracesQuery } from '../types';
|
||||
|
||||
import FakeSchemaData from './__mocks__/schema';
|
||||
import AzureLogAnalyticsDatasource from './azure_log_analytics_datasource';
|
||||
|
||||
const templateSrv = new TemplateSrv();
|
||||
let getTempVars = () => [] as CustomVariableModel[];
|
||||
let replace = () => '';
|
||||
|
||||
jest.mock('app/core/services/backend_srv');
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getTemplateSrv: () => templateSrv,
|
||||
}));
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getTemplateSrv: () => ({
|
||||
replace: replace,
|
||||
getVariables: getTempVars,
|
||||
updateTimeRange: jest.fn(),
|
||||
containsTemplate: jest.fn(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('AzureLogAnalyticsDatasource', () => {
|
||||
let ctx: Context;
|
||||
|
||||
beforeEach(() => {
|
||||
templateSrv.init([singleVariable]);
|
||||
templateSrv.getVariables = jest.fn().mockReturnValue([singleVariable]);
|
||||
ctx = createContext({
|
||||
instanceSettings: { jsonData: { subscriptionId: 'xxx' }, url: 'http://azureloganalyticsapi' },
|
||||
});
|
||||
ctx.templateSrv = templateSrv;
|
||||
});
|
||||
|
||||
describe('When performing getSchema', () => {
|
||||
beforeEach(() => {
|
||||
getTempVars = () => [] as CustomVariableModel[];
|
||||
replace = (target?: string) => target || '';
|
||||
ctx = createContext();
|
||||
ctx.getResource = jest.fn().mockImplementation((path: string) => {
|
||||
expect(path).toContain('metadata');
|
||||
return Promise.resolve(FakeSchemaData.getlogAnalyticsFakeMetadata());
|
||||
@@ -71,6 +77,13 @@ describe('AzureLogAnalyticsDatasource', () => {
|
||||
});
|
||||
|
||||
it('should interpolate variables when making a request for a schema with a uri that contains template variables', async () => {
|
||||
replace = () => 'myWorkspace/var1-foo';
|
||||
ctx = createContext();
|
||||
ctx.getResource = jest.fn().mockImplementation((path: string) => {
|
||||
expect(path).toContain('metadata');
|
||||
return Promise.resolve(FakeSchemaData.getlogAnalyticsFakeMetadata());
|
||||
});
|
||||
ctx.datasource.azureLogAnalyticsDatasource.getResource = ctx.getResource;
|
||||
await ctx.datasource.azureLogAnalyticsDatasource.getKustoSchema('myWorkspace/$var1');
|
||||
expect(ctx.getResource).lastCalledWith('loganalytics/v1myWorkspace/var1-foo/metadata');
|
||||
});
|
||||
@@ -89,7 +102,15 @@ describe('AzureLogAnalyticsDatasource', () => {
|
||||
});
|
||||
|
||||
it('should include template variables as global parameters', async () => {
|
||||
getTempVars = () => [singleVariable];
|
||||
ctx = createContext();
|
||||
ctx.getResource = jest.fn().mockImplementation((path: string) => {
|
||||
expect(path).toContain('metadata');
|
||||
return Promise.resolve(FakeSchemaData.getlogAnalyticsFakeMetadata());
|
||||
});
|
||||
ctx.datasource.azureLogAnalyticsDatasource.getResource = ctx.getResource;
|
||||
const result = await ctx.datasource.azureLogAnalyticsDatasource.getKustoSchema('myWorkspace');
|
||||
|
||||
expect(result.globalScalarParameters?.map((f: { name: string }) => f.name)).toEqual([`$${singleVariable.name}`]);
|
||||
});
|
||||
});
|
||||
@@ -129,37 +150,6 @@ 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 = { resources: [`$${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', () => {
|
||||
let laDatasource: AzureLogAnalyticsDatasource;
|
||||
|
||||
@@ -248,7 +238,9 @@ describe('AzureLogAnalyticsDatasource', () => {
|
||||
|
||||
describe('When performing interpolateVariablesInQueries for azure_log_analytics', () => {
|
||||
beforeEach(() => {
|
||||
templateSrv.init([]);
|
||||
getTempVars = () => [] as CustomVariableModel[];
|
||||
replace = (target?: string) => target || '';
|
||||
ctx = createContext();
|
||||
});
|
||||
|
||||
it('should return a query unchanged if no template variables are provided', () => {
|
||||
@@ -259,14 +251,18 @@ describe('AzureLogAnalyticsDatasource', () => {
|
||||
});
|
||||
|
||||
it('should return a logs 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));
|
||||
replace = (target?: string) => {
|
||||
if (target === '$var') {
|
||||
return 'template-variable';
|
||||
}
|
||||
return target || '';
|
||||
};
|
||||
ctx = createContext();
|
||||
const query = createMockQuery();
|
||||
const azureLogAnalytics: Partial<AzureLogsQuery> = {};
|
||||
azureLogAnalytics.query = '$query';
|
||||
azureLogAnalytics.workspace = '$workspace';
|
||||
azureLogAnalytics.resources = ['$resource'];
|
||||
azureLogAnalytics.query = '$var';
|
||||
azureLogAnalytics.workspace = '$var';
|
||||
azureLogAnalytics.resources = ['$var'];
|
||||
query.queryType = AzureQueryType.LogAnalytics;
|
||||
query.azureLogAnalytics = {
|
||||
...query.azureLogAnalytics,
|
||||
@@ -275,15 +271,15 @@ describe('AzureLogAnalyticsDatasource', () => {
|
||||
const templatedQuery = ctx.datasource.interpolateVariablesInQueries([query], {});
|
||||
expect(templatedQuery[0]).toHaveProperty('datasource');
|
||||
expect(templatedQuery[0].azureLogAnalytics).toMatchObject({
|
||||
query: templateVariables.get('query')?.templateVariable.current.value,
|
||||
workspace: templateVariables.get('workspace')?.templateVariable.current.value,
|
||||
resources: [templateVariables.get('resource')?.templateVariable.current.value],
|
||||
query: 'template-variable',
|
||||
workspace: 'template-variable',
|
||||
resources: ['template-variable'],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a logs query with multiple resources template variables replaced', () => {
|
||||
const templateVariables = createTemplateVariables(['resource'], 'resource1,resource2');
|
||||
templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable));
|
||||
replace = () => 'resource1,resource2';
|
||||
ctx = createContext();
|
||||
const query = createMockQuery();
|
||||
const azureLogAnalytics: Partial<AzureLogsQuery> = {};
|
||||
azureLogAnalytics.resources = ['$resource'];
|
||||
@@ -300,16 +296,15 @@ describe('AzureLogAnalyticsDatasource', () => {
|
||||
});
|
||||
|
||||
it('should return a traces query with any template variables replaced', () => {
|
||||
const templateableProps = ['resource', 'query', 'traceTypes', 'property', 'operation', 'filter', 'operationId'];
|
||||
const templateVariables = createTemplateVariables(templateableProps);
|
||||
templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable));
|
||||
replace = (target?: string) => (target === '$var' ? 'template-variable' : target || '');
|
||||
ctx = createContext();
|
||||
const query = createMockQuery();
|
||||
const azureTraces: Partial<AzureTracesQuery> = {};
|
||||
azureTraces.resources = ['$resource'];
|
||||
azureTraces.query = '$query';
|
||||
azureTraces.traceTypes = ['$traceTypes'];
|
||||
azureTraces.filters = [{ filters: ['$filter'], operation: 'eq', property: '$property' }];
|
||||
azureTraces.operationId = '$operationId';
|
||||
azureTraces.resources = ['$var'];
|
||||
azureTraces.query = '$var';
|
||||
azureTraces.traceTypes = ['$var'];
|
||||
azureTraces.filters = [{ filters: ['$var'], operation: 'eq', property: '$var' }];
|
||||
azureTraces.operationId = '$var';
|
||||
query.queryType = AzureQueryType.AzureTraces;
|
||||
query.azureTraces = {
|
||||
...query.azureTraces,
|
||||
@@ -319,23 +314,23 @@ describe('AzureLogAnalyticsDatasource', () => {
|
||||
const templatedQuery = ctx.datasource.interpolateVariablesInQueries([query], {});
|
||||
expect(templatedQuery[0]).toHaveProperty('datasource');
|
||||
expect(templatedQuery[0].azureTraces).toMatchObject({
|
||||
query: templateVariables.get('query')?.templateVariable.current.value,
|
||||
resources: [templateVariables.get('resource')?.templateVariable.current.value],
|
||||
operationId: templateVariables.get('operationId')?.templateVariable.current.value,
|
||||
traceTypes: [templateVariables.get('traceTypes')?.templateVariable.current.value],
|
||||
query: 'template-variable',
|
||||
resources: ['template-variable'],
|
||||
operationId: 'template-variable',
|
||||
traceTypes: ['template-variable'],
|
||||
filters: [
|
||||
{
|
||||
filters: [templateVariables.get('filter')?.templateVariable.current.value],
|
||||
filters: ['template-variable'],
|
||||
operation: 'eq',
|
||||
property: templateVariables.get('property')?.templateVariable.current.value,
|
||||
property: 'template-variable',
|
||||
},
|
||||
],
|
||||
});
|
||||
});
|
||||
|
||||
it('should return a trace query with multiple resources template variables replaced', () => {
|
||||
const templateVariables = createTemplateVariables(['resource'], 'resource1,resource2');
|
||||
templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable));
|
||||
replace = () => 'resource1,resource2';
|
||||
ctx = createContext();
|
||||
const query = createMockQuery();
|
||||
const azureTraces: Partial<AzureTracesQuery> = {};
|
||||
azureTraces.resources = ['$resource'];
|
||||
|
||||
+17
-21
@@ -1,8 +1,7 @@
|
||||
import { map } from 'lodash';
|
||||
|
||||
import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data';
|
||||
import { DataSourceWithBackend, getTemplateSrv } from '@grafana/runtime';
|
||||
import { TimeSrv, getTimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import ResponseParser from '../azure_monitor/response_parser';
|
||||
import { getAuthType, getAzureCloud, getAzurePortalUrl } from '../credentials';
|
||||
@@ -33,9 +32,10 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
azureMonitorPath: string;
|
||||
firstWorkspace?: string;
|
||||
|
||||
readonly timeSrv: TimeSrv = getTimeSrv();
|
||||
|
||||
constructor(private instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>) {
|
||||
constructor(
|
||||
private instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>,
|
||||
private readonly templateSrv: TemplateSrv = getTemplateSrv()
|
||||
) {
|
||||
super(instanceSettings);
|
||||
|
||||
this.resourcePath = `${routeNames.logAnalytics}`;
|
||||
@@ -85,7 +85,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
}
|
||||
|
||||
private getWorkspaceList(subscription: string): Promise<AzureAPIResponse<Workspace>> {
|
||||
const subscriptionId = getTemplateSrv().replace(subscription || this.defaultSubscriptionId);
|
||||
const subscriptionId = this.templateSrv.replace(subscription || this.defaultSubscriptionId);
|
||||
|
||||
const workspaceListUrl =
|
||||
this.azureMonitorPath +
|
||||
@@ -101,25 +101,23 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
}
|
||||
|
||||
async getKustoSchema(resourceUri: string) {
|
||||
const templateSrv = getTemplateSrv();
|
||||
const interpolatedUri = templateSrv.replace(resourceUri, {}, interpolateVariable);
|
||||
const interpolatedUri = this.templateSrv.replace(resourceUri, {}, interpolateVariable);
|
||||
const metadata = await this.getMetadata(interpolatedUri);
|
||||
return transformMetadataToKustoSchema(metadata, interpolatedUri, templateSrv.getVariables());
|
||||
return transformMetadataToKustoSchema(metadata, interpolatedUri, this.templateSrv.getVariables());
|
||||
}
|
||||
|
||||
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery {
|
||||
let item;
|
||||
if (target.queryType === AzureQueryType.LogAnalytics && target.azureLogAnalytics) {
|
||||
item = target.azureLogAnalytics;
|
||||
const templateSrv = getTemplateSrv();
|
||||
const resources = this.expandResourcesForMultipleVariables(item.resources, scopedVars);
|
||||
let workspace = templateSrv.replace(item.workspace, scopedVars);
|
||||
let workspace = this.templateSrv.replace(item.workspace, scopedVars);
|
||||
|
||||
if (!workspace && !resources && this.firstWorkspace) {
|
||||
workspace = this.firstWorkspace;
|
||||
}
|
||||
|
||||
const query = templateSrv.replace(item.query, scopedVars, interpolateVariable);
|
||||
const query = this.templateSrv.replace(item.query, scopedVars, interpolateVariable);
|
||||
|
||||
return {
|
||||
...target,
|
||||
@@ -132,23 +130,22 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
// Workspace was removed in Grafana 8, but remains for backwards compat
|
||||
workspace,
|
||||
dashboardTime: item.dashboardTime,
|
||||
timeColumn: templateSrv.replace(item.timeColumn, scopedVars),
|
||||
timeColumn: this.templateSrv.replace(item.timeColumn, scopedVars),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (target.queryType === AzureQueryType.AzureTraces && target.azureTraces) {
|
||||
item = target.azureTraces;
|
||||
const templateSrv = getTemplateSrv();
|
||||
const resources = this.expandResourcesForMultipleVariables(item.resources, scopedVars);
|
||||
const query = templateSrv.replace(item.query, scopedVars, interpolateVariable);
|
||||
const traceTypes = item.traceTypes?.map((t) => templateSrv.replace(t, scopedVars));
|
||||
const query = this.templateSrv.replace(item.query, scopedVars, interpolateVariable);
|
||||
const traceTypes = item.traceTypes?.map((t) => this.templateSrv.replace(t, scopedVars));
|
||||
const filters = (item.filters ?? [])
|
||||
.filter((f) => !!f.property)
|
||||
.map((f) => {
|
||||
const filtersReplaced = f.filters?.map((filter) => templateSrv.replace(filter ?? '', scopedVars));
|
||||
const filtersReplaced = f.filters?.map((filter) => this.templateSrv.replace(filter ?? '', scopedVars));
|
||||
return {
|
||||
property: templateSrv.replace(f.property, scopedVars),
|
||||
property: this.templateSrv.replace(f.property, scopedVars),
|
||||
operation: f.operation || 'eq',
|
||||
filters: filtersReplaced || [],
|
||||
};
|
||||
@@ -162,7 +159,7 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
resultFormat: item.resultFormat,
|
||||
query,
|
||||
resources,
|
||||
operationId: templateSrv.replace(target.azureTraces?.operationId, scopedVars),
|
||||
operationId: this.templateSrv.replace(target.azureTraces?.operationId, scopedVars),
|
||||
filters,
|
||||
traceTypes,
|
||||
},
|
||||
@@ -180,9 +177,8 @@ export default class AzureLogAnalyticsDatasource extends DataSourceWithBackend<
|
||||
return undefined;
|
||||
}
|
||||
const expandedResources: string[] = [];
|
||||
const templateSrv = getTemplateSrv();
|
||||
resources.forEach((r: string) => {
|
||||
const tempVars = templateSrv.replace(r, scopedVars, 'raw');
|
||||
const tempVars = this.templateSrv.replace(r, scopedVars, 'raw');
|
||||
const values = tempVars.split(',');
|
||||
values.forEach((value) => {
|
||||
expandedResources.push(value);
|
||||
|
||||
+116
-112
@@ -1,20 +1,27 @@
|
||||
import { get, set } from 'lodash';
|
||||
|
||||
import { DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import createMockQuery from '../__mocks__/query';
|
||||
import { createTemplateVariables } from '../__mocks__/utils';
|
||||
import { multiVariable, singleVariable, subscriptionsVariable } from '../__mocks__/variables';
|
||||
import { multiVariable } from '../__mocks__/variables';
|
||||
import AzureMonitorDatasource from '../datasource';
|
||||
import { AzureAPIResponse, AzureDataSourceJsonData, AzureQueryType, Location } from '../types';
|
||||
import { AzureAPIResponse, AzureDataSourceJsonData, Location } from '../types';
|
||||
|
||||
const templateSrv = new TemplateSrv();
|
||||
let replace = () => '';
|
||||
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getTemplateSrv: () => templateSrv,
|
||||
}));
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getTemplateSrv: () => ({
|
||||
replace: replace,
|
||||
getVariables: jest.fn(),
|
||||
updateTimeRange: jest.fn(),
|
||||
containsTemplate: jest.fn(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
interface TestContext {
|
||||
instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>;
|
||||
@@ -79,6 +86,11 @@ describe('AzureMonitorDatasource', () => {
|
||||
});
|
||||
|
||||
describe('applyTemplateVariables', () => {
|
||||
beforeEach(() => {
|
||||
replace = (target?: string) => target || '';
|
||||
ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings);
|
||||
});
|
||||
|
||||
it('should migrate metricDefinition to metricNamespace', () => {
|
||||
const query = createMockQuery({
|
||||
azureMonitor: {
|
||||
@@ -99,15 +111,14 @@ describe('AzureMonitorDatasource', () => {
|
||||
const resourceGroup = 'cloud-datasources';
|
||||
const metricNamespace = 'microsoft.insights/components';
|
||||
const resourceName = 'AppInsightsTestData';
|
||||
templateSrv.init([
|
||||
{
|
||||
id: 'resourceUri',
|
||||
name: 'resourceUri',
|
||||
current: {
|
||||
value: `/subscriptions/${subscription}/resourceGroups/${resourceGroup}/providers/${metricNamespace}/${resourceName}`,
|
||||
},
|
||||
},
|
||||
]);
|
||||
replace = (target?: string) => {
|
||||
if (target?.includes('$resourceUri')) {
|
||||
return `/subscriptions/${subscription}/resourceGroups/${resourceGroup}/providers/${metricNamespace}/${resourceName}`;
|
||||
}
|
||||
return target || '';
|
||||
};
|
||||
ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings);
|
||||
|
||||
const query = createMockQuery({
|
||||
azureMonitor: {
|
||||
resourceUri: '$resourceUri',
|
||||
@@ -126,22 +137,16 @@ describe('AzureMonitorDatasource', () => {
|
||||
it('expand template variables in resource groups and names', () => {
|
||||
const resourceGroup = '$rg';
|
||||
const resourceName = '$rn';
|
||||
templateSrv.init([
|
||||
{
|
||||
id: 'rg',
|
||||
name: 'rg',
|
||||
current: {
|
||||
value: `rg1,rg2`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'rn',
|
||||
name: 'rn',
|
||||
current: {
|
||||
value: `rn1,rn2`,
|
||||
},
|
||||
},
|
||||
]);
|
||||
replace = (target?: string) => {
|
||||
if (target?.includes('$rg')) {
|
||||
return 'rg1,rg2';
|
||||
}
|
||||
if (target?.includes('$rn')) {
|
||||
return 'rn1,rn2';
|
||||
}
|
||||
return target || '';
|
||||
};
|
||||
ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings);
|
||||
const query = createMockQuery({
|
||||
azureMonitor: {
|
||||
resources: [{ resourceGroup, resourceName }],
|
||||
@@ -162,15 +167,14 @@ describe('AzureMonitorDatasource', () => {
|
||||
|
||||
it('expand template variables for a region', () => {
|
||||
const region = '$reg';
|
||||
templateSrv.init([
|
||||
{
|
||||
id: 'reg',
|
||||
name: 'reg',
|
||||
current: {
|
||||
value: `eastus`,
|
||||
},
|
||||
},
|
||||
]);
|
||||
replace = (target?: string) => {
|
||||
if (target?.includes('$reg')) {
|
||||
return 'eastus';
|
||||
}
|
||||
return target || '';
|
||||
};
|
||||
ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings);
|
||||
|
||||
const query = createMockQuery({
|
||||
azureMonitor: {
|
||||
region,
|
||||
@@ -185,29 +189,20 @@ describe('AzureMonitorDatasource', () => {
|
||||
});
|
||||
|
||||
it('should migrate legacy properties before interpolation', () => {
|
||||
templateSrv.init([
|
||||
{
|
||||
id: 'resourcegroup',
|
||||
name: 'resourcegroup',
|
||||
current: {
|
||||
value: `test-rg`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'resourcename',
|
||||
name: 'resourcename',
|
||||
current: {
|
||||
value: `test-resource`,
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'metric',
|
||||
name: 'metric',
|
||||
current: {
|
||||
value: `test-ns`,
|
||||
},
|
||||
},
|
||||
]);
|
||||
replace = (target?: string) => {
|
||||
if (target?.includes('$resourcegroup')) {
|
||||
return 'test-rg';
|
||||
}
|
||||
if (target?.includes('$resourcename')) {
|
||||
return 'test-resource';
|
||||
}
|
||||
if (target?.includes('$metric')) {
|
||||
return 'test-ns';
|
||||
}
|
||||
return target || '';
|
||||
};
|
||||
ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings);
|
||||
|
||||
const query = createMockQuery({
|
||||
azureMonitor: {
|
||||
metricDefinition: '$metric',
|
||||
@@ -434,15 +429,23 @@ describe('AzureMonitorDatasource', () => {
|
||||
});
|
||||
|
||||
it('should replace a template variable for the metric name', () => {
|
||||
templateSrv.init([
|
||||
{
|
||||
id: 'metric',
|
||||
name: 'metric',
|
||||
current: {
|
||||
value: 'UsedCapacity',
|
||||
},
|
||||
},
|
||||
]);
|
||||
replace = (target?: string) => {
|
||||
if (target?.includes('$metric')) {
|
||||
return 'UsedCapacity';
|
||||
}
|
||||
return target || '';
|
||||
};
|
||||
ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings);
|
||||
ctx.ds.azureMonitorDatasource.getResource = jest.fn().mockImplementation((path: string) => {
|
||||
const basePath = 'azuremonitor/subscriptions/mock-subscription-id/resourceGroups/nodeapp';
|
||||
const expected =
|
||||
basePath +
|
||||
'/providers/microsoft.insights/components/resource1' +
|
||||
'/providers/microsoft.insights/metricdefinitions?api-version=2018-01-01';
|
||||
expect(path).toBe(expected);
|
||||
return Promise.resolve(response);
|
||||
});
|
||||
|
||||
return ctx.ds.azureMonitorDatasource
|
||||
.getMetricMetadata({
|
||||
resourceUri:
|
||||
@@ -460,7 +463,8 @@ describe('AzureMonitorDatasource', () => {
|
||||
|
||||
describe('When performing interpolateVariablesInQueries for azure_monitor_metrics', () => {
|
||||
beforeEach(() => {
|
||||
templateSrv.init([]);
|
||||
replace = (target?: string) => target || '';
|
||||
ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings);
|
||||
});
|
||||
|
||||
it('should return a query unchanged if no template variables are provided', () => {
|
||||
@@ -481,7 +485,35 @@ describe('AzureMonitorDatasource', () => {
|
||||
'dimensionFilters[0].filters[0]',
|
||||
];
|
||||
const templateVariables = createTemplateVariables(templateableProps);
|
||||
templateSrv.init(Array.from(templateVariables.values()).map((item) => item.templateVariable));
|
||||
replace = (target?: string) => {
|
||||
if (target === '$resources0resourceGroup') {
|
||||
return 'resources0resourceGroup-template-variable';
|
||||
}
|
||||
if (target === '$resources0resourceName') {
|
||||
return 'resources0resourceName-template-variable';
|
||||
}
|
||||
if (target === '$metricNamespace') {
|
||||
return 'metricNamespace-template-variable';
|
||||
}
|
||||
if (target === '$timeGrain') {
|
||||
return 'timeGrain-template-variable';
|
||||
}
|
||||
if (target === '$aggregation') {
|
||||
return 'aggregation-template-variable';
|
||||
}
|
||||
if (target === '$top') {
|
||||
return 'top-template-variable';
|
||||
}
|
||||
if (target === '$dimensionFilters0dimension') {
|
||||
return 'dimensionFilters0dimension-template-variable';
|
||||
}
|
||||
if (target === '$dimensionFilters0filters0') {
|
||||
return 'dimensionFilters0filters0-template-variable';
|
||||
}
|
||||
return target || '';
|
||||
};
|
||||
ctx.ds = new AzureMonitorDatasource(ctx.instanceSettings);
|
||||
|
||||
const query = createMockQuery();
|
||||
const azureMonitorQuery = {};
|
||||
for (const [path, templateVariable] of templateVariables.entries()) {
|
||||
@@ -712,12 +744,14 @@ describe('AzureMonitorDatasource', () => {
|
||||
});
|
||||
|
||||
it('should return multiple resources from a template variable', () => {
|
||||
const tsrv = new TemplateSrv();
|
||||
tsrv.replace = jest
|
||||
.fn()
|
||||
.mockImplementation((value: string) => (value === `$${multiVariable.id}` ? 'foo,bar' : value ?? ''));
|
||||
const ds = new AzureMonitorDatasource(ctx.instanceSettings, templateSrv);
|
||||
ds.azureMonitorDatasource.templateSrv = tsrv;
|
||||
replace = (target?: string) => {
|
||||
if (target?.includes('$reg')) {
|
||||
return 'eastus';
|
||||
}
|
||||
return target === `$${multiVariable.id}` ? 'foo,bar' : target ?? '';
|
||||
};
|
||||
const ds = new AzureMonitorDatasource(ctx.instanceSettings);
|
||||
//ds.azureMonitorDatasource.templateSrv = tsrv;
|
||||
ds.azureMonitorDatasource.getResource = jest
|
||||
.fn()
|
||||
.mockImplementationOnce((path: string) => {
|
||||
@@ -1074,36 +1108,6 @@ 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 = { resources: [`$${singleVariable.name}`] };
|
||||
expect(ds.targetContainsTemplate(query)).toEqual(false);
|
||||
});
|
||||
});
|
||||
|
||||
it('should return an empty array for a Metric that does not have dimensions', () => {
|
||||
return ctx.ds.azureMonitorDatasource
|
||||
.getMetricMetadata({
|
||||
|
||||
+19
-20
@@ -3,7 +3,6 @@ import { find, startsWith } from 'lodash';
|
||||
|
||||
import { DataSourceInstanceSettings, ScopedVars } from '@grafana/data';
|
||||
import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
import { getTimeSrv, TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
|
||||
import { getAuthType, getAzureCloud, getAzurePortalUrl } from '../credentials';
|
||||
import TimegrainConverter from '../time_grain_converter';
|
||||
@@ -49,14 +48,13 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
azurePortalUrl: string;
|
||||
declare resourceGroup: string;
|
||||
declare resourceName: string;
|
||||
timeSrv: TimeSrv;
|
||||
templateSrv: TemplateSrv;
|
||||
|
||||
constructor(private instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>) {
|
||||
constructor(
|
||||
private instanceSettings: DataSourceInstanceSettings<AzureDataSourceJsonData>,
|
||||
private readonly templateSrv: TemplateSrv = getTemplateSrv()
|
||||
) {
|
||||
super(instanceSettings);
|
||||
|
||||
this.timeSrv = getTimeSrv();
|
||||
this.templateSrv = getTemplateSrv();
|
||||
this.defaultSubscriptionId = instanceSettings.jsonData.subscriptionId;
|
||||
|
||||
const cloud = getAzureCloud(instanceSettings);
|
||||
@@ -92,14 +90,12 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
throw new Error('Query is not a valid Azure Monitor Metrics query');
|
||||
}
|
||||
|
||||
const templateSrv = getTemplateSrv();
|
||||
|
||||
// These properties need to be replaced pre-migration to ensure values are correctly interpolated
|
||||
if (preMigrationQuery.resourceUri) {
|
||||
preMigrationQuery.resourceUri = templateSrv.replace(preMigrationQuery.resourceUri, scopedVars);
|
||||
preMigrationQuery.resourceUri = this.templateSrv.replace(preMigrationQuery.resourceUri, scopedVars);
|
||||
}
|
||||
if (preMigrationQuery.metricDefinition) {
|
||||
preMigrationQuery.metricDefinition = templateSrv.replace(preMigrationQuery.metricDefinition, scopedVars);
|
||||
preMigrationQuery.metricDefinition = this.templateSrv.replace(preMigrationQuery.metricDefinition, scopedVars);
|
||||
}
|
||||
|
||||
// fix for timeGrainUnit which is a deprecated/removed field name
|
||||
@@ -117,20 +113,23 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
throw new Error('Query is not a valid Azure Monitor Metrics query');
|
||||
}
|
||||
|
||||
const subscriptionId = templateSrv.replace(migratedTarget.subscription || this.defaultSubscriptionId, scopedVars);
|
||||
const subscriptionId = this.templateSrv.replace(
|
||||
migratedTarget.subscription || this.defaultSubscriptionId,
|
||||
scopedVars
|
||||
);
|
||||
const resources = migratedQuery.resources?.map((r) => this.replaceTemplateVariables(r, scopedVars)).flat();
|
||||
const metricNamespace = templateSrv.replace(migratedQuery.metricNamespace, scopedVars);
|
||||
const customNamespace = templateSrv.replace(migratedQuery.customNamespace, scopedVars);
|
||||
const timeGrain = templateSrv.replace((migratedQuery.timeGrain || '').toString(), scopedVars);
|
||||
const aggregation = templateSrv.replace(migratedQuery.aggregation, scopedVars);
|
||||
const top = templateSrv.replace(migratedQuery.top || '', scopedVars);
|
||||
const metricNamespace = this.templateSrv.replace(migratedQuery.metricNamespace, scopedVars);
|
||||
const customNamespace = this.templateSrv.replace(migratedQuery.customNamespace, scopedVars);
|
||||
const timeGrain = this.templateSrv.replace((migratedQuery.timeGrain || '').toString(), scopedVars);
|
||||
const aggregation = this.templateSrv.replace(migratedQuery.aggregation, scopedVars);
|
||||
const top = this.templateSrv.replace(migratedQuery.top || '', scopedVars);
|
||||
|
||||
const dimensionFilters = (migratedQuery.dimensionFilters ?? [])
|
||||
.filter((f) => f.dimension && f.dimension !== 'None')
|
||||
.map((f) => {
|
||||
const filters = f.filters?.map((filter) => templateSrv.replace(filter ?? '', scopedVars));
|
||||
const filters = f.filters?.map((filter) => this.templateSrv.replace(filter ?? '', scopedVars));
|
||||
return {
|
||||
dimension: templateSrv.replace(f.dimension, scopedVars),
|
||||
dimension: this.templateSrv.replace(f.dimension, scopedVars),
|
||||
operator: f.operator || 'eq',
|
||||
filters: filters || [],
|
||||
};
|
||||
@@ -143,8 +142,8 @@ export default class AzureMonitorDatasource extends DataSourceWithBackend<AzureM
|
||||
customNamespace,
|
||||
timeGrain,
|
||||
allowedTimeGrainsMs: migratedQuery.allowedTimeGrainsMs,
|
||||
metricName: templateSrv.replace(migratedQuery.metricName, scopedVars),
|
||||
region: templateSrv.replace(migratedQuery.region, scopedVars),
|
||||
metricName: this.templateSrv.replace(migratedQuery.metricName, scopedVars),
|
||||
region: this.templateSrv.replace(migratedQuery.region, scopedVars),
|
||||
aggregation: aggregation,
|
||||
dimensionFilters,
|
||||
top: top || '10',
|
||||
|
||||
+45
-81
@@ -1,63 +1,55 @@
|
||||
import { set, get } from 'lodash';
|
||||
|
||||
import { backendSrv } from 'app/core/services/backend_srv';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { CustomVariableModel } from '@grafana/data';
|
||||
|
||||
import { Context, createContext } from '../__mocks__/datasource';
|
||||
import createMockQuery from '../__mocks__/query';
|
||||
import { createTemplateVariables } from '../__mocks__/utils';
|
||||
import { multiVariable, singleVariable, subscriptionsVariable } from '../__mocks__/variables';
|
||||
import AzureMonitorDatasource from '../datasource';
|
||||
import { AzureQueryType } from '../types';
|
||||
|
||||
const templateSrv = new TemplateSrv({
|
||||
getVariables: () => [subscriptionsVariable, singleVariable, multiVariable],
|
||||
getVariableWithName: jest.fn(),
|
||||
getFilteredVariables: jest.fn(),
|
||||
let getTempVars = () => [] as CustomVariableModel[];
|
||||
let replace = () => '';
|
||||
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getTemplateSrv: () => ({
|
||||
replace: replace,
|
||||
getVariables: getTempVars,
|
||||
updateTimeRange: jest.fn(),
|
||||
containsTemplate: jest.fn(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
jest.mock('app/core/services/backend_srv');
|
||||
jest.mock('@grafana/runtime', () => ({
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getBackendSrv: () => backendSrv,
|
||||
getTemplateSrv: () => templateSrv,
|
||||
}));
|
||||
|
||||
describe('AzureResourceGraphDatasource', () => {
|
||||
const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
datasourceRequestMock.mockImplementation(jest.fn());
|
||||
});
|
||||
|
||||
let ctx: Context;
|
||||
|
||||
beforeEach(() => {
|
||||
ctx = createContext({
|
||||
instanceSettings: {
|
||||
url: 'http://azureresourcegraphapi',
|
||||
jsonData: { subscriptionId: '9935389e-9122-4ef9-95f9-1513dd24753f', cloudName: 'azuremonitor' },
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
describe('When performing interpolateVariablesInQueries for azure_resource_graph', () => {
|
||||
beforeEach(() => {
|
||||
templateSrv.init([]);
|
||||
ctx = createContext({
|
||||
instanceSettings: {
|
||||
url: 'http://azureresourcegraphapi',
|
||||
jsonData: { subscriptionId: '9935389e-9122-4ef9-95f9-1513dd24753f', cloudName: 'azuremonitor' },
|
||||
},
|
||||
});
|
||||
getTempVars = () => [] as CustomVariableModel[];
|
||||
replace = (target?: string) => target || '';
|
||||
});
|
||||
|
||||
it('should return a query unchanged if no template variables are provided', () => {
|
||||
const query = createMockQuery();
|
||||
query.queryType = AzureQueryType.AzureResourceGraph;
|
||||
const templatedQuery = ctx.datasource.azureResourceGraphDatasource.interpolateVariablesInQueries([query], {});
|
||||
expect(templatedQuery[0]).toEqual(query);
|
||||
const templatedQuery = ctx.datasource.interpolateVariablesInQueries([query], {});
|
||||
expect(templatedQuery).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));
|
||||
replace = () => 'query-template-variable';
|
||||
const query = createMockQuery();
|
||||
const azureResourceGraph = {};
|
||||
for (const [path, templateVariable] of templateVariables.entries()) {
|
||||
@@ -69,7 +61,7 @@ describe('AzureResourceGraphDatasource', () => {
|
||||
...query.azureResourceGraph,
|
||||
...azureResourceGraph,
|
||||
};
|
||||
const templatedQuery = ctx.datasource.azureResourceGraphDatasource.interpolateVariablesInQueries([query], {});
|
||||
const templatedQuery = ctx.datasource.interpolateVariablesInQueries([query], {});
|
||||
expect(templatedQuery[0]).toHaveProperty('datasource');
|
||||
for (const [path, templateVariable] of templateVariables.entries()) {
|
||||
expect(get(templatedQuery[0].azureResourceGraph, path)).toEqual(
|
||||
@@ -81,7 +73,8 @@ describe('AzureResourceGraphDatasource', () => {
|
||||
|
||||
describe('When applying template variables', () => {
|
||||
beforeEach(() => {
|
||||
templateSrv.init([subscriptionsVariable, singleVariable, multiVariable]);
|
||||
getTempVars = () => [] as CustomVariableModel[];
|
||||
replace = (target?: string) => target || '';
|
||||
});
|
||||
|
||||
it('should expand single value template variable', () => {
|
||||
@@ -92,6 +85,10 @@ describe('AzureResourceGraphDatasource', () => {
|
||||
resultFormat: '',
|
||||
},
|
||||
});
|
||||
getTempVars = () =>
|
||||
Array.from([subscriptionsVariable, singleVariable, multiVariable].values()).map((item) => item);
|
||||
replace = (target?: string | undefined) =>
|
||||
target === 'Resources | $var1' ? 'Resources | var1-foo' : target || '';
|
||||
expect(ctx.datasource.azureResourceGraphDatasource.applyTemplateVariables(target, {})).toEqual(
|
||||
expect.objectContaining({
|
||||
...target,
|
||||
@@ -110,6 +107,14 @@ describe('AzureResourceGraphDatasource', () => {
|
||||
resultFormat: '',
|
||||
},
|
||||
});
|
||||
getTempVars = () =>
|
||||
Array.from([subscriptionsVariable, singleVariable, multiVariable].values()).map((item) => item);
|
||||
replace = (target?: string | undefined) => {
|
||||
if (target === 'resources | where $__contains(name, $var3)') {
|
||||
return "resources | where $__contains(name, 'var3-foo','var3-baz')";
|
||||
}
|
||||
return target || '';
|
||||
};
|
||||
expect(ctx.datasource.azureResourceGraphDatasource.applyTemplateVariables(target, {})).toEqual(
|
||||
expect.objectContaining({
|
||||
...target,
|
||||
@@ -128,14 +133,16 @@ describe('AzureResourceGraphDatasource', () => {
|
||||
const target = createMockQuery({
|
||||
subscriptions: ['$subs'],
|
||||
azureResourceGraph: {
|
||||
query: 'resources | where $__contains(name, $var3)',
|
||||
query: 'resources | where $__contains(name)',
|
||||
resultFormat: '',
|
||||
},
|
||||
});
|
||||
getTempVars = () => Array.from([subscriptionsVariable, singleVariable, multiVariable].values()).map((item) => item);
|
||||
replace = (target?: string | undefined) => (target === '$subs' ? 'sub-foo,sub-baz' : target || '');
|
||||
expect(ctx.datasource.azureResourceGraphDatasource.applyTemplateVariables(target, {})).toEqual(
|
||||
expect.objectContaining({
|
||||
azureResourceGraph: {
|
||||
query: `resources | where $__contains(name, 'var3-foo','var3-baz')`,
|
||||
query: `resources | where $__contains(name)`,
|
||||
resultFormat: 'table',
|
||||
},
|
||||
queryType: 'Azure Resource Graph',
|
||||
@@ -143,47 +150,4 @@ describe('AzureResourceGraphDatasource', () => {
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
+4
-5
@@ -16,15 +16,14 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend<
|
||||
}
|
||||
|
||||
applyTemplateVariables(target: AzureMonitorQuery, scopedVars: ScopedVars): AzureMonitorQuery {
|
||||
const ts = getTemplateSrv();
|
||||
const item = target.azureResourceGraph;
|
||||
if (!item) {
|
||||
return target;
|
||||
}
|
||||
|
||||
const templateSrv = getTemplateSrv();
|
||||
const variableNames = templateSrv.getVariables().map((v) => `$${v.name}`);
|
||||
const variableNames = ts.getVariables().map((v) => `$${v.name}`);
|
||||
const subscriptionVar = _.find(target.subscriptions, (sub) => _.includes(variableNames, sub));
|
||||
const interpolatedSubscriptions = templateSrv
|
||||
const interpolatedSubscriptions = ts
|
||||
.replace(subscriptionVar, scopedVars, (v: string[] | string) => v)
|
||||
.split(',')
|
||||
.filter((v) => v.length > 0);
|
||||
@@ -32,7 +31,7 @@ export default class AzureResourceGraphDatasource extends DataSourceWithBackend<
|
||||
...interpolatedSubscriptions,
|
||||
..._.filter(target.subscriptions, (sub) => !_.includes(variableNames, sub)),
|
||||
];
|
||||
const query = templateSrv.replace(item.query, scopedVars, interpolateVariable);
|
||||
const query = ts.replace(item.query, scopedVars, interpolateVariable);
|
||||
|
||||
return {
|
||||
...target,
|
||||
|
||||
@@ -34,6 +34,7 @@ const QueryEditor = ({
|
||||
onChange,
|
||||
onRunQuery: baseOnRunQuery,
|
||||
data,
|
||||
range,
|
||||
}: AzureMonitorQueryEditorProps) => {
|
||||
const [errorMessage, setError] = useLastError();
|
||||
const onRunQuery = useMemo(() => debounce(baseOnRunQuery, 500), [baseOnRunQuery]);
|
||||
@@ -66,6 +67,7 @@ const QueryEditor = ({
|
||||
onChange={onQueryChange}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
setError={setError}
|
||||
range={range}
|
||||
/>
|
||||
|
||||
{errorMessage && (
|
||||
@@ -94,6 +96,7 @@ const EditorForQueryType = ({
|
||||
variableOptionGroup,
|
||||
onChange,
|
||||
setError,
|
||||
range,
|
||||
}: EditorForQueryTypeProps) => {
|
||||
switch (query.queryType) {
|
||||
case AzureQueryType.AzureMonitor:
|
||||
@@ -141,6 +144,7 @@ const EditorForQueryType = ({
|
||||
onChange={onChange}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
setError={setError}
|
||||
range={range}
|
||||
/>
|
||||
);
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ import { cx } from '@emotion/css';
|
||||
import React, { RefCallback, SyntheticEvent, useState } from 'react';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
import { CoreApp, DataFrame, SelectableValue, TimeRange } from '@grafana/data';
|
||||
import { CoreApp, DataFrame, getDefaultTimeRange, SelectableValue, TimeRange } from '@grafana/data';
|
||||
import { AccessoryButton } from '@grafana/experimental';
|
||||
import {
|
||||
HorizontalGroup,
|
||||
@@ -24,10 +24,10 @@ export interface FilterProps {
|
||||
datasource: Datasource;
|
||||
propertyMap: Map<string, SelectableValue[]>;
|
||||
setPropertyMap: React.Dispatch<React.SetStateAction<Map<string, Array<SelectableValue<string>>>>>;
|
||||
timeRange: TimeRange;
|
||||
queryTraceTypes: string[];
|
||||
properties: string[];
|
||||
variableOptionGroup: VariableOptionGroup;
|
||||
range?: TimeRange;
|
||||
}
|
||||
|
||||
const onFieldChange = <Key extends keyof AzureTracesFilter>(
|
||||
@@ -50,11 +50,11 @@ const onFieldChange = <Key extends keyof AzureTracesFilter>(
|
||||
const getTraceProperties = async (
|
||||
query: AzureMonitorQuery,
|
||||
datasource: Datasource,
|
||||
timeRange: TimeRange,
|
||||
traceTypes: string[],
|
||||
propertyMap: Map<string, SelectableValue[]>,
|
||||
setPropertyMap: React.Dispatch<React.SetStateAction<Map<string, Array<SelectableValue<string>>>>>,
|
||||
filter?: Partial<AzureTracesFilter>
|
||||
filter?: Partial<AzureTracesFilter>,
|
||||
range?: TimeRange
|
||||
): Promise<SelectableValue[]> => {
|
||||
const { azureTraces } = query;
|
||||
if (!azureTraces) {
|
||||
@@ -97,7 +97,7 @@ const getTraceProperties = async (
|
||||
queryType: AzureQueryType.LogAnalytics,
|
||||
},
|
||||
],
|
||||
range: timeRange,
|
||||
range: range || getDefaultTimeRange(),
|
||||
})
|
||||
);
|
||||
if (results.data.length > 0) {
|
||||
@@ -191,13 +191,13 @@ const Filter = (
|
||||
datasource,
|
||||
propertyMap,
|
||||
setPropertyMap,
|
||||
timeRange,
|
||||
queryTraceTypes,
|
||||
properties,
|
||||
item,
|
||||
onChange,
|
||||
onDelete,
|
||||
variableOptionGroup,
|
||||
range,
|
||||
} = props;
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [values, setValues] = useState<Array<SelectableValue<string> | VariableOptionGroup>>(
|
||||
@@ -215,11 +215,11 @@ const Filter = (
|
||||
const promise = await getTraceProperties(
|
||||
query,
|
||||
datasource,
|
||||
timeRange,
|
||||
queryTraceTypes,
|
||||
propertyMap,
|
||||
setPropertyMap,
|
||||
item
|
||||
item,
|
||||
range
|
||||
);
|
||||
setValues(addValueToOptions(promise, variableOptionGroup));
|
||||
setLoading(false);
|
||||
|
||||
+13
@@ -16,6 +16,19 @@ import { AzureMonitorQuery } from '../../types';
|
||||
import Filters from './Filters';
|
||||
import { setFilters } from './setQueryValue';
|
||||
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getTemplateSrv: () => ({
|
||||
replace: jest.fn(),
|
||||
getVariables: jest.fn(),
|
||||
updateTimeRange: jest.fn(),
|
||||
containsTemplate: jest.fn(),
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
const variableOptionGroup = {
|
||||
label: 'Template variables',
|
||||
options: [],
|
||||
|
||||
+4
-18
@@ -1,7 +1,7 @@
|
||||
import { uniq } from 'lodash';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { SelectableValue, TimeRange } from '@grafana/data';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { EditorList } from '@grafana/experimental';
|
||||
import { Field } from '@grafana/ui';
|
||||
|
||||
@@ -11,7 +11,7 @@ import { makeRenderItem } from './Filter';
|
||||
import { tablesSchema } from './consts';
|
||||
import { setFilters } from './setQueryValue';
|
||||
|
||||
const Filters = ({ query, datasource, onQueryChange, variableOptionGroup }: AzureQueryEditorFieldProps) => {
|
||||
const Filters = ({ query, datasource, onQueryChange, variableOptionGroup, range }: AzureQueryEditorFieldProps) => {
|
||||
const { azureTraces } = query;
|
||||
const queryTraceTypes = azureTraces?.traceTypes ? azureTraces.traceTypes : Object.keys(tablesSchema);
|
||||
|
||||
@@ -34,23 +34,9 @@ const Filters = ({ query, datasource, onQueryChange, variableOptionGroup }: Azur
|
||||
const queryFilters = useMemo(() => query.azureTraces?.filters ?? [], [query.azureTraces?.filters]);
|
||||
const [filters, updateFilters] = useState(queryFilters);
|
||||
|
||||
const timeSrv = datasource.azureLogAnalyticsDatasource.timeSrv;
|
||||
const [timeRange, setTimeRange] = useState(timeSrv.timeRange());
|
||||
|
||||
const useTime = (time: TimeRange) => {
|
||||
if (
|
||||
timeRange !== null &&
|
||||
(timeRange.raw.from.toString() !== time.raw.from.toString() ||
|
||||
timeRange.raw.to.toString() !== time.raw.to.toString())
|
||||
) {
|
||||
setTimeRange({ ...time });
|
||||
}
|
||||
};
|
||||
useTime(timeSrv.timeRange());
|
||||
|
||||
useEffect(() => {
|
||||
setPropertyMap(new Map<string, Array<SelectableValue<string>>>());
|
||||
}, [timeRange, query.azureTraces?.resources, query.azureTraces?.traceTypes, query.azureTraces?.operationId]);
|
||||
}, [query.azureTraces?.resources, query.azureTraces?.traceTypes, query.azureTraces?.operationId]);
|
||||
|
||||
const changedFunc = (changed: Array<Partial<AzureTracesFilter>>) => {
|
||||
let updateQuery = false;
|
||||
@@ -82,10 +68,10 @@ const Filters = ({ query, datasource, onQueryChange, variableOptionGroup }: Azur
|
||||
datasource,
|
||||
propertyMap,
|
||||
setPropertyMap,
|
||||
timeRange,
|
||||
queryTraceTypes,
|
||||
properties,
|
||||
variableOptionGroup,
|
||||
range,
|
||||
})}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
+7
@@ -1,6 +1,7 @@
|
||||
import React, { useCallback, useEffect, useState } from 'react';
|
||||
import { usePrevious } from 'react-use';
|
||||
|
||||
import { TimeRange } from '@grafana/data';
|
||||
import { EditorFieldGroup, EditorRow, EditorRows } from '@grafana/experimental';
|
||||
import { Input } from '@grafana/ui';
|
||||
|
||||
@@ -25,6 +26,7 @@ interface TracesQueryEditorProps {
|
||||
onChange: (newQuery: AzureMonitorQuery) => void;
|
||||
variableOptionGroup: { label: string; options: AzureMonitorOption[] };
|
||||
setError: (source: string, error: AzureMonitorErrorish | undefined) => void;
|
||||
range?: TimeRange;
|
||||
}
|
||||
|
||||
const TracesQueryEditor = ({
|
||||
@@ -34,6 +36,7 @@ const TracesQueryEditor = ({
|
||||
variableOptionGroup,
|
||||
onChange,
|
||||
setError,
|
||||
range,
|
||||
}: TracesQueryEditorProps) => {
|
||||
const disableRow = (row: ResourceRow, selectedRows: ResourceRowGroup) => {
|
||||
if (selectedRows.length === 0) {
|
||||
@@ -102,6 +105,7 @@ const TracesQueryEditor = ({
|
||||
<AdvancedResourcePicker resources={resources as string[]} onChange={onChange} />
|
||||
)}
|
||||
selectionNotice={() => 'You may only choose items of the same resource type.'}
|
||||
range={range}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
@@ -113,6 +117,7 @@ const TracesQueryEditor = ({
|
||||
query={query}
|
||||
setError={setError}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
range={range}
|
||||
/>
|
||||
<Field label="Operation ID">
|
||||
<Input
|
||||
@@ -133,6 +138,7 @@ const TracesQueryEditor = ({
|
||||
query={query}
|
||||
setError={setError}
|
||||
variableOptionGroup={variableOptionGroup}
|
||||
range={range}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
@@ -152,6 +158,7 @@ const TracesQueryEditor = ({
|
||||
defaultValue={ResultFormat.Table}
|
||||
setFormatAs={setFormatAs}
|
||||
resultFormat={query.azureTraces?.resultFormat}
|
||||
range={range}
|
||||
/>
|
||||
</EditorFieldGroup>
|
||||
</EditorRow>
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
import { createMockInstanceSetttings } from './__mocks__/instanceSettings';
|
||||
import createMockQuery from './__mocks__/query';
|
||||
import { singleVariable } from './__mocks__/variables';
|
||||
import Datasource from './datasource';
|
||||
import { AzureQueryType } from './types';
|
||||
|
||||
jest.mock('@grafana/runtime', () => {
|
||||
return {
|
||||
__esModule: true,
|
||||
...jest.requireActual('@grafana/runtime'),
|
||||
getTemplateSrv: () => ({
|
||||
replace: (target?: string) => {
|
||||
if (target === '$resourceGroup') {
|
||||
return 'the-resource-group';
|
||||
}
|
||||
return target || '';
|
||||
},
|
||||
getVariables: jest.fn(),
|
||||
updateTimeRange: jest.fn(),
|
||||
containsTemplate: (target?: string) => {
|
||||
return (target || '').includes('$');
|
||||
},
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Azure Monitor Datasource', () => {
|
||||
describe('interpolateVariablesInQueries()', () => {
|
||||
@@ -49,4 +71,45 @@ describe('Azure Monitor Datasource', () => {
|
||||
delete query.queryType;
|
||||
expect(ds.filterQuery(query)).toBe(false);
|
||||
});
|
||||
|
||||
describe('When performing targetContainsTemplate', () => {
|
||||
it('should return false when no variable is being used', () => {
|
||||
const query = {
|
||||
...createMockQuery(),
|
||||
queryType: AzureQueryType.AzureResourceGraph,
|
||||
};
|
||||
const ds = new Datasource(createMockInstanceSetttings());
|
||||
expect(ds.targetContainsTemplate(query)).toEqual(false);
|
||||
});
|
||||
|
||||
it('should return true when resource field is using a variable', () => {
|
||||
const query = {
|
||||
...createMockQuery(),
|
||||
queryType: AzureQueryType.AzureResourceGraph,
|
||||
azureResourceGraph: { query: '$temp-var' },
|
||||
};
|
||||
const ds = new Datasource(createMockInstanceSetttings());
|
||||
expect(ds.targetContainsTemplate(query)).toEqual(true);
|
||||
});
|
||||
|
||||
it('should return true when resource field is using a variable in the subscriptions field', () => {
|
||||
const query = {
|
||||
...createMockQuery(),
|
||||
queryType: AzureQueryType.AzureResourceGraph,
|
||||
subscriptions: ['$temp-var'],
|
||||
};
|
||||
const ds = new Datasource(createMockInstanceSetttings());
|
||||
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(),
|
||||
queryType: AzureQueryType.AzureResourceGraph,
|
||||
azureMonitor: { metricName: `$${singleVariable.name}` },
|
||||
};
|
||||
const ds = new Datasource(createMockInstanceSetttings());
|
||||
expect(ds.targetContainsTemplate(query)).toEqual(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,8 +10,7 @@ import {
|
||||
LoadingState,
|
||||
ScopedVars,
|
||||
} from '@grafana/data';
|
||||
import { DataSourceWithBackend } from '@grafana/runtime';
|
||||
import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { DataSourceWithBackend, getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import AzureLogAnalyticsDatasource from './azure_log_analytics/azure_log_analytics_datasource';
|
||||
import AzureMonitorDatasource from './azure_monitor/azure_monitor_datasource';
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
DataSourceSettings,
|
||||
PanelData,
|
||||
SelectableValue,
|
||||
TimeRange,
|
||||
} from '@grafana/data';
|
||||
|
||||
import Datasource from '../datasource';
|
||||
@@ -142,6 +143,7 @@ export interface AzureQueryEditorFieldProps {
|
||||
subscriptionId?: string;
|
||||
variableOptionGroup: VariableOptionGroup;
|
||||
schema?: EngineSchema;
|
||||
range?: TimeRange;
|
||||
|
||||
onQueryChange: (newQuery: AzureMonitorQuery) => void;
|
||||
setError: (source: string, error: AzureMonitorErrorish | undefined) => void;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { map } from 'lodash';
|
||||
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { VariableWithMultiSupport } from 'app/features/variables/types';
|
||||
import { SelectableValue, VariableWithMultiSupport } from '@grafana/data';
|
||||
|
||||
import { AzureMonitorOption, VariableOptionGroup } from '../types';
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
MetricFindValue,
|
||||
toDataFrame,
|
||||
} from '@grafana/data';
|
||||
import { getTemplateSrv } from '@grafana/runtime';
|
||||
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import VariableEditor from './components/VariableEditor/VariableEditor';
|
||||
import DataSource from './datasource';
|
||||
@@ -17,12 +17,12 @@ import { GrafanaTemplateVariableQuery } from './types/templateVariables';
|
||||
import messageFromError from './utils/messageFromError';
|
||||
|
||||
export class VariableSupport extends CustomVariableSupport<DataSource, AzureMonitorQuery> {
|
||||
templateSrv = getTemplateSrv();
|
||||
|
||||
constructor(private readonly datasource: DataSource) {
|
||||
constructor(
|
||||
private readonly datasource: DataSource,
|
||||
private readonly templateSrv: TemplateSrv = getTemplateSrv()
|
||||
) {
|
||||
super();
|
||||
this.datasource = datasource;
|
||||
this.templateSrv = getTemplateSrv();
|
||||
}
|
||||
|
||||
editor = VariableEditor;
|
||||
@@ -169,6 +169,6 @@ export class VariableSupport extends CustomVariableSupport<DataSource, AzureMoni
|
||||
}
|
||||
|
||||
replaceVariable(metric: string) {
|
||||
return getTemplateSrv().replace((metric || '').trim());
|
||||
return this.templateSrv.replace((metric || '').trim());
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user