mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CloudWatch: Remove dependency on local TemplateSrv (#79283)
This commit is contained in:
parent
f3bb16c598
commit
4fa6bad7c0
public/app/plugins/datasource/cloudwatch
__mocks__
AnnotationQueryRunner.tsCloudWatchDataSource.tsLogsQueryRunner.tsMetricsQueryRunner.tsResourcesAPI.ts
components
datasource.test.tsdatasource.tshooks.test.tslanguage
cloudwatch-logs
cloudwatch-sql
query-runner
CloudWatchAnnotationQueryRunner.tsCloudWatchLogsQueryRunner.test.tsCloudWatchLogsQueryRunner.tsCloudWatchMetricsQueryRunner.test.tsCloudWatchMetricsQueryRunner.tsCloudWatchRequest.ts
resources
utils
@ -1,7 +1,6 @@
|
||||
import { of } from 'rxjs';
|
||||
|
||||
import { CustomVariableModel, DataQueryRequest } from '@grafana/data';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { CloudWatchAnnotationQueryRunner } from '../query-runner/CloudWatchAnnotationQueryRunner';
|
||||
import { CloudWatchQuery } from '../types';
|
||||
@ -10,10 +9,7 @@ import { CloudWatchSettings, setupMockedTemplateService } from './CloudWatchData
|
||||
import { TimeRangeMock } from './timeRange';
|
||||
|
||||
export function setupMockedAnnotationQueryRunner({ variables }: { variables?: CustomVariableModel[] }) {
|
||||
let templateService = new TemplateSrv();
|
||||
if (variables) {
|
||||
templateService = setupMockedTemplateService(variables);
|
||||
}
|
||||
const templateService = setupMockedTemplateService(variables);
|
||||
|
||||
const queryMock = jest.fn().mockReturnValue(of({}));
|
||||
const runner = new CloudWatchAnnotationQueryRunner(CloudWatchSettings, templateService);
|
||||
|
@ -6,22 +6,70 @@ import {
|
||||
DataSourcePluginMeta,
|
||||
PluginMetaInfo,
|
||||
PluginType,
|
||||
ScopedVars,
|
||||
VariableHide,
|
||||
} from '@grafana/data';
|
||||
import { getBackendSrv, setBackendSrv, DataSourceWithBackend } from '@grafana/runtime';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { getBackendSrv, setBackendSrv, DataSourceWithBackend, TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { initialCustomVariableModelState } from '../__mocks__/variables';
|
||||
import { CloudWatchDatasource } from '../datasource';
|
||||
import { CloudWatchJsonData } from '../types';
|
||||
import { getVariableName } from '../utils/templateVariableUtils';
|
||||
|
||||
const queryMock = jest.fn().mockReturnValue(of({ data: [] }));
|
||||
jest.spyOn(DataSourceWithBackend.prototype, 'query').mockImplementation((args) => queryMock(args));
|
||||
const separatorMap = new Map<string, string>([
|
||||
['pipe', '|'],
|
||||
['raw', ','],
|
||||
['text', ' + '],
|
||||
]);
|
||||
|
||||
export function setupMockedTemplateService(variables: CustomVariableModel[]) {
|
||||
const templateService = new TemplateSrv();
|
||||
templateService.init(variables);
|
||||
templateService.getVariables = jest.fn().mockReturnValue(variables);
|
||||
export function setupMockedTemplateService(variables?: CustomVariableModel[]): TemplateSrv {
|
||||
const templateService = {
|
||||
replace: jest.fn().mockImplementation((input: string, scopedVars?: ScopedVars, format?: string) => {
|
||||
if (!input) {
|
||||
return '';
|
||||
}
|
||||
let output = input;
|
||||
['datasource', 'dimension'].forEach((name) => {
|
||||
const variable = scopedVars ? scopedVars[name] : undefined;
|
||||
if (variable) {
|
||||
output = output.replace('$' + name, variable.value);
|
||||
}
|
||||
});
|
||||
|
||||
if (variables) {
|
||||
variables.forEach((variable) => {
|
||||
let repVal = '';
|
||||
let value = format === 'text' ? variable.current.text : variable.current.value;
|
||||
let separator = separatorMap.get(format ?? 'raw');
|
||||
if (Array.isArray(value)) {
|
||||
repVal = value.join(separator);
|
||||
} else {
|
||||
repVal = value;
|
||||
}
|
||||
output = output.replace('$' + variable.name, repVal);
|
||||
output = output.replace('[[' + variable.name + ']]', repVal);
|
||||
});
|
||||
}
|
||||
return output;
|
||||
}),
|
||||
getVariables: jest.fn().mockReturnValue(variables ?? []),
|
||||
containsTemplate: jest.fn().mockImplementation((name) => {
|
||||
const varName = getVariableName(name);
|
||||
if (!varName || !variables) {
|
||||
return false;
|
||||
}
|
||||
let found = false;
|
||||
variables.forEach((variable) => {
|
||||
if (varName === variable.name) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
return found;
|
||||
}),
|
||||
updateTimeRange: jest.fn(),
|
||||
};
|
||||
return templateService;
|
||||
}
|
||||
|
||||
@ -62,22 +110,14 @@ export const CloudWatchSettings: DataSourceInstanceSettings<CloudWatchJsonData>
|
||||
|
||||
export function setupMockedDataSource({
|
||||
variables,
|
||||
mockGetVariableName = true,
|
||||
getMock = jest.fn(),
|
||||
customInstanceSettings = CloudWatchSettings,
|
||||
}: {
|
||||
getMock?: jest.Func;
|
||||
variables?: CustomVariableModel[];
|
||||
mockGetVariableName?: boolean;
|
||||
customInstanceSettings?: DataSourceInstanceSettings<CloudWatchJsonData>;
|
||||
} = {}) {
|
||||
let templateService = new TemplateSrv();
|
||||
if (variables) {
|
||||
templateService = setupMockedTemplateService(variables);
|
||||
if (mockGetVariableName) {
|
||||
templateService.getVariableName = (name: string) => name.replace('$', '');
|
||||
}
|
||||
}
|
||||
const templateService = setupMockedTemplateService(variables);
|
||||
|
||||
const datasource = new CloudWatchDatasource(customInstanceSettings, templateService);
|
||||
datasource.getVariables = () => ['test'];
|
||||
|
@ -2,7 +2,6 @@ import { of } from 'rxjs';
|
||||
|
||||
import { CustomVariableModel, DataFrame, DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { BackendDataSourceResponse, toDataQueryResponse } from '@grafana/runtime';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { CloudWatchLogsQueryRunner } from '../query-runner/CloudWatchLogsQueryRunner';
|
||||
import { CloudWatchJsonData, CloudWatchLogsQueryStatus, CloudWatchLogsRequest } from '../types';
|
||||
@ -22,13 +21,7 @@ export function setupMockedLogsQueryRunner({
|
||||
mockGetVariableName?: boolean;
|
||||
settings?: DataSourceInstanceSettings<CloudWatchJsonData>;
|
||||
} = {}) {
|
||||
let templateService = new TemplateSrv();
|
||||
if (variables) {
|
||||
templateService = setupMockedTemplateService(variables);
|
||||
if (mockGetVariableName) {
|
||||
templateService.getVariableName = (name: string) => name;
|
||||
}
|
||||
}
|
||||
let templateService = setupMockedTemplateService(variables);
|
||||
|
||||
const queryMock = jest.fn().mockReturnValue(of(toDataQueryResponse({ data })));
|
||||
const runner = new CloudWatchLogsQueryRunner(settings, templateService);
|
||||
|
@ -2,7 +2,6 @@ import { of, throwError } from 'rxjs';
|
||||
|
||||
import { CustomVariableModel, DataQueryError, DataQueryRequest, DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { BackendDataSourceResponse, toDataQueryResponse } from '@grafana/runtime';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { CloudWatchMetricsQueryRunner } from '../query-runner/CloudWatchMetricsQueryRunner';
|
||||
import { CloudWatchJsonData, CloudWatchQuery } from '../types';
|
||||
@ -15,23 +14,15 @@ export function setupMockedMetricsQueryRunner({
|
||||
results: {},
|
||||
},
|
||||
variables,
|
||||
mockGetVariableName = true,
|
||||
errorResponse,
|
||||
instanceSettings = CloudWatchSettings,
|
||||
}: {
|
||||
data?: BackendDataSourceResponse;
|
||||
variables?: CustomVariableModel[];
|
||||
mockGetVariableName?: boolean;
|
||||
errorResponse?: DataQueryError;
|
||||
instanceSettings?: DataSourceInstanceSettings<CloudWatchJsonData>;
|
||||
} = {}) {
|
||||
let templateService = new TemplateSrv();
|
||||
if (variables) {
|
||||
templateService = setupMockedTemplateService(variables);
|
||||
if (mockGetVariableName) {
|
||||
templateService.getVariableName = (name: string) => name.replace('$', '');
|
||||
}
|
||||
}
|
||||
const templateService = setupMockedTemplateService(variables);
|
||||
|
||||
const queryMock = errorResponse
|
||||
? jest.fn().mockImplementation(() => throwError(errorResponse))
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { CustomVariableModel } from '@grafana/data';
|
||||
import { getBackendSrv, setBackendSrv } from '@grafana/runtime';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { ResourcesAPI } from '../resources/ResourcesAPI';
|
||||
|
||||
@ -16,7 +15,7 @@ export function setupMockedResourcesAPI({
|
||||
variables?: CustomVariableModel[];
|
||||
mockGetVariableName?: boolean;
|
||||
} = {}) {
|
||||
let templateService = variables ? setupMockedTemplateService(variables) : new TemplateSrv();
|
||||
let templateService = setupMockedTemplateService(variables);
|
||||
|
||||
const api = new ResourcesAPI(CloudWatchSettings, templateService);
|
||||
let resourceRequestMock = getMock ? getMock : jest.fn().mockReturnValue(response);
|
||||
|
@ -4,8 +4,8 @@ import selectEvent from 'react-select-event';
|
||||
|
||||
import { CustomVariableModel, DataSourceInstanceSettings } from '@grafana/data';
|
||||
import * as ui from '@grafana/ui';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { setupMockedTemplateService } from '../../../__mocks__/CloudWatchDataSource';
|
||||
import { initialVariableModelState } from '../../../__mocks__/variables';
|
||||
import { CloudWatchDatasource } from '../../../datasource';
|
||||
import { CloudWatchJsonData, MetricEditorMode, MetricQueryType } from '../../../types';
|
||||
@ -24,7 +24,6 @@ const setup = () => {
|
||||
jsonData: { defaultRegion: 'us-east-1' },
|
||||
} as DataSourceInstanceSettings<CloudWatchJsonData>;
|
||||
|
||||
const templateSrv = new TemplateSrv();
|
||||
const variable: CustomVariableModel = {
|
||||
...initialVariableModelState,
|
||||
id: 'var3',
|
||||
@ -41,7 +40,7 @@ const setup = () => {
|
||||
query: '',
|
||||
type: 'custom',
|
||||
};
|
||||
templateSrv.init([variable]);
|
||||
const templateSrv = setupMockedTemplateService([variable]);
|
||||
|
||||
const datasource = new CloudWatchDatasource(instanceSettings, templateSrv);
|
||||
datasource.metricFindQuery = async () => [{ value: 'test', label: 'test', text: 'test' }];
|
||||
|
@ -5,7 +5,11 @@ import React from 'react';
|
||||
|
||||
import { config } from '@grafana/runtime';
|
||||
|
||||
import { logGroupNamesVariable, setupMockedDataSource } from '../../../__mocks__/CloudWatchDataSource';
|
||||
import {
|
||||
logGroupNamesVariable,
|
||||
setupMockedDataSource,
|
||||
setupMockedTemplateService,
|
||||
} from '../../../__mocks__/CloudWatchDataSource';
|
||||
|
||||
import { LogGroupsField } from './LogGroupsField';
|
||||
|
||||
@ -17,6 +21,7 @@ const defaultProps = {
|
||||
region: '',
|
||||
onChange: jest.fn(),
|
||||
};
|
||||
|
||||
describe('LogGroupSelection', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
@ -35,6 +40,7 @@ describe('LogGroupSelection', () => {
|
||||
defaultProps.datasource.resources.getLogGroups = jest
|
||||
.fn()
|
||||
.mockResolvedValue([{ value: { arn: 'arn', name: 'loggroupname' } }]);
|
||||
defaultProps.datasource.resources.templateSrv = setupMockedTemplateService();
|
||||
render(<LogGroupsField {...defaultProps} legacyLogGroupNames={['loggroupname']} />);
|
||||
|
||||
await waitFor(async () => expect(screen.getByText('Select log groups')).toBeInTheDocument());
|
||||
@ -51,7 +57,8 @@ describe('LogGroupSelection', () => {
|
||||
defaultProps.datasource.resources.getLogGroups = jest
|
||||
.fn()
|
||||
.mockResolvedValue([{ value: { arn: 'arn', name: 'loggroupname' } }]);
|
||||
render(<LogGroupsField {...defaultProps} legacyLogGroupNames={['loggroupname', logGroupNamesVariable.name]} />);
|
||||
const varName = '$' + logGroupNamesVariable.name;
|
||||
render(<LogGroupsField {...defaultProps} legacyLogGroupNames={['loggroupname', varName]} />);
|
||||
|
||||
await waitFor(async () => expect(screen.getByText('Select log groups')).toBeInTheDocument());
|
||||
expect(defaultProps.datasource.resources.getLogGroups).toHaveBeenCalledTimes(1);
|
||||
@ -61,7 +68,7 @@ describe('LogGroupSelection', () => {
|
||||
});
|
||||
expect(defaultProps.onChange).toHaveBeenCalledWith([
|
||||
{ arn: 'arn', name: 'loggroupname' },
|
||||
{ arn: logGroupNamesVariable.name, name: logGroupNamesVariable.name },
|
||||
{ arn: varName, name: varName },
|
||||
]);
|
||||
});
|
||||
|
||||
@ -70,6 +77,7 @@ describe('LogGroupSelection', () => {
|
||||
defaultProps.datasource.resources.getLogGroups = jest
|
||||
.fn()
|
||||
.mockResolvedValue([{ value: { arn: 'arn', name: 'loggroupname' } }]);
|
||||
defaultProps.datasource.resources.templateSrv = setupMockedTemplateService();
|
||||
render(<LogGroupsField {...defaultProps} logGroups={[{ arn: 'arn', name: 'loggroupname' }]} />);
|
||||
await waitFor(() => expect(screen.getByText('Select log groups')).toBeInTheDocument());
|
||||
expect(defaultProps.datasource.resources.getLogGroups).not.toHaveBeenCalled();
|
||||
|
@ -21,6 +21,7 @@ import {
|
||||
MetricEditorMode,
|
||||
MetricQueryType,
|
||||
} from './types';
|
||||
import * as templateUtils from './utils/templateVariableUtils';
|
||||
|
||||
describe('datasource', () => {
|
||||
beforeEach(() => {
|
||||
@ -178,7 +179,6 @@ describe('datasource', () => {
|
||||
it('should interpolate multi-value template variable for log group names in the query', async () => {
|
||||
const { datasource, queryMock } = setupMockedDataSource({
|
||||
variables: [fieldsVariable, logGroupNamesVariable, regionVariable],
|
||||
mockGetVariableName: false,
|
||||
});
|
||||
await lastValueFrom(
|
||||
datasource
|
||||
@ -304,7 +304,9 @@ describe('datasource', () => {
|
||||
it('should replace correct variables in CloudWatchMetricsQuery', () => {
|
||||
const { datasource, templateService } = setupMockedDataSource();
|
||||
templateService.replace = jest.fn();
|
||||
templateService.getVariableName = jest.fn();
|
||||
const mockGetVariableName = jest
|
||||
.spyOn(templateUtils, 'getVariableName')
|
||||
.mockImplementation((name: string) => name.replace('$', ''));
|
||||
const variableName = 'someVar';
|
||||
const metricsQuery: CloudWatchMetricsQuery = {
|
||||
queryMode: 'Metrics',
|
||||
@ -330,8 +332,8 @@ describe('datasource', () => {
|
||||
expect(templateService.replace).toHaveBeenCalledWith(`$${variableName}`, {});
|
||||
expect(templateService.replace).toHaveBeenCalledTimes(8);
|
||||
|
||||
expect(templateService.getVariableName).toHaveBeenCalledWith(`$${variableName}`);
|
||||
expect(templateService.getVariableName).toHaveBeenCalledTimes(1);
|
||||
expect(mockGetVariableName).toHaveBeenCalledWith(`$${variableName}`);
|
||||
expect(mockGetVariableName).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -13,8 +13,7 @@ import {
|
||||
LogRowModel,
|
||||
ScopedVars,
|
||||
} from '@grafana/data';
|
||||
import { DataSourceWithBackend } from '@grafana/runtime';
|
||||
import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { DataSourceWithBackend, TemplateSrv, getTemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { CloudWatchAnnotationSupport } from './annotationSupport';
|
||||
import { DEFAULT_METRICS_QUERY, getDefaultLogsQuery } from './defaultQueries';
|
||||
|
@ -64,7 +64,6 @@ describe('hooks', () => {
|
||||
describe('useDimensionKeys', () => {
|
||||
it('should interpolate variables before calling api', async () => {
|
||||
const { datasource } = setupMockedDataSource({
|
||||
mockGetVariableName: true,
|
||||
variables: [regionVariable, namespaceVariable, accountIdVariable, metricVariable, dimensionVariable],
|
||||
});
|
||||
const getDimensionKeysMock = jest.fn().mockResolvedValue([]);
|
||||
|
@ -3,6 +3,7 @@ import { Value } from 'slate';
|
||||
|
||||
import { TypeaheadOutput } from '@grafana/ui';
|
||||
|
||||
import { setupMockedTemplateService } from '../../__mocks__/CloudWatchDataSource';
|
||||
import { CloudWatchDatasource } from '../../datasource';
|
||||
import { ResourceResponse } from '../../resources/types';
|
||||
import { LogGroupField } from '../../types';
|
||||
@ -124,7 +125,7 @@ function makeDatasource(): CloudWatchDatasource {
|
||||
* Get suggestion items based on query. Use `^` to mark position of the cursor.
|
||||
*/
|
||||
function getProvideCompletionItems(query: string): Promise<TypeaheadOutput> {
|
||||
const provider = new CloudWatchLogsLanguageProvider(makeDatasource());
|
||||
const provider = new CloudWatchLogsLanguageProvider(makeDatasource(), setupMockedTemplateService());
|
||||
const cursorOffset = query.indexOf('^');
|
||||
const queryWithoutCursor = query.replace('^', '');
|
||||
let tokens: Token[] = Prism.tokenize(queryWithoutCursor, provider.getSyntax()) as any;
|
||||
|
@ -2,9 +2,8 @@ import Prism, { Grammar } from 'prismjs';
|
||||
import { lastValueFrom } from 'rxjs';
|
||||
|
||||
import { AbsoluteTimeRange, HistoryItem, LanguageProvider } from '@grafana/data';
|
||||
import { BackendDataSourceResponse, FetchResponse } from '@grafana/runtime';
|
||||
import { BackendDataSourceResponse, FetchResponse, TemplateSrv, getTemplateSrv } from '@grafana/runtime';
|
||||
import { CompletionItemGroup, SearchFunctionType, Token, TypeaheadInput, TypeaheadOutput } from '@grafana/ui';
|
||||
import { getTemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import { CloudWatchDatasource } from '../../datasource';
|
||||
import { CloudWatchQuery, LogGroup } from '../../types';
|
||||
@ -34,11 +33,13 @@ export class CloudWatchLogsLanguageProvider extends LanguageProvider {
|
||||
started = false;
|
||||
declare initialRange: AbsoluteTimeRange;
|
||||
datasource: CloudWatchDatasource;
|
||||
templateSrv: TemplateSrv;
|
||||
|
||||
constructor(datasource: CloudWatchDatasource, initialValues?: any) {
|
||||
constructor(datasource: CloudWatchDatasource, templateSrv?: TemplateSrv, initialValues?: any) {
|
||||
super();
|
||||
|
||||
this.datasource = datasource;
|
||||
this.templateSrv = templateSrv ?? getTemplateSrv();
|
||||
|
||||
Object.assign(this, initialValues);
|
||||
}
|
||||
@ -132,7 +133,7 @@ export class CloudWatchLogsLanguageProvider extends LanguageProvider {
|
||||
|
||||
private fetchFields = async (logGroups: LogGroup[], region: string): Promise<string[]> => {
|
||||
const interpolatedLogGroups = interpolateStringArrayUsingSingleOrMultiValuedVariable(
|
||||
getTemplateSrv(),
|
||||
this.templateSrv,
|
||||
logGroups.map((lg) => lg.name),
|
||||
{},
|
||||
'text'
|
||||
|
@ -1,10 +1,9 @@
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
|
||||
import {
|
||||
aggregationvariable,
|
||||
labelsVariable,
|
||||
metricVariable,
|
||||
namespaceVariable,
|
||||
setupMockedTemplateService,
|
||||
} from '../../__mocks__/CloudWatchDataSource';
|
||||
import {
|
||||
createFunctionWithParameter,
|
||||
@ -25,11 +24,12 @@ describe('SQLGenerator', () => {
|
||||
from: createFunctionWithParameter('SCHEMA', ['AWS/EC2']),
|
||||
orderByDirection: 'DESC',
|
||||
};
|
||||
let mockTemplateSrv = setupMockedTemplateService();
|
||||
|
||||
describe('mandatory fields check', () => {
|
||||
it('should return undefined if metric and aggregation is missing', () => {
|
||||
expect(
|
||||
new SQLGenerator().expressionToSqlQuery({
|
||||
new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({
|
||||
from: createFunctionWithParameter('SCHEMA', ['AWS/EC2']),
|
||||
})
|
||||
).toBeUndefined();
|
||||
@ -37,7 +37,7 @@ describe('SQLGenerator', () => {
|
||||
|
||||
it('should return undefined if aggregation is missing', () => {
|
||||
expect(
|
||||
new SQLGenerator().expressionToSqlQuery({
|
||||
new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({
|
||||
from: createFunctionWithParameter('SCHEMA', []),
|
||||
})
|
||||
).toBeUndefined();
|
||||
@ -45,27 +45,27 @@ describe('SQLGenerator', () => {
|
||||
});
|
||||
|
||||
it('should return query if mandatory fields are provided', () => {
|
||||
expect(new SQLGenerator().expressionToSqlQuery(baseQuery)).not.toBeUndefined();
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery(baseQuery)).not.toBeUndefined();
|
||||
});
|
||||
|
||||
describe('select', () => {
|
||||
it('should use statistic and metric name', () => {
|
||||
const select = createFunctionWithParameter('COUNT', ['BytesPerSecond']);
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, select })).toEqual(
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, select })).toEqual(
|
||||
`SELECT COUNT(BytesPerSecond) FROM SCHEMA("AWS/EC2")`
|
||||
);
|
||||
});
|
||||
|
||||
it('should wrap in double quotes if metric name contains illegal characters ', () => {
|
||||
const select = createFunctionWithParameter('COUNT', ['Bytes-Per-Second']);
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, select })).toEqual(
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, select })).toEqual(
|
||||
`SELECT COUNT("Bytes-Per-Second") FROM SCHEMA("AWS/EC2")`
|
||||
);
|
||||
});
|
||||
|
||||
it('should wrap in double quotes if metric name starts with a number ', () => {
|
||||
const select = createFunctionWithParameter('COUNT', ['4xxErrorRate']);
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, select })).toEqual(
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, select })).toEqual(
|
||||
`SELECT COUNT("4xxErrorRate") FROM SCHEMA("AWS/EC2")`
|
||||
);
|
||||
});
|
||||
@ -75,14 +75,14 @@ describe('SQLGenerator', () => {
|
||||
describe('with schema contraint', () => {
|
||||
it('should handle schema without dimensions', () => {
|
||||
const from = createFunctionWithParameter('SCHEMA', ['AWS/MQ']);
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, from })).toEqual(
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, from })).toEqual(
|
||||
`SELECT SUM(CPUUtilization) FROM SCHEMA("AWS/MQ")`
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle schema with dimensions', () => {
|
||||
const from = createFunctionWithParameter('SCHEMA', ['AWS/MQ', 'InstanceId', 'InstanceType']);
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, from })).toEqual(
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, from })).toEqual(
|
||||
`SELECT SUM(CPUUtilization) FROM SCHEMA("AWS/MQ", InstanceId, InstanceType)`
|
||||
);
|
||||
});
|
||||
@ -94,7 +94,7 @@ describe('SQLGenerator', () => {
|
||||
'Instance.Type',
|
||||
'Instance-Group',
|
||||
]);
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, from })).toEqual(
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, from })).toEqual(
|
||||
`SELECT SUM(CPUUtilization) FROM SCHEMA("AWS/MQ", "Instance Id", "Instance.Type", "Instance-Group")`
|
||||
);
|
||||
});
|
||||
@ -103,7 +103,7 @@ describe('SQLGenerator', () => {
|
||||
describe('without schema', () => {
|
||||
it('should use the specified namespace', () => {
|
||||
const from = createProperty('AWS/MQ');
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, from })).toEqual(
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, from })).toEqual(
|
||||
`SELECT SUM(CPUUtilization) FROM "AWS/MQ"`
|
||||
);
|
||||
});
|
||||
@ -111,25 +111,25 @@ describe('SQLGenerator', () => {
|
||||
});
|
||||
|
||||
function assertQueryEndsWith(rest: Partial<SQLExpression>, expectedFilter: string) {
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, ...rest })).toEqual(
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, ...rest })).toEqual(
|
||||
`SELECT SUM(CPUUtilization) FROM SCHEMA("AWS/EC2") ${expectedFilter}`
|
||||
);
|
||||
}
|
||||
|
||||
describe('filter', () => {
|
||||
it('should not add WHERE clause in case its empty', () => {
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery })).not.toContain('WHERE');
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery })).not.toContain('WHERE');
|
||||
});
|
||||
|
||||
it('should not add WHERE clause when there is no filter conditions', () => {
|
||||
const where = createArray([]);
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, where })).not.toContain('WHERE');
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, where })).not.toContain('WHERE');
|
||||
});
|
||||
|
||||
// TODO: We should handle this scenario
|
||||
it.skip('should not add WHERE clause when the operator is incomplete', () => {
|
||||
const where = createArray([createOperator('Instance-Id', '=')]);
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery, where })).not.toContain('WHERE');
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery, where })).not.toContain('WHERE');
|
||||
});
|
||||
|
||||
it('should handle one top level filter with AND', () => {
|
||||
@ -280,7 +280,7 @@ describe('SQLGenerator', () => {
|
||||
|
||||
describe('group by', () => {
|
||||
it('should not add GROUP BY clause in case its empty', () => {
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery })).not.toContain('GROUP BY');
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery })).not.toContain('GROUP BY');
|
||||
});
|
||||
it('should handle single label', () => {
|
||||
const groupBy = createArray([createGroupBy('InstanceId')], QueryEditorExpressionType.And);
|
||||
@ -297,7 +297,7 @@ describe('SQLGenerator', () => {
|
||||
|
||||
describe('order by', () => {
|
||||
it('should not add ORDER BY clause in case its empty', () => {
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery })).not.toContain('ORDER BY');
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery })).not.toContain('ORDER BY');
|
||||
});
|
||||
it('should handle SUM ASC', () => {
|
||||
const orderBy = createFunction('SUM');
|
||||
@ -315,7 +315,7 @@ describe('SQLGenerator', () => {
|
||||
});
|
||||
describe('limit', () => {
|
||||
it('should not add LIMIT clause in case its empty', () => {
|
||||
expect(new SQLGenerator().expressionToSqlQuery({ ...baseQuery })).not.toContain('LIMIT');
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery({ ...baseQuery })).not.toContain('LIMIT');
|
||||
});
|
||||
|
||||
it('should be added in case its specified', () => {
|
||||
@ -346,15 +346,19 @@ describe('SQLGenerator', () => {
|
||||
orderByDirection: 'DESC',
|
||||
limit: 100,
|
||||
};
|
||||
expect(new SQLGenerator().expressionToSqlQuery(query)).toEqual(
|
||||
expect(new SQLGenerator(mockTemplateSrv).expressionToSqlQuery(query)).toEqual(
|
||||
`SELECT COUNT(DroppedBytes) FROM SCHEMA("AWS/MQ", InstanceId, "Instance-Group") WHERE (InstanceId = 'I-123' OR Type != 'some-type') AND (InstanceId != 'I-456' OR Type != 'some-type') GROUP BY InstanceId, InstanceType ORDER BY COUNT() DESC LIMIT 100`
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('using variables', () => {
|
||||
const templateService = new TemplateSrv();
|
||||
templateService.init([metricVariable, namespaceVariable, labelsVariable, aggregationvariable]);
|
||||
const templateService = setupMockedTemplateService([
|
||||
metricVariable,
|
||||
namespaceVariable,
|
||||
labelsVariable,
|
||||
aggregationvariable,
|
||||
]);
|
||||
|
||||
it('should interpolate variables correctly', () => {
|
||||
let query: SQLExpression = {
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { getTemplateSrv, TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { getTemplateSrv, TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import {
|
||||
QueryEditorArrayExpression,
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { DataQueryRequest, DataQueryResponse, DataSourceInstanceSettings } from '@grafana/data';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { CloudWatchAnnotationQuery, CloudWatchJsonData, CloudWatchQuery } from '../types';
|
||||
|
||||
|
@ -244,7 +244,7 @@ describe('CloudWatchLogsQueryRunner', () => {
|
||||
id: '',
|
||||
region: '$' + regionVariable.name,
|
||||
refId: 'A',
|
||||
expression: `stats count(*) by queryType, bin($__interval)`,
|
||||
expression: `stats count(*) by queryType, bin(20s)`,
|
||||
};
|
||||
|
||||
describe('handleLogQueries', () => {
|
||||
|
@ -31,8 +31,7 @@ import {
|
||||
getDefaultTimeRange,
|
||||
rangeUtil,
|
||||
} from '@grafana/data';
|
||||
import { config, FetchError } from '@grafana/runtime';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { config, FetchError, TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import {
|
||||
CloudWatchJsonData,
|
||||
|
@ -358,7 +358,7 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
|
||||
it('interpolates variables correctly', async () => {
|
||||
const { runner, queryMock, request } = setupMockedMetricsQueryRunner({
|
||||
variables: [namespaceVariable, metricVariable, labelsVariable, limitVariable],
|
||||
variables: [namespaceVariable, metricVariable, limitVariable],
|
||||
});
|
||||
runner.handleMetricQueries(
|
||||
[
|
||||
@ -376,7 +376,7 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
expression: '',
|
||||
metricQueryType: MetricQueryType.Query,
|
||||
metricEditorMode: MetricEditorMode.Code,
|
||||
sqlExpression: 'SELECT SUM($metric) FROM "$namespace" GROUP BY ${labels:raw} LIMIT $limit',
|
||||
sqlExpression: 'SELECT SUM($metric) FROM "$namespace" GROUP BY InstanceId,InstanceType LIMIT $limit',
|
||||
},
|
||||
],
|
||||
request,
|
||||
@ -666,8 +666,8 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
queryMode: 'Metrics',
|
||||
id: '',
|
||||
region: 'us-east-2',
|
||||
namespace: namespaceVariable.id,
|
||||
metricName: metricVariable.id,
|
||||
namespace: '$' + namespaceVariable.name,
|
||||
metricName: '$' + metricVariable.name,
|
||||
period: '',
|
||||
alias: '',
|
||||
dimensions: {},
|
||||
@ -712,7 +712,7 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
sqlExpression: 'select SUM(CPUUtilization) from $datasource',
|
||||
dimensions: { InstanceId: '$dimension' },
|
||||
};
|
||||
const { runner } = setupMockedMetricsQueryRunner({ variables: [dimensionVariable], mockGetVariableName: false });
|
||||
const { runner } = setupMockedMetricsQueryRunner({ variables: [dimensionVariable] });
|
||||
const result = runner.interpolateMetricsQueryVariables(testQuery, {
|
||||
datasource: { text: 'foo', value: 'foo' },
|
||||
dimension: { text: 'foo', value: 'foo' },
|
||||
@ -732,7 +732,6 @@ describe('CloudWatchMetricsQueryRunner', () => {
|
||||
describe('convertMultiFiltersFormat', () => {
|
||||
const { runner } = setupMockedMetricsQueryRunner({
|
||||
variables: [labelsVariable, dimensionVariable],
|
||||
mockGetVariableName: false,
|
||||
});
|
||||
it('converts keys and values correctly', () => {
|
||||
const filters = { $dimension: ['b'], a: ['$labels', 'bar'] };
|
||||
|
@ -12,9 +12,9 @@ import {
|
||||
rangeUtil,
|
||||
ScopedVars,
|
||||
} from '@grafana/data';
|
||||
import { TemplateSrv } from '@grafana/runtime';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { store } from 'app/store/store';
|
||||
import { AppNotificationTimeout } from 'app/types';
|
||||
|
||||
|
@ -1,15 +1,15 @@
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
import { DataSourceInstanceSettings, DataSourceRef, getDataSourceRef, ScopedVars } from '@grafana/data';
|
||||
import { BackendDataSourceResponse, FetchResponse, getBackendSrv } from '@grafana/runtime';
|
||||
import { BackendDataSourceResponse, FetchResponse, getBackendSrv, TemplateSrv } from '@grafana/runtime';
|
||||
import { notifyApp } from 'app/core/actions';
|
||||
import { createErrorNotification } from 'app/core/copy/appNotification';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { store } from 'app/store/store';
|
||||
import { AppNotificationTimeout } from 'app/types';
|
||||
|
||||
import memoizedDebounce from '../memoizedDebounce';
|
||||
import { CloudWatchJsonData, Dimensions, MetricRequest, MultiFilters } from '../types';
|
||||
import { getVariableName } from '../utils/templateVariableUtils';
|
||||
|
||||
export abstract class CloudWatchRequest {
|
||||
templateSrv: TemplateSrv;
|
||||
@ -62,7 +62,7 @@ export abstract class CloudWatchRequest {
|
||||
|
||||
// get the value for a given template variable
|
||||
expandVariableToArray(value: string, scopedVars: ScopedVars): string[] {
|
||||
const variableName = this.templateSrv.getVariableName(value);
|
||||
const variableName = getVariableName(value);
|
||||
const valueVar = this.templateSrv.getVariables().find(({ name }) => {
|
||||
return name === variableName;
|
||||
});
|
||||
@ -101,7 +101,7 @@ export abstract class CloudWatchRequest {
|
||||
) {
|
||||
if (displayErrorIfIsMultiTemplateVariable && !!target) {
|
||||
const variables = this.templateSrv.getVariables();
|
||||
const variable = variables.find(({ name }) => name === this.templateSrv.getVariableName(target));
|
||||
const variable = variables.find(({ name }) => name === getVariableName(target));
|
||||
const isMultiVariable =
|
||||
variable?.type === 'custom' || variable?.type === 'query' || variable?.type === 'datasource';
|
||||
if (isMultiVariable && variable.multi) {
|
||||
|
@ -1,8 +1,7 @@
|
||||
import { memoize } from 'lodash';
|
||||
|
||||
import { DataSourceInstanceSettings, SelectableValue } from '@grafana/data';
|
||||
import { getBackendSrv, config } from '@grafana/runtime';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { getBackendSrv, config, TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
import { CloudWatchRequest } from '../query-runner/CloudWatchRequest';
|
||||
import { CloudWatchJsonData, LogGroupField, MultiFilters } from '../types';
|
||||
|
@ -1,5 +1,29 @@
|
||||
import { VariableOption, UserProps, OrgProps, DashboardProps, ScopedVars } from '@grafana/data';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import { TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
/*
|
||||
* This regex matches 3 types of variable reference with an optional format specifier
|
||||
* There are 6 capture groups that replace will return
|
||||
* \$(\w+) $var1
|
||||
* \[\[(\w+?)(?::(\w+))?\]\] [[var2]] or [[var2:fmt2]]
|
||||
* \${(\w+)(?:\.([^:^\}]+))?(?::([^\}]+))?} ${var3} or ${var3.fieldPath} or ${var3:fmt3} (or ${var3.fieldPath:fmt3} but that is not a separate capture group)
|
||||
*/
|
||||
const variableRegex = /\$(\w+)|\[\[(\w+?)(?::(\w+))?\]\]|\${(\w+)(?:\.([^:^\}]+))?(?::([^\}]+))?}/g;
|
||||
|
||||
// Helper function since lastIndex is not reset
|
||||
const variableRegexExec = (variableString: string) => {
|
||||
variableRegex.lastIndex = 0;
|
||||
return variableRegex.exec(variableString);
|
||||
};
|
||||
|
||||
export const getVariableName = (expression: string) => {
|
||||
const match = variableRegexExec(expression);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
const variableName = match.slice(1).find((match) => match !== undefined);
|
||||
return variableName;
|
||||
};
|
||||
|
||||
/**
|
||||
* @remarks
|
||||
@ -22,7 +46,7 @@ export const interpolateStringArrayUsingSingleOrMultiValuedVariable = (
|
||||
const format = key === 'value' ? 'pipe' : 'text';
|
||||
let result: string[] = [];
|
||||
for (const string of strings) {
|
||||
const variableName = templateSrv.getVariableName(string);
|
||||
const variableName = getVariableName(string);
|
||||
const valueVar = templateSrv.getVariables().find(({ name }) => name === variableName);
|
||||
|
||||
if (valueVar && 'current' in valueVar && isVariableOption(valueVar.current)) {
|
||||
@ -43,7 +67,7 @@ export const interpolateStringArrayUsingSingleOrMultiValuedVariable = (
|
||||
};
|
||||
|
||||
export const isTemplateVariable = (templateSrv: TemplateSrv, string: string) => {
|
||||
const variableName = templateSrv.getVariableName(string);
|
||||
const variableName = getVariableName(string);
|
||||
return templateSrv.getVariables().some(({ name }) => name === variableName);
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user