mirror of
https://github.com/grafana/grafana.git
synced 2025-02-10 23:55:47 -06:00
370 lines
12 KiB
TypeScript
370 lines
12 KiB
TypeScript
import { lastValueFrom } from 'rxjs';
|
|
import { toArray } from 'rxjs/operators';
|
|
|
|
import { CoreApp, Field } from '@grafana/data';
|
|
|
|
import {
|
|
CloudWatchSettings,
|
|
fieldsVariable,
|
|
logGroupNamesVariable,
|
|
regionVariable,
|
|
setupMockedDataSource,
|
|
} from './__mocks__/CloudWatchDataSource';
|
|
import { setupForLogs } from './__mocks__/logsTestContext';
|
|
import { validLogsQuery, validMetricSearchBuilderQuery } from './__mocks__/queries';
|
|
import { TimeRangeMock } from './__mocks__/timeRange';
|
|
import {
|
|
CloudWatchDefaultQuery,
|
|
CloudWatchLogsQuery,
|
|
CloudWatchMetricsQuery,
|
|
CloudWatchQuery,
|
|
MetricEditorMode,
|
|
MetricQueryType,
|
|
} from './types';
|
|
import * as templateUtils from './utils/templateVariableUtils';
|
|
|
|
describe('datasource', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
describe('query', () => {
|
|
it('should not run a query if log groups is not specified', async () => {
|
|
const { datasource, queryMock } = setupMockedDataSource();
|
|
await lastValueFrom(
|
|
datasource.query({
|
|
targets: [
|
|
{
|
|
queryMode: 'Logs',
|
|
id: '',
|
|
refId: '',
|
|
region: '',
|
|
expression: 'some query string', // missing logGroups and logGroupNames, this query will be not be run
|
|
},
|
|
{
|
|
queryMode: 'Logs',
|
|
id: '',
|
|
refId: '',
|
|
region: '',
|
|
logGroupNames: ['/some/group'],
|
|
expression: 'some query string',
|
|
},
|
|
],
|
|
requestId: '',
|
|
interval: '',
|
|
intervalMs: 0,
|
|
range: TimeRangeMock,
|
|
scopedVars: {},
|
|
timezone: '',
|
|
app: '',
|
|
startTime: 0,
|
|
})
|
|
);
|
|
|
|
expect(queryMock.mock.calls[0][0].targets).toHaveLength(1);
|
|
expect(queryMock.mock.calls[0][0].targets[0]).toMatchObject({
|
|
queryString: 'some query string',
|
|
logGroupNames: ['/some/group'],
|
|
region: 'us-west-1',
|
|
});
|
|
});
|
|
|
|
it('should not run a query if query expression is not specified', async () => {
|
|
const { datasource, queryMock } = setupMockedDataSource();
|
|
await lastValueFrom(
|
|
datasource.query({
|
|
targets: [
|
|
{
|
|
queryMode: 'Logs',
|
|
id: '',
|
|
refId: '',
|
|
region: '',
|
|
logGroupNames: ['/some/group'], // missing query expression, this query will be not be run
|
|
},
|
|
{
|
|
queryMode: 'Logs',
|
|
id: '',
|
|
refId: '',
|
|
region: '',
|
|
logGroupNames: ['/some/group'],
|
|
expression: 'some query string',
|
|
},
|
|
],
|
|
requestId: '',
|
|
interval: '',
|
|
intervalMs: 0,
|
|
range: TimeRangeMock,
|
|
scopedVars: {},
|
|
timezone: '',
|
|
app: '',
|
|
startTime: 0,
|
|
})
|
|
);
|
|
|
|
expect(queryMock.mock.calls[0][0].targets).toHaveLength(1);
|
|
expect(queryMock.mock.calls[0][0].targets[0]).toMatchObject({
|
|
queryString: 'some query string',
|
|
logGroupNames: ['/some/group'],
|
|
region: 'us-west-1',
|
|
});
|
|
});
|
|
|
|
it('should return empty response if queries are hidden', async () => {
|
|
const { datasource } = setupMockedDataSource();
|
|
const observable = datasource.query({
|
|
targets: [{ queryMode: 'Logs', hide: true, id: '', refId: '', region: '' }],
|
|
requestId: '',
|
|
interval: '',
|
|
intervalMs: 0,
|
|
range: TimeRangeMock,
|
|
scopedVars: {},
|
|
timezone: '',
|
|
app: '',
|
|
startTime: 0,
|
|
});
|
|
|
|
await expect(observable).toEmitValuesWith((received) => {
|
|
const response = received[0];
|
|
expect(response.data).toEqual([]);
|
|
});
|
|
});
|
|
|
|
const testTable: Array<{ query: CloudWatchQuery; valid: boolean }> = [
|
|
{ query: { ...validLogsQuery, hide: true }, valid: false },
|
|
{ query: { ...validLogsQuery, hide: false }, valid: true },
|
|
{ query: { ...validMetricSearchBuilderQuery, hide: true }, valid: false },
|
|
{ query: { ...validMetricSearchBuilderQuery, hide: true, id: 'queryA' }, valid: true },
|
|
{ query: { ...validMetricSearchBuilderQuery, hide: false }, valid: true },
|
|
];
|
|
|
|
test.each(testTable)('should filter out hidden queries unless id is provided', ({ query, valid }) => {
|
|
const { datasource } = setupMockedDataSource();
|
|
expect(datasource.filterQuery(query)).toEqual(valid);
|
|
});
|
|
|
|
it('should interpolate variables in the query', async () => {
|
|
const { datasource, queryMock } = setupMockedDataSource({
|
|
variables: [fieldsVariable, regionVariable],
|
|
});
|
|
await lastValueFrom(
|
|
datasource
|
|
.query({
|
|
targets: [
|
|
{
|
|
id: '',
|
|
refId: '',
|
|
queryMode: 'Logs',
|
|
region: '$region',
|
|
expression: 'fields $fields',
|
|
logGroupNames: ['/some/group'],
|
|
},
|
|
],
|
|
requestId: '',
|
|
interval: '',
|
|
intervalMs: 0,
|
|
range: TimeRangeMock,
|
|
scopedVars: {},
|
|
timezone: '',
|
|
app: '',
|
|
startTime: 0,
|
|
})
|
|
.pipe(toArray())
|
|
);
|
|
expect(queryMock.mock.calls[0][0].targets[0]).toMatchObject({
|
|
queryString: 'fields templatedField',
|
|
logGroupNames: ['/some/group'],
|
|
region: 'templatedRegion',
|
|
});
|
|
});
|
|
|
|
it('should interpolate multi-value template variable for log group names in the query', async () => {
|
|
const { datasource, queryMock } = setupMockedDataSource({
|
|
variables: [fieldsVariable, logGroupNamesVariable, regionVariable],
|
|
});
|
|
await lastValueFrom(
|
|
datasource
|
|
.query({
|
|
targets: [
|
|
{
|
|
id: '',
|
|
refId: '',
|
|
queryMode: 'Logs',
|
|
region: '$region',
|
|
expression: 'fields $fields',
|
|
logGroupNames: ['$groups'],
|
|
},
|
|
],
|
|
requestId: '',
|
|
interval: '',
|
|
intervalMs: 0,
|
|
range: TimeRangeMock,
|
|
scopedVars: {},
|
|
timezone: '',
|
|
app: '',
|
|
startTime: 0,
|
|
})
|
|
.pipe(toArray())
|
|
);
|
|
expect(queryMock.mock.calls[0][0].targets[0]).toMatchObject({
|
|
queryString: 'fields templatedField',
|
|
logGroupNames: ['templatedGroup-1', 'templatedGroup-2'],
|
|
region: 'templatedRegion',
|
|
});
|
|
});
|
|
|
|
it('should add links to log queries', async () => {
|
|
const { datasource } = setupForLogs();
|
|
|
|
const observable = datasource.query({
|
|
targets: [
|
|
{
|
|
id: '',
|
|
region: '',
|
|
queryMode: 'Logs',
|
|
logGroupNames: ['test'],
|
|
expression: 'some query',
|
|
refId: 'a',
|
|
},
|
|
],
|
|
requestId: '',
|
|
interval: '',
|
|
intervalMs: 0,
|
|
range: TimeRangeMock,
|
|
scopedVars: {},
|
|
timezone: '',
|
|
app: '',
|
|
startTime: 0,
|
|
});
|
|
|
|
const emits = await lastValueFrom(observable.pipe(toArray()));
|
|
expect(emits).toHaveLength(1);
|
|
expect(emits[0].data[0].fields.find((f: Field) => f.name === '@xrayTraceId').config.links).toMatchObject([
|
|
{
|
|
title: 'Xray',
|
|
url: '',
|
|
internal: {
|
|
query: { query: '${__value.raw}', region: 'us-west-1', queryType: 'getTrace' },
|
|
datasourceUid: 'xray',
|
|
datasourceName: 'Xray',
|
|
},
|
|
},
|
|
]);
|
|
|
|
expect(emits[0].data[0].fields.find((f: Field) => f.name === '@message').config.links).toMatchObject([
|
|
{
|
|
title: 'View in CloudWatch console',
|
|
url: "https://us-west-1.console.aws.amazon.com/cloudwatch/home?region=us-west-1#logs-insights:queryDetail=~(end~'2016-12-31T16*3a00*3a00.000Z~start~'2016-12-31T15*3a00*3a00.000Z~timeType~'ABSOLUTE~tz~'UTC~editorString~'some*20query~isLiveTail~false~source~(~'test))",
|
|
},
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe('resource requests', () => {
|
|
it('should map resource response to metric response', async () => {
|
|
const datasource = setupMockedDataSource({
|
|
getMock: jest.fn().mockResolvedValue([
|
|
{ value: { namespace: 'AWS/EC2', name: 'CPUUtilization' } },
|
|
{
|
|
value: { namespace: 'AWS/Redshift', name: 'CPUPercentage' },
|
|
},
|
|
]),
|
|
}).datasource;
|
|
const allMetrics = await datasource.resources.getAllMetrics({ region: 'us-east-2' });
|
|
expect(allMetrics[0].metricName).toEqual('CPUUtilization');
|
|
expect(allMetrics[0].namespace).toEqual('AWS/EC2');
|
|
expect(allMetrics[1].metricName).toEqual('CPUPercentage');
|
|
expect(allMetrics[1].namespace).toEqual('AWS/Redshift');
|
|
});
|
|
});
|
|
|
|
describe('when interpolating variables', () => {
|
|
it('should return an empty array if no queries are provided', () => {
|
|
const { datasource } = setupMockedDataSource();
|
|
|
|
expect(datasource.interpolateVariablesInQueries([], {})).toHaveLength(0);
|
|
});
|
|
|
|
it('should replace correct variables in CloudWatchLogsQuery', () => {
|
|
const { datasource, templateService } = setupMockedDataSource();
|
|
templateService.replace = jest.fn();
|
|
const variableName = 'someVar';
|
|
const logQuery: CloudWatchLogsQuery = {
|
|
queryMode: 'Logs',
|
|
expression: `$${variableName}`,
|
|
region: `$${variableName}`,
|
|
id: '',
|
|
refId: '',
|
|
};
|
|
|
|
datasource.interpolateVariablesInQueries([logQuery], {});
|
|
|
|
expect(templateService.replace).toHaveBeenCalledWith(`$${variableName}`, {});
|
|
expect(templateService.replace).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it('should replace correct variables in CloudWatchMetricsQuery', () => {
|
|
const { datasource, templateService } = setupMockedDataSource();
|
|
templateService.replace = jest.fn();
|
|
const mockGetVariableName = jest
|
|
.spyOn(templateUtils, 'getVariableName')
|
|
.mockImplementation((name: string) => name.replace('$', ''));
|
|
const variableName = 'someVar';
|
|
const metricsQuery: CloudWatchMetricsQuery = {
|
|
queryMode: 'Metrics',
|
|
id: 'someId',
|
|
refId: 'someRefId',
|
|
expression: `$${variableName}`,
|
|
region: `$${variableName}`,
|
|
period: `$${variableName}`,
|
|
alias: `$${variableName}`,
|
|
metricName: `$${variableName}`,
|
|
namespace: `$${variableName}`,
|
|
dimensions: {
|
|
[`$${variableName}`]: `$${variableName}`,
|
|
},
|
|
matchExact: false,
|
|
statistic: '',
|
|
sqlExpression: `$${variableName}`,
|
|
};
|
|
|
|
datasource.interpolateVariablesInQueries([metricsQuery], {});
|
|
|
|
// We interpolate `expression`, `sqlExpression`, `region`, `period`, `alias`, `metricName`, `dimensions`, and `nameSpace` in CloudWatchMetricsQuery
|
|
expect(templateService.replace).toHaveBeenCalledWith(`$${variableName}`, {});
|
|
expect(templateService.replace).toHaveBeenCalledTimes(8);
|
|
|
|
expect(mockGetVariableName).toHaveBeenCalledWith(`$${variableName}`);
|
|
expect(mockGetVariableName).toHaveBeenCalledTimes(1);
|
|
});
|
|
});
|
|
|
|
describe('when setting default query', () => {
|
|
it('should set default query to be a Metrics query', () => {
|
|
const { datasource } = setupMockedDataSource();
|
|
expect(datasource.getDefaultQuery(CoreApp.PanelEditor).queryMode).toEqual('Metrics');
|
|
});
|
|
it('should set default log groups in default query', () => {
|
|
const { datasource } = setupMockedDataSource({
|
|
customInstanceSettings: {
|
|
...CloudWatchSettings,
|
|
jsonData: { ...CloudWatchSettings.jsonData, defaultLogGroups: ['testLogGroup'] },
|
|
},
|
|
});
|
|
expect((datasource.getDefaultQuery(CoreApp.PanelEditor) as CloudWatchDefaultQuery).logGroupNames).toEqual([
|
|
'testLogGroup',
|
|
]);
|
|
});
|
|
it('should set default values from metrics query', () => {
|
|
const { datasource } = setupMockedDataSource();
|
|
expect(datasource.getDefaultQuery(CoreApp.PanelEditor).region).toEqual('default');
|
|
expect((datasource.getDefaultQuery(CoreApp.PanelEditor) as CloudWatchDefaultQuery).statistic).toEqual('Average');
|
|
expect((datasource.getDefaultQuery(CoreApp.PanelEditor) as CloudWatchDefaultQuery).metricQueryType).toEqual(
|
|
MetricQueryType.Search
|
|
);
|
|
expect((datasource.getDefaultQuery(CoreApp.PanelEditor) as CloudWatchDefaultQuery).metricEditorMode).toEqual(
|
|
MetricEditorMode.Builder
|
|
);
|
|
expect((datasource.getDefaultQuery(CoreApp.PanelEditor) as CloudWatchDefaultQuery).matchExact).toEqual(true);
|
|
});
|
|
});
|
|
});
|