mirror of
https://github.com/grafana/grafana.git
synced 2025-01-16 03:32:37 -06:00
CloudWatch Logs: Fix variable interpolation in queries (#40899)
* Interpolate variables in queries * Remove test
This commit is contained in:
parent
0808ec927e
commit
8cf7e520e8
48
public/app/features/templating/template_srv.mock.ts
Normal file
48
public/app/features/templating/template_srv.mock.ts
Normal file
@ -0,0 +1,48 @@
|
||||
import { ScopedVars, VariableModel } from '@grafana/data';
|
||||
import { variableRegex } from '../variables/utils';
|
||||
import { TemplateSrv } from '@grafana/runtime';
|
||||
|
||||
/**
|
||||
* Mock for TemplateSrv where you can just supply map of key and values and it will do the interpolation based on that.
|
||||
* For simple tests whether you your data source for example calls correct replacing code.
|
||||
*
|
||||
* This is implementing TemplateSrv interface but that is not enough in most cases. Datasources require some additional
|
||||
* methods and usually require TemplateSrv class directly instead of just the interface which probably should be fixed
|
||||
* later on.
|
||||
*/
|
||||
export class TemplateSrvMock implements TemplateSrv {
|
||||
private regex = variableRegex;
|
||||
constructor(private variables: Record<string, string>) {}
|
||||
|
||||
getVariables(): VariableModel[] {
|
||||
return Object.keys(this.variables).map((key) => {
|
||||
return {
|
||||
type: 'custom',
|
||||
name: key,
|
||||
label: key,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
replace(target?: string, scopedVars?: ScopedVars, format?: string | Function): string {
|
||||
if (!target) {
|
||||
return target ?? '';
|
||||
}
|
||||
|
||||
this.regex.lastIndex = 0;
|
||||
|
||||
return target.replace(this.regex, (match, var1, var2, fmt2, var3, fieldPath, fmt3) => {
|
||||
const variableName = var1 || var2 || var3;
|
||||
return this.variables[variableName];
|
||||
});
|
||||
}
|
||||
|
||||
getVariableName(expression: string) {
|
||||
this.regex.lastIndex = 0;
|
||||
const match = this.regex.exec(expression);
|
||||
if (!match) {
|
||||
return null;
|
||||
}
|
||||
return match.slice(1).find((match) => match !== undefined);
|
||||
}
|
||||
}
|
@ -2,22 +2,16 @@ import { lastValueFrom, of } from 'rxjs';
|
||||
import { setBackendSrv, setDataSourceSrv } from '@grafana/runtime';
|
||||
import { ArrayVector, DataFrame, dataFrameToJSON, dateTime, Field, MutableDataFrame } from '@grafana/data';
|
||||
|
||||
import { TemplateSrv } from '../../../features/templating/template_srv';
|
||||
import { CloudWatchDatasource } from './datasource';
|
||||
import { toArray } from 'rxjs/operators';
|
||||
import { CloudWatchLogsQueryStatus } from './types';
|
||||
import { TemplateSrvMock } from '../../../features/templating/template_srv.mock';
|
||||
|
||||
describe('datasource', () => {
|
||||
describe('query', () => {
|
||||
it('should return error if log query and log groups is not specified', async () => {
|
||||
const { datasource } = setup();
|
||||
const observable = datasource.query({
|
||||
targets: [
|
||||
{
|
||||
queryMode: 'Logs' as 'Logs',
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
const observable = datasource.query({ targets: [{ queryMode: 'Logs' as 'Logs' }] } as any);
|
||||
|
||||
await expect(observable).toEmitValuesWith((received) => {
|
||||
const response = received[0];
|
||||
@ -27,14 +21,7 @@ describe('datasource', () => {
|
||||
|
||||
it('should return empty response if queries are hidden', async () => {
|
||||
const { datasource } = setup();
|
||||
const observable = datasource.query({
|
||||
targets: [
|
||||
{
|
||||
queryMode: 'Logs' as 'Logs',
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
const observable = datasource.query({ targets: [{ queryMode: 'Logs' as 'Logs', hide: true }] } as any);
|
||||
|
||||
await expect(observable).toEmitValuesWith((received) => {
|
||||
const response = received[0];
|
||||
@ -42,6 +29,25 @@ describe('datasource', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should interpolate variables in the query', async () => {
|
||||
const { datasource, fetchMock } = setup();
|
||||
datasource.query({
|
||||
targets: [
|
||||
{
|
||||
queryMode: 'Logs' as 'Logs',
|
||||
region: '$region',
|
||||
expression: 'fields $fields',
|
||||
logGroupNames: ['/some/$group'],
|
||||
},
|
||||
],
|
||||
} as any);
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0]).toMatchObject({
|
||||
queryString: 'fields templatedField',
|
||||
logGroupNames: ['/some/templatedGroup'],
|
||||
region: 'templatedRegion',
|
||||
});
|
||||
});
|
||||
|
||||
it('should add links to log queries', async () => {
|
||||
const { datasource } = setupForLogs();
|
||||
const observable = datasource.query({
|
||||
@ -121,7 +127,7 @@ describe('datasource', () => {
|
||||
function setup({ data = [] }: { data?: any } = {}) {
|
||||
const datasource = new CloudWatchDatasource(
|
||||
{ jsonData: { defaultRegion: 'us-west-1', tracingDatasourceUid: 'xray' } } as any,
|
||||
new TemplateSrv(),
|
||||
new TemplateSrvMock({ region: 'templatedRegion', fields: 'templatedField', group: 'templatedGroup' }) as any,
|
||||
{
|
||||
timeRange() {
|
||||
const time = dateTime('2021-01-01T01:00:00Z');
|
||||
|
@ -44,6 +44,7 @@ import {
|
||||
LogAction,
|
||||
MetricQuery,
|
||||
MetricRequest,
|
||||
StartQueryRequest,
|
||||
TSDBResponse,
|
||||
} from './types';
|
||||
import { CloudWatchLanguageProvider } from './language_provider';
|
||||
@ -163,6 +164,7 @@ export class CloudWatchDatasource
|
||||
|
||||
// This first starts the query which returns queryId which can be used to retrieve results.
|
||||
return this.makeLogActionRequest('StartQuery', queryParams, {
|
||||
makeReplacements: true,
|
||||
scopedVars: options.scopedVars,
|
||||
skipCache: true,
|
||||
}).pipe(
|
||||
@ -518,7 +520,7 @@ export class CloudWatchDatasource
|
||||
|
||||
makeLogActionRequest(
|
||||
subtype: LogAction,
|
||||
queryParams: any[],
|
||||
queryParams: Array<GetLogEventsRequest | StartQueryRequest | DescribeLogGroupsRequest | GetLogGroupFieldsRequest>,
|
||||
options: {
|
||||
scopedVars?: ScopedVars;
|
||||
makeReplacements?: boolean;
|
||||
@ -546,18 +548,23 @@ export class CloudWatchDatasource
|
||||
|
||||
if (options.makeReplacements) {
|
||||
requestParams.queries.forEach((query) => {
|
||||
if (query.hasOwnProperty('queryString')) {
|
||||
query.queryString = this.replace(query.queryString, options.scopedVars, true);
|
||||
const fieldsToReplace: Array<
|
||||
keyof (GetLogEventsRequest & StartQueryRequest & DescribeLogGroupsRequest & GetLogGroupFieldsRequest)
|
||||
> = ['queryString', 'logGroupNames', 'logGroupName', 'logGroupNamePrefix'];
|
||||
|
||||
for (const fieldName of fieldsToReplace) {
|
||||
if (query.hasOwnProperty(fieldName)) {
|
||||
if (Array.isArray(query[fieldName])) {
|
||||
query[fieldName] = query[fieldName].map((val: string) =>
|
||||
this.replace(val, options.scopedVars, true, fieldName)
|
||||
);
|
||||
} else {
|
||||
query[fieldName] = this.replace(query[fieldName], options.scopedVars, true, fieldName);
|
||||
}
|
||||
}
|
||||
}
|
||||
query.region = this.replace(query.region, options.scopedVars, true, 'region');
|
||||
query.region = this.getActualRegion(query.region);
|
||||
|
||||
// interpolate log groups
|
||||
if (query.logGroupNames) {
|
||||
query.logGroupNames = query.logGroupNames.map((logGroup: string) =>
|
||||
this.replace(logGroup, options.scopedVars, true, 'log groups')
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -290,22 +290,6 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
expect(i).toBe(3);
|
||||
});
|
||||
|
||||
it('should call the replace method on provided log groups', () => {
|
||||
const { ds } = getTestContext();
|
||||
const replaceSpy = jest.spyOn(ds, 'replace').mockImplementation((target?: string) => target ?? '');
|
||||
ds.makeLogActionRequest('StartQuery', [
|
||||
{
|
||||
queryString: 'test query string',
|
||||
region: 'default',
|
||||
logGroupNames: ['log-group', '${my_var}Variable', 'Cool${other_var}'],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(replaceSpy).toBeCalledWith('log-group', undefined, true, 'log groups');
|
||||
expect(replaceSpy).toBeCalledWith('${my_var}Variable', undefined, true, 'log groups');
|
||||
expect(replaceSpy).toBeCalledWith('Cool${other_var}', undefined, true, 'log groups');
|
||||
});
|
||||
});
|
||||
|
||||
describe('When performing CloudWatch metrics query', () => {
|
||||
|
Loading…
Reference in New Issue
Block a user