mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
CloudWatch: Support request cancellation properly (#28865)
This commit is contained in:
parent
f9281742d7
commit
32d4c8c6bc
@ -1,88 +1,101 @@
|
||||
import { DataQueryResponse, dateTime, DefaultTimeRange } from '@grafana/data';
|
||||
import { of } from 'rxjs';
|
||||
import { setBackendSrv } from '@grafana/runtime';
|
||||
import { dateTime, DefaultTimeRange, observableTester } from '@grafana/data';
|
||||
|
||||
import { TemplateSrv } from '../../../features/templating/template_srv';
|
||||
import { CloudWatchDatasource } from './datasource';
|
||||
|
||||
describe('datasource', () => {
|
||||
describe('query', () => {
|
||||
it('should return error if log query and log groups is not specified', async () => {
|
||||
it('should return error if log query and log groups is not specified', done => {
|
||||
const { datasource } = setup();
|
||||
const response: DataQueryResponse = (await datasource
|
||||
.query({
|
||||
|
||||
observableTester().subscribeAndExpectOnNext({
|
||||
observable: datasource.query({
|
||||
targets: [
|
||||
{
|
||||
queryMode: 'Logs' as 'Logs',
|
||||
},
|
||||
],
|
||||
} as any)
|
||||
.toPromise()) as any;
|
||||
expect(response.error?.message).toBe('Log group is required');
|
||||
} as any),
|
||||
expect: response => {
|
||||
expect(response.error?.message).toBe('Log group is required');
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
it('should return empty response if queries are hidden', async () => {
|
||||
it('should return empty response if queries are hidden', done => {
|
||||
const { datasource } = setup();
|
||||
const response: DataQueryResponse = (await datasource
|
||||
.query({
|
||||
|
||||
observableTester().subscribeAndExpectOnNext({
|
||||
observable: datasource.query({
|
||||
targets: [
|
||||
{
|
||||
queryMode: 'Logs' as 'Logs',
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
} as any)
|
||||
.toPromise()) as any;
|
||||
expect(response.data).toEqual([]);
|
||||
} as any),
|
||||
expect: response => {
|
||||
expect(response.data).toEqual([]);
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('performTimeSeriesQuery', () => {
|
||||
it('should return the same length of data as result', async () => {
|
||||
const { datasource } = setup();
|
||||
const awsRequestMock = jest.spyOn(datasource, 'awsRequest');
|
||||
const buildCloudwatchConsoleUrlMock = jest.spyOn(datasource, 'buildCloudwatchConsoleUrl');
|
||||
buildCloudwatchConsoleUrlMock.mockImplementation(() => '');
|
||||
awsRequestMock.mockImplementation(async () => {
|
||||
return {
|
||||
it('should return the same length of data as result', done => {
|
||||
const { datasource } = setup({
|
||||
data: {
|
||||
results: {
|
||||
a: { refId: 'a', series: [{ name: 'cpu', points: [1, 1] }], meta: { gmdMeta: '' } },
|
||||
b: { refId: 'b', series: [{ name: 'memory', points: [2, 2] }], meta: { gmdMeta: '' } },
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
const buildCloudwatchConsoleUrlMock = jest.spyOn(datasource, 'buildCloudwatchConsoleUrl');
|
||||
buildCloudwatchConsoleUrlMock.mockImplementation(() => '');
|
||||
|
||||
observableTester().subscribeAndExpectOnNext({
|
||||
observable: datasource.performTimeSeriesQuery(
|
||||
{
|
||||
queries: [
|
||||
{ datasourceId: 1, refId: 'a' },
|
||||
{ datasourceId: 1, refId: 'b' },
|
||||
],
|
||||
} as any,
|
||||
{ from: dateTime(), to: dateTime() } as any
|
||||
),
|
||||
expect: response => {
|
||||
expect(response.data.length).toEqual(2);
|
||||
},
|
||||
done,
|
||||
});
|
||||
const response: DataQueryResponse = await datasource.performTimeSeriesQuery(
|
||||
{
|
||||
queries: [
|
||||
{ datasourceId: 1, refId: 'a' },
|
||||
{ datasourceId: 1, refId: 'b' },
|
||||
],
|
||||
} as any,
|
||||
{ from: dateTime(), to: dateTime() } as any
|
||||
);
|
||||
expect(response.data.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('describeLogGroup', () => {
|
||||
it('replaces region correctly in the query', async () => {
|
||||
const { datasource, datasourceRequestMock } = setup();
|
||||
const { datasource, fetchMock } = setup();
|
||||
await datasource.describeLogGroups({ region: 'default' });
|
||||
expect(datasourceRequestMock.mock.calls[0][0].data.queries[0].region).toBe('us-west-1');
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].region).toBe('us-west-1');
|
||||
|
||||
await datasource.describeLogGroups({ region: 'eu-east' });
|
||||
expect(datasourceRequestMock.mock.calls[1][0].data.queries[0].region).toBe('eu-east');
|
||||
expect(fetchMock.mock.calls[1][0].data.queries[0].region).toBe('eu-east');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
function setup() {
|
||||
function setup({ data = [] }: { data?: any } = {}) {
|
||||
const datasource = new CloudWatchDatasource({ jsonData: { defaultRegion: 'us-west-1' } } as any, new TemplateSrv(), {
|
||||
timeRange() {
|
||||
return DefaultTimeRange;
|
||||
},
|
||||
} as any);
|
||||
const datasourceRequestMock = jest.fn();
|
||||
datasourceRequestMock.mockResolvedValue({ data: [] });
|
||||
setBackendSrv({ datasourceRequest: datasourceRequestMock } as any);
|
||||
const fetchMock = jest.fn().mockReturnValue(of({ data }));
|
||||
setBackendSrv({ fetch: fetchMock } as any);
|
||||
|
||||
return { datasource, datasourceRequestMock };
|
||||
return { datasource, fetchMock };
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react';
|
||||
import angular from 'angular';
|
||||
import _ from 'lodash';
|
||||
import { from, merge, Observable, of, zip } from 'rxjs';
|
||||
import { merge, Observable, of, throwError, zip } from 'rxjs';
|
||||
import {
|
||||
catchError,
|
||||
concatMap,
|
||||
@ -184,7 +184,7 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
queries: queryParams,
|
||||
};
|
||||
|
||||
return from(this.awsRequest(TSDB_QUERY_ENDPOINT, requestParams)).pipe(
|
||||
return this.awsRequest(TSDB_QUERY_ENDPOINT, requestParams).pipe(
|
||||
mergeMap((response: TSDBResponse) => {
|
||||
const channelName: string = response.results['A'].meta.channelName;
|
||||
const channel = getGrafanaLiveSrv().getChannel({
|
||||
@ -310,11 +310,17 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
queries: validMetricsQueries,
|
||||
};
|
||||
|
||||
return from(this.performTimeSeriesQuery(request, options.range));
|
||||
return this.performTimeSeriesQuery(request, options.range);
|
||||
};
|
||||
|
||||
logsQuery(
|
||||
queryParams: Array<{ queryId: string; refId: string; limit?: number; region: string; statsGroups?: string[] }>
|
||||
queryParams: Array<{
|
||||
queryId: string;
|
||||
refId: string;
|
||||
limit?: number;
|
||||
region: string;
|
||||
statsGroups?: string[];
|
||||
}>
|
||||
): Observable<DataQueryResponse> {
|
||||
this.logQueries = {};
|
||||
queryParams.forEach(param => {
|
||||
@ -587,74 +593,76 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
)}`;
|
||||
}
|
||||
|
||||
async performTimeSeriesQuery(request: MetricRequest, { from, to }: TimeRange): Promise<any> {
|
||||
try {
|
||||
const res: TSDBResponse = await this.awsRequest(TSDB_QUERY_ENDPOINT, request);
|
||||
const dataframes: DataFrame[] = toDataQueryResponse({ data: res }).data;
|
||||
if (!dataframes || dataframes.length <= 0) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
const data = dataframes.map(frame => {
|
||||
const queryResult = res.results[frame.refId!];
|
||||
const error = queryResult.error ? { message: queryResult.error } : null;
|
||||
if (!queryResult) {
|
||||
return { frame, error };
|
||||
performTimeSeriesQuery(request: MetricRequest, { from, to }: TimeRange): Observable<any> {
|
||||
return this.awsRequest(TSDB_QUERY_ENDPOINT, request).pipe(
|
||||
map(res => {
|
||||
const dataframes: DataFrame[] = toDataQueryResponse({ data: res }).data;
|
||||
if (!dataframes || dataframes.length <= 0) {
|
||||
return { data: [] };
|
||||
}
|
||||
|
||||
const requestQuery = request.queries.find(q => q.refId === frame.refId!) as any;
|
||||
|
||||
const link = this.buildCloudwatchConsoleUrl(
|
||||
requestQuery!,
|
||||
from.toISOString(),
|
||||
to.toISOString(),
|
||||
frame.refId!,
|
||||
queryResult.meta.gmdMeta
|
||||
);
|
||||
|
||||
if (link) {
|
||||
for (const field of frame.fields) {
|
||||
field.config.links = [
|
||||
{
|
||||
url: link,
|
||||
title: 'View in CloudWatch console',
|
||||
targetBlank: true,
|
||||
},
|
||||
];
|
||||
const data = dataframes.map(frame => {
|
||||
const queryResult = res.results[frame.refId!];
|
||||
const error = queryResult.error ? { message: queryResult.error } : null;
|
||||
if (!queryResult) {
|
||||
return { frame, error };
|
||||
}
|
||||
|
||||
const requestQuery = request.queries.find(q => q.refId === frame.refId!) as any;
|
||||
|
||||
const link = this.buildCloudwatchConsoleUrl(
|
||||
requestQuery!,
|
||||
from.toISOString(),
|
||||
to.toISOString(),
|
||||
frame.refId!,
|
||||
queryResult.meta.gmdMeta
|
||||
);
|
||||
|
||||
if (link) {
|
||||
for (const field of frame.fields) {
|
||||
field.config.links = [
|
||||
{
|
||||
url: link,
|
||||
title: 'View in CloudWatch console',
|
||||
targetBlank: true,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
return { frame, error };
|
||||
});
|
||||
|
||||
return {
|
||||
data: data.map(o => o.frame),
|
||||
error: data
|
||||
.map(o => o.error)
|
||||
.reduce((err, error) => {
|
||||
return err || error;
|
||||
}, null),
|
||||
};
|
||||
}),
|
||||
catchError(err => {
|
||||
if (/^Throttling:.*/.test(err.data.message)) {
|
||||
const failedRedIds = Object.keys(err.data.results);
|
||||
const regionsAffected = Object.values(request.queries).reduce(
|
||||
(res: string[], { refId, region }) =>
|
||||
(refId && !failedRedIds.includes(refId)) || res.includes(region) ? res : [...res, region],
|
||||
[]
|
||||
) as string[];
|
||||
|
||||
regionsAffected.forEach(region => this.debouncedAlert(this.datasourceName, this.getActualRegion(region)));
|
||||
}
|
||||
return { frame, error };
|
||||
});
|
||||
|
||||
return {
|
||||
data: data.map(o => o.frame),
|
||||
error: data
|
||||
.map(o => o.error)
|
||||
.reduce((err, error) => {
|
||||
return err || error;
|
||||
}, null),
|
||||
};
|
||||
} catch (err) {
|
||||
if (/^Throttling:.*/.test(err.data.message)) {
|
||||
const failedRedIds = Object.keys(err.data.results);
|
||||
const regionsAffected = Object.values(request.queries).reduce(
|
||||
(res: string[], { refId, region }) =>
|
||||
(refId && !failedRedIds.includes(refId)) || res.includes(region) ? res : [...res, region],
|
||||
[]
|
||||
) as string[];
|
||||
if (err.data && err.data.message === 'Metric request error' && err.data.error) {
|
||||
err.data.message = err.data.error;
|
||||
}
|
||||
|
||||
regionsAffected.forEach(region => this.debouncedAlert(this.datasourceName, this.getActualRegion(region)));
|
||||
}
|
||||
|
||||
if (err.data && err.data.message === 'Metric request error' && err.data.error) {
|
||||
err.data.message = err.data.error;
|
||||
}
|
||||
|
||||
throw err;
|
||||
}
|
||||
return throwError(err);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
transformSuggestDataFromTable(suggestData: TSDBResponse) {
|
||||
transformSuggestDataFromTable(suggestData: TSDBResponse): Array<{ text: any; label: any; value: any }> {
|
||||
return suggestData.results['metricFindQuery'].tables[0].rows.map(([text, value]) => ({
|
||||
text,
|
||||
value,
|
||||
@ -662,7 +670,7 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
}));
|
||||
}
|
||||
|
||||
doMetricQueryRequest(subtype: string, parameters: any) {
|
||||
doMetricQueryRequest(subtype: string, parameters: any): Promise<Array<{ text: any; label: any; value: any }>> {
|
||||
const range = this.timeSrv.timeRange();
|
||||
return this.awsRequest(TSDB_QUERY_ENDPOINT, {
|
||||
from: range.from.valueOf().toString(),
|
||||
@ -678,9 +686,13 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
...parameters,
|
||||
},
|
||||
],
|
||||
}).then((r: TSDBResponse) => {
|
||||
return this.transformSuggestDataFromTable(r);
|
||||
});
|
||||
})
|
||||
.pipe(
|
||||
map(r => {
|
||||
return this.transformSuggestDataFromTable(r);
|
||||
})
|
||||
)
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
makeLogActionRequest(
|
||||
@ -724,7 +736,7 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
|
||||
const resultsToDataFrames = (val: any): DataFrame[] => toDataQueryResponse(val).data || [];
|
||||
|
||||
return from(this.awsRequest(TSDB_QUERY_ENDPOINT, requestParams)).pipe(
|
||||
return this.awsRequest(TSDB_QUERY_ENDPOINT, requestParams).pipe(
|
||||
map(response => resultsToDataFrames({ data: response })),
|
||||
catchError(err => {
|
||||
if (err.data?.error) {
|
||||
@ -920,15 +932,19 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
...parameters,
|
||||
},
|
||||
],
|
||||
}).then((r: TSDBResponse) => {
|
||||
return r.results['annotationQuery'].tables[0].rows.map(v => ({
|
||||
annotation: annotation,
|
||||
time: Date.parse(v[0]),
|
||||
title: v[1],
|
||||
tags: [v[2]],
|
||||
text: v[3],
|
||||
}));
|
||||
});
|
||||
})
|
||||
.pipe(
|
||||
map(r => {
|
||||
return r.results['annotationQuery'].tables[0].rows.map(v => ({
|
||||
annotation: annotation,
|
||||
time: Date.parse(v[0]),
|
||||
title: v[1],
|
||||
tags: [v[2]],
|
||||
text: v[3],
|
||||
}));
|
||||
})
|
||||
)
|
||||
.toPromise();
|
||||
}
|
||||
|
||||
targetContainsTemplate(target: any) {
|
||||
@ -955,16 +971,16 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
}));
|
||||
}
|
||||
|
||||
async awsRequest(url: string, data: MetricRequest) {
|
||||
awsRequest(url: string, data: MetricRequest): Observable<TSDBResponse> {
|
||||
const options = {
|
||||
method: 'POST',
|
||||
url,
|
||||
data,
|
||||
};
|
||||
|
||||
const result = await getBackendSrv().datasourceRequest(options);
|
||||
|
||||
return result.data;
|
||||
return getBackendSrv()
|
||||
.fetch<TSDBResponse>(options)
|
||||
.pipe(map(result => result.data));
|
||||
}
|
||||
|
||||
getDefaultRegion() {
|
||||
|
@ -3,22 +3,22 @@ import _ from 'lodash';
|
||||
|
||||
// Services & Utils
|
||||
import syntax, {
|
||||
QUERY_COMMANDS,
|
||||
AGGREGATION_FUNCTIONS_STATS,
|
||||
STRING_FUNCTIONS,
|
||||
DATETIME_FUNCTIONS,
|
||||
IP_FUNCTIONS,
|
||||
BOOLEAN_FUNCTIONS,
|
||||
NUMERIC_OPERATORS,
|
||||
DATETIME_FUNCTIONS,
|
||||
FIELD_AND_FILTER_FUNCTIONS,
|
||||
IP_FUNCTIONS,
|
||||
NUMERIC_OPERATORS,
|
||||
QUERY_COMMANDS,
|
||||
STRING_FUNCTIONS,
|
||||
} from './syntax';
|
||||
|
||||
// Types
|
||||
import { CloudWatchQuery } from './types';
|
||||
import { AbsoluteTimeRange, LanguageProvider, HistoryItem } from '@grafana/data';
|
||||
import { CloudWatchQuery, TSDBResponse } from './types';
|
||||
import { AbsoluteTimeRange, HistoryItem, LanguageProvider } from '@grafana/data';
|
||||
|
||||
import { CloudWatchDatasource } from './datasource';
|
||||
import { TypeaheadInput, TypeaheadOutput, Token } from '@grafana/ui';
|
||||
import { Token, TypeaheadInput, TypeaheadOutput } from '@grafana/ui';
|
||||
import Prism, { Grammar } from 'prismjs';
|
||||
|
||||
export type CloudWatchHistoryItem = HistoryItem<CloudWatchQuery>;
|
||||
@ -49,8 +49,8 @@ export class CloudWatchLanguageProvider extends LanguageProvider {
|
||||
return syntax;
|
||||
}
|
||||
|
||||
request = (url: string, params?: any): Promise<{ data: { data: string[] } }> => {
|
||||
return this.datasource.awsRequest(url, params);
|
||||
request = (url: string, params?: any): Promise<TSDBResponse> => {
|
||||
return this.datasource.awsRequest(url, params).toPromise();
|
||||
};
|
||||
|
||||
start = () => {
|
||||
|
@ -1,6 +1,4 @@
|
||||
import '../datasource';
|
||||
import { CloudWatchDatasource, MAX_ATTEMPTS } from '../datasource';
|
||||
import * as redux from 'app/store/store';
|
||||
import { interval, of, throwError } from 'rxjs';
|
||||
import {
|
||||
DataFrame,
|
||||
DataQueryErrorType,
|
||||
@ -8,22 +6,20 @@ import {
|
||||
DataSourceInstanceSettings,
|
||||
dateMath,
|
||||
getFrameDisplayName,
|
||||
observableTester,
|
||||
} from '@grafana/data';
|
||||
import { FetchResponse } from '@grafana/runtime';
|
||||
|
||||
import * as redux from 'app/store/store';
|
||||
import '../datasource';
|
||||
import { CloudWatchDatasource, MAX_ATTEMPTS } from '../datasource';
|
||||
import { TemplateSrv } from 'app/features/templating/template_srv';
|
||||
import {
|
||||
CloudWatchLogsQuery,
|
||||
CloudWatchLogsQueryStatus,
|
||||
CloudWatchMetricsQuery,
|
||||
CloudWatchQuery,
|
||||
LogAction,
|
||||
} from '../types';
|
||||
import { CloudWatchLogsQuery, CloudWatchLogsQueryStatus, CloudWatchMetricsQuery, LogAction } from '../types';
|
||||
import { backendSrv } from 'app/core/services/backend_srv'; // will use the version in __mocks__
|
||||
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
|
||||
import { convertToStoreState } from '../../../../../test/helpers/convertToStoreState';
|
||||
import { getTemplateSrvDependencies } from 'test/helpers/getTemplateSrvDependencies';
|
||||
import { interval, of } from 'rxjs';
|
||||
import { CustomVariableModel, initialVariableModelState, VariableHide } from '../../../../features/variables/types';
|
||||
import { TimeSrvStub } from '../../../../../test/specs/helpers';
|
||||
|
||||
import * as rxjsUtils from '../utils/rxjs/increasingInterval';
|
||||
|
||||
@ -38,18 +34,22 @@ jest.mock('@grafana/runtime', () => ({
|
||||
getBackendSrv: () => backendSrv,
|
||||
}));
|
||||
|
||||
describe('CloudWatchDatasource', () => {
|
||||
const datasourceRequestMock = jest.spyOn(backendSrv, 'datasourceRequest');
|
||||
type Args = { response?: any; throws?: boolean; templateSrv?: TemplateSrv };
|
||||
|
||||
function getTestContext({ response = {}, throws = false, templateSrv = new TemplateSrv() }: Args = {}) {
|
||||
jest.clearAllMocks();
|
||||
|
||||
const fetchMock = jest.spyOn(backendSrv, 'fetch');
|
||||
|
||||
throws
|
||||
? fetchMock.mockImplementation(() => throwError(response))
|
||||
: fetchMock.mockImplementation(() => of(createFetchResponse(response)));
|
||||
|
||||
const instanceSettings = {
|
||||
jsonData: { defaultRegion: 'us-east-1' },
|
||||
name: 'TestDatasource',
|
||||
} as DataSourceInstanceSettings;
|
||||
|
||||
let templateSrv = new TemplateSrv();
|
||||
const start = 1483196400 * 1000;
|
||||
const defaultTimeRange = { from: new Date(start), to: new Date(start + 3600 * 1000) };
|
||||
|
||||
const timeSrv = {
|
||||
time: { from: '2016-12-31 15:00:00Z', to: '2016-12-31 16:00:00Z' },
|
||||
timeRange: () => {
|
||||
@ -60,35 +60,32 @@ describe('CloudWatchDatasource', () => {
|
||||
},
|
||||
} as TimeSrv;
|
||||
|
||||
const ctx = {
|
||||
templateSrv,
|
||||
} as any;
|
||||
const ds = new CloudWatchDatasource(instanceSettings, templateSrv, timeSrv);
|
||||
|
||||
return { ds, fetchMock, instanceSettings };
|
||||
}
|
||||
|
||||
describe('CloudWatchDatasource', () => {
|
||||
const start = 1483196400 * 1000;
|
||||
const defaultTimeRange = { from: new Date(start), to: new Date(start + 3600 * 1000) };
|
||||
|
||||
beforeEach(() => {
|
||||
ctx.ds = new CloudWatchDatasource(instanceSettings, templateSrv, timeSrv);
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
describe('When getting log groups', () => {
|
||||
beforeEach(() => {
|
||||
datasourceRequestMock.mockImplementation(() =>
|
||||
Promise.resolve({
|
||||
data: {
|
||||
results: {
|
||||
A: {
|
||||
dataframes: [
|
||||
'QVJST1cxAAD/////GAEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAFgAAAACAAAAKAAAAAQAAAB8////CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAAJz///8IAAAAFAAAAAkAAABsb2dHcm91cHMAAAAEAAAAbmFtZQAAAAABAAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAABMAAAAUAAAAAAABQFMAAAAAQAAAAwAAAAIAAwACAAEAAgAAAAIAAAAGAAAAAwAAABsb2dHcm91cE5hbWUAAAAABAAAAG5hbWUAAAAAAAAAAAQABAAEAAAADAAAAGxvZ0dyb3VwTmFtZQAAAAD/////mAAAABQAAAAAAAAADAAWABQAEwAMAAQADAAAAGAGAAAAAAAAFAAAAAAAAAMDAAoAGAAMAAgABAAKAAAAFAAAAEgAAAAhAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAAAAAACIAAAAAAAAANgFAAAAAAAAAAAAAAEAAAAhAAAAAAAAAAAAAAAAAAAAAAAAADIAAABiAAAAkQAAALwAAADuAAAAHwEAAFQBAACHAQAAtQEAAOoBAAAbAgAASgIAAHQCAAClAgAA1QIAABADAABEAwAAdgMAAKMDAADXAwAACQQAAEAEAAB3BAAAlwQAAK0EAAC8BAAA+wQAAEIFAABhBQAAeAUAAJIFAAC0BQAA1gUAAC9hd3MvY29udGFpbmVyaW5zaWdodHMvZGV2MzAzLXdvcmtzaG9wL2FwcGxpY2F0aW9uL2F3cy9jb250YWluZXJpbnNpZ2h0cy9kZXYzMDMtd29ya3Nob3AvZGF0YXBsYW5lL2F3cy9jb250YWluZXJpbnNpZ2h0cy9kZXYzMDMtd29ya3Nob3AvZmxvd2xvZ3MvYXdzL2NvbnRhaW5lcmluc2lnaHRzL2RldjMwMy13b3Jrc2hvcC9ob3N0L2F3cy9jb250YWluZXJpbnNpZ2h0cy9kZXYzMDMtd29ya3Nob3AvcGVyZm9ybWFuY2UvYXdzL2NvbnRhaW5lcmluc2lnaHRzL2RldjMwMy13b3Jrc2hvcC9wcm9tZXRoZXVzL2F3cy9jb250YWluZXJpbnNpZ2h0cy9lY29tbWVyY2Utc29ja3Nob3AvYXBwbGljYXRpb24vYXdzL2NvbnRhaW5lcmluc2lnaHRzL2Vjb21tZXJjZS1zb2Nrc2hvcC9kYXRhcGxhbmUvYXdzL2NvbnRhaW5lcmluc2lnaHRzL2Vjb21tZXJjZS1zb2Nrc2hvcC9ob3N0L2F3cy9jb250YWluZXJpbnNpZ2h0cy9lY29tbWVyY2Utc29ja3Nob3AvcGVyZm9ybWFuY2UvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1wZXJmL2FwcGxpY2F0aW9uL2F3cy9jb250YWluZXJpbnNpZ2h0cy93YXRjaGRlbW8tcGVyZi9kYXRhcGxhbmUvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1wZXJmL2hvc3QvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1wZXJmL3BlcmZvcm1hbmNlL2F3cy9jb250YWluZXJpbnNpZ2h0cy93YXRjaGRlbW8tcGVyZi9wcm9tZXRoZXVzL2F3cy9jb250YWluZXJpbnNpZ2h0cy93YXRjaGRlbW8tcHJvZC11cy1lYXN0LTEvcGVyZm9ybWFuY2UvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1zdGFnaW5nL2FwcGxpY2F0aW9uL2F3cy9jb250YWluZXJpbnNpZ2h0cy93YXRjaGRlbW8tc3RhZ2luZy9kYXRhcGxhbmUvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1zdGFnaW5nL2hvc3QvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1zdGFnaW5nL3BlcmZvcm1hbmNlL2F3cy9lY3MvY29udGFpbmVyaW5zaWdodHMvYnVnYmFzaC1lYzIvcGVyZm9ybWFuY2UvYXdzL2Vjcy9jb250YWluZXJpbnNpZ2h0cy9lY3MtZGVtb3dvcmtzaG9wL3BlcmZvcm1hbmNlL2F3cy9lY3MvY29udGFpbmVyaW5zaWdodHMvZWNzLXdvcmtzaG9wLWRldi9wZXJmb3JtYW5jZS9hd3MvZWtzL2RldjMwMy13b3Jrc2hvcC9jbHVzdGVyL2F3cy9ldmVudHMvY2xvdWR0cmFpbC9hd3MvZXZlbnRzL2Vjcy9hd3MvbGFtYmRhL2N3c3luLW15Y2FuYXJ5LWZhYzk3ZGVkLWYxMzQtNDk5YS05ZDcxLTRjM2JlMWY2MzE4Mi9hd3MvbGFtYmRhL2N3c3luLXdhdGNoLWxpbmtjaGVja3MtZWY3ZWYyNzMtNWRhMi00NjYzLWFmNTQtZDJmNTJkNTViMDYwL2Vjcy9lY3MtY3dhZ2VudC1kYWVtb24tc2VydmljZS9lY3MvZWNzLWRlbW8tbGltaXRUYXNrQ2xvdWRUcmFpbC9EZWZhdWx0TG9nR3JvdXBjb250YWluZXItaW5zaWdodHMtcHJvbWV0aGV1cy1iZXRhY29udGFpbmVyLWluc2lnaHRzLXByb21ldGhldXMtZGVtbwAAEAAAAAwAFAASAAwACAAEAAwAAAAQAAAALAAAADwAAAAAAAMAAQAAACgBAAAAAAAAoAAAAAAAAABgBgAAAAAAAAAAAAAAAAAAAAAAAAAACgAMAAAACAAEAAoAAAAIAAAAWAAAAAIAAAAoAAAABAAAAHz///8IAAAADAAAAAAAAAAAAAAABQAAAHJlZklkAAAAnP///wgAAAAUAAAACQAAAGxvZ0dyb3VwcwAAAAQAAABuYW1lAAAAAAEAAAAYAAAAAAASABgAFAATABIADAAAAAgABAASAAAAFAAAAEwAAABQAAAAAAAFAUwAAAABAAAADAAAAAgADAAIAAQACAAAAAgAAAAYAAAADAAAAGxvZ0dyb3VwTmFtZQAAAAAEAAAAbmFtZQAAAAAAAAAABAAEAAQAAAAMAAAAbG9nR3JvdXBOYW1lAAAAAEgBAABBUlJPVzE=',
|
||||
],
|
||||
refId: 'A',
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should return log groups as an array of strings', async () => {
|
||||
const logGroups = await ctx.ds.describeLogGroups();
|
||||
const response = {
|
||||
results: {
|
||||
A: {
|
||||
dataframes: [
|
||||
'QVJST1cxAAD/////GAEAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEDAAoADAAAAAgABAAKAAAACAAAAFgAAAACAAAAKAAAAAQAAAB8////CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAAJz///8IAAAAFAAAAAkAAABsb2dHcm91cHMAAAAEAAAAbmFtZQAAAAABAAAAGAAAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAABMAAAAUAAAAAAABQFMAAAAAQAAAAwAAAAIAAwACAAEAAgAAAAIAAAAGAAAAAwAAABsb2dHcm91cE5hbWUAAAAABAAAAG5hbWUAAAAAAAAAAAQABAAEAAAADAAAAGxvZ0dyb3VwTmFtZQAAAAD/////mAAAABQAAAAAAAAADAAWABQAEwAMAAQADAAAAGAGAAAAAAAAFAAAAAAAAAMDAAoAGAAMAAgABAAKAAAAFAAAAEgAAAAhAAAAAAAAAAAAAAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAAAAAACIAAAAAAAAANgFAAAAAAAAAAAAAAEAAAAhAAAAAAAAAAAAAAAAAAAAAAAAADIAAABiAAAAkQAAALwAAADuAAAAHwEAAFQBAACHAQAAtQEAAOoBAAAbAgAASgIAAHQCAAClAgAA1QIAABADAABEAwAAdgMAAKMDAADXAwAACQQAAEAEAAB3BAAAlwQAAK0EAAC8BAAA+wQAAEIFAABhBQAAeAUAAJIFAAC0BQAA1gUAAC9hd3MvY29udGFpbmVyaW5zaWdodHMvZGV2MzAzLXdvcmtzaG9wL2FwcGxpY2F0aW9uL2F3cy9jb250YWluZXJpbnNpZ2h0cy9kZXYzMDMtd29ya3Nob3AvZGF0YXBsYW5lL2F3cy9jb250YWluZXJpbnNpZ2h0cy9kZXYzMDMtd29ya3Nob3AvZmxvd2xvZ3MvYXdzL2NvbnRhaW5lcmluc2lnaHRzL2RldjMwMy13b3Jrc2hvcC9ob3N0L2F3cy9jb250YWluZXJpbnNpZ2h0cy9kZXYzMDMtd29ya3Nob3AvcGVyZm9ybWFuY2UvYXdzL2NvbnRhaW5lcmluc2lnaHRzL2RldjMwMy13b3Jrc2hvcC9wcm9tZXRoZXVzL2F3cy9jb250YWluZXJpbnNpZ2h0cy9lY29tbWVyY2Utc29ja3Nob3AvYXBwbGljYXRpb24vYXdzL2NvbnRhaW5lcmluc2lnaHRzL2Vjb21tZXJjZS1zb2Nrc2hvcC9kYXRhcGxhbmUvYXdzL2NvbnRhaW5lcmluc2lnaHRzL2Vjb21tZXJjZS1zb2Nrc2hvcC9ob3N0L2F3cy9jb250YWluZXJpbnNpZ2h0cy9lY29tbWVyY2Utc29ja3Nob3AvcGVyZm9ybWFuY2UvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1wZXJmL2FwcGxpY2F0aW9uL2F3cy9jb250YWluZXJpbnNpZ2h0cy93YXRjaGRlbW8tcGVyZi9kYXRhcGxhbmUvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1wZXJmL2hvc3QvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1wZXJmL3BlcmZvcm1hbmNlL2F3cy9jb250YWluZXJpbnNpZ2h0cy93YXRjaGRlbW8tcGVyZi9wcm9tZXRoZXVzL2F3cy9jb250YWluZXJpbnNpZ2h0cy93YXRjaGRlbW8tcHJvZC11cy1lYXN0LTEvcGVyZm9ybWFuY2UvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1zdGFnaW5nL2FwcGxpY2F0aW9uL2F3cy9jb250YWluZXJpbnNpZ2h0cy93YXRjaGRlbW8tc3RhZ2luZy9kYXRhcGxhbmUvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1zdGFnaW5nL2hvc3QvYXdzL2NvbnRhaW5lcmluc2lnaHRzL3dhdGNoZGVtby1zdGFnaW5nL3BlcmZvcm1hbmNlL2F3cy9lY3MvY29udGFpbmVyaW5zaWdodHMvYnVnYmFzaC1lYzIvcGVyZm9ybWFuY2UvYXdzL2Vjcy9jb250YWluZXJpbnNpZ2h0cy9lY3MtZGVtb3dvcmtzaG9wL3BlcmZvcm1hbmNlL2F3cy9lY3MvY29udGFpbmVyaW5zaWdodHMvZWNzLXdvcmtzaG9wLWRldi9wZXJmb3JtYW5jZS9hd3MvZWtzL2RldjMwMy13b3Jrc2hvcC9jbHVzdGVyL2F3cy9ldmVudHMvY2xvdWR0cmFpbC9hd3MvZXZlbnRzL2Vjcy9hd3MvbGFtYmRhL2N3c3luLW15Y2FuYXJ5LWZhYzk3ZGVkLWYxMzQtNDk5YS05ZDcxLTRjM2JlMWY2MzE4Mi9hd3MvbGFtYmRhL2N3c3luLXdhdGNoLWxpbmtjaGVja3MtZWY3ZWYyNzMtNWRhMi00NjYzLWFmNTQtZDJmNTJkNTViMDYwL2Vjcy9lY3MtY3dhZ2VudC1kYWVtb24tc2VydmljZS9lY3MvZWNzLWRlbW8tbGltaXRUYXNrQ2xvdWRUcmFpbC9EZWZhdWx0TG9nR3JvdXBjb250YWluZXItaW5zaWdodHMtcHJvbWV0aGV1cy1iZXRhY29udGFpbmVyLWluc2lnaHRzLXByb21ldGhldXMtZGVtbwAAEAAAAAwAFAASAAwACAAEAAwAAAAQAAAALAAAADwAAAAAAAMAAQAAACgBAAAAAAAAoAAAAAAAAABgBgAAAAAAAAAAAAAAAAAAAAAAAAAACgAMAAAACAAEAAoAAAAIAAAAWAAAAAIAAAAoAAAABAAAAHz///8IAAAADAAAAAAAAAAAAAAABQAAAHJlZklkAAAAnP///wgAAAAUAAAACQAAAGxvZ0dyb3VwcwAAAAQAAABuYW1lAAAAAAEAAAAYAAAAAAASABgAFAATABIADAAAAAgABAASAAAAFAAAAEwAAABQAAAAAAAFAUwAAAABAAAADAAAAAgADAAIAAQACAAAAAgAAAAYAAAADAAAAGxvZ0dyb3VwTmFtZQAAAAAEAAAAbmFtZQAAAAAAAAAABAAEAAQAAAAMAAAAbG9nR3JvdXBOYW1lAAAAAEgBAABBUlJPVzE=',
|
||||
],
|
||||
refId: 'A',
|
||||
},
|
||||
},
|
||||
};
|
||||
const { ds } = getTestContext({ response });
|
||||
const expectedLogGroups = [
|
||||
'/aws/containerinsights/dev303-workshop/application',
|
||||
'/aws/containerinsights/dev303-workshop/dataplane',
|
||||
@ -124,6 +121,9 @@ describe('CloudWatchDatasource', () => {
|
||||
'container-insights-prometheus-beta',
|
||||
'container-insights-prometheus-demo',
|
||||
];
|
||||
|
||||
const logGroups = await ds.describeLogGroups({});
|
||||
|
||||
expect(logGroups).toEqual(expectedLogGroups);
|
||||
});
|
||||
});
|
||||
@ -134,6 +134,7 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
|
||||
it('should add data links to response', () => {
|
||||
const { ds } = getTestContext();
|
||||
const mockResponse: DataQueryResponse = {
|
||||
data: [
|
||||
{
|
||||
@ -149,7 +150,7 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const mockOptions = {
|
||||
const mockOptions: any = {
|
||||
targets: [
|
||||
{
|
||||
refId: 'A',
|
||||
@ -160,7 +161,7 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
const saturatedResponse = ctx.ds.addDataLinksToLogsResponse(mockResponse, mockOptions);
|
||||
const saturatedResponse = ds['addDataLinksToLogsResponse'](mockResponse, mockOptions);
|
||||
expect(saturatedResponse).toMatchObject({
|
||||
data: [
|
||||
{
|
||||
@ -185,6 +186,7 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
|
||||
it('should stop querying when no more data received a number of times in a row', async () => {
|
||||
const { ds } = getTestContext();
|
||||
const fakeFrames = genMockFrames(20);
|
||||
const initialRecordsMatched = fakeFrames[0].meta!.stats!.find(stat => stat.displayName === 'Records scanned')!
|
||||
.value!;
|
||||
@ -209,7 +211,7 @@ describe('CloudWatchDatasource', () => {
|
||||
}
|
||||
|
||||
let i = 0;
|
||||
jest.spyOn(ctx.ds, 'makeLogActionRequest').mockImplementation((subtype: LogAction) => {
|
||||
jest.spyOn(ds, 'makeLogActionRequest').mockImplementation((subtype: LogAction) => {
|
||||
if (subtype === 'GetQueryResults') {
|
||||
const mockObservable = of([fakeFrames[i]]);
|
||||
i++;
|
||||
@ -219,7 +221,7 @@ describe('CloudWatchDatasource', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const myResponse = await ctx.ds.logsQuery([{ queryId: 'fake-query-id', region: 'default' }]).toPromise();
|
||||
const myResponse = await ds.logsQuery([{ queryId: 'fake-query-id', region: 'default', refId: 'A' }]).toPromise();
|
||||
|
||||
const expectedData = [
|
||||
{
|
||||
@ -246,10 +248,11 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
|
||||
it('should continue querying as long as new data is being received', async () => {
|
||||
const { ds } = getTestContext();
|
||||
const fakeFrames = genMockFrames(15);
|
||||
|
||||
let i = 0;
|
||||
jest.spyOn(ctx.ds, 'makeLogActionRequest').mockImplementation((subtype: LogAction) => {
|
||||
jest.spyOn(ds, 'makeLogActionRequest').mockImplementation((subtype: LogAction) => {
|
||||
if (subtype === 'GetQueryResults') {
|
||||
const mockObservable = of([fakeFrames[i]]);
|
||||
i++;
|
||||
@ -259,7 +262,7 @@ describe('CloudWatchDatasource', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const myResponse = await ctx.ds.logsQuery([{ queryId: 'fake-query-id', region: 'default' }]).toPromise();
|
||||
const myResponse = await ds.logsQuery([{ queryId: 'fake-query-id', region: 'default', refId: 'A' }]).toPromise();
|
||||
expect(myResponse).toEqual({
|
||||
data: [fakeFrames[fakeFrames.length - 1]],
|
||||
key: 'test-key',
|
||||
@ -269,9 +272,10 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
|
||||
it('should stop querying when results come back with status "Complete"', async () => {
|
||||
const { ds } = getTestContext();
|
||||
const fakeFrames = genMockFrames(3);
|
||||
let i = 0;
|
||||
jest.spyOn(ctx.ds, 'makeLogActionRequest').mockImplementation((subtype: LogAction) => {
|
||||
jest.spyOn(ds, 'makeLogActionRequest').mockImplementation((subtype: LogAction) => {
|
||||
if (subtype === 'GetQueryResults') {
|
||||
const mockObservable = of([fakeFrames[i]]);
|
||||
i++;
|
||||
@ -281,7 +285,7 @@ describe('CloudWatchDatasource', () => {
|
||||
}
|
||||
});
|
||||
|
||||
const myResponse = await ctx.ds.logsQuery([{ queryId: 'fake-query-id', region: 'default' }]).toPromise();
|
||||
const myResponse = await ds.logsQuery([{ queryId: 'fake-query-id', region: 'default', refId: 'A' }]).toPromise();
|
||||
|
||||
expect(myResponse).toEqual({
|
||||
data: [fakeFrames[2]],
|
||||
@ -292,8 +296,9 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
|
||||
it('should call the replace method on provided log groups', () => {
|
||||
const replaceSpy = jest.spyOn(ctx.ds, 'replace').mockImplementation((target: string) => target);
|
||||
ctx.ds.makeLogActionRequest('StartQuery', [
|
||||
const { ds } = getTestContext();
|
||||
const replaceSpy = jest.spyOn(ds, 'replace').mockImplementation((target: string) => target);
|
||||
ds.makeLogActionRequest('StartQuery', [
|
||||
{
|
||||
queryString: 'test query string',
|
||||
region: 'default',
|
||||
@ -308,7 +313,7 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
|
||||
describe('When performing CloudWatch metrics query', () => {
|
||||
const query = {
|
||||
const query: any = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
targets: [
|
||||
@ -353,28 +358,29 @@ describe('CloudWatchDatasource', () => {
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
datasourceRequestMock.mockImplementation(() => {
|
||||
return Promise.resolve({ data: response });
|
||||
it('should generate the correct query', done => {
|
||||
const { ds, fetchMock } = getTestContext({ response });
|
||||
|
||||
observableTester().subscribeAndExpectOnComplete({
|
||||
observable: ds.query(query),
|
||||
expect: () => {
|
||||
expect(fetchMock.mock.calls[0][0].data.queries).toMatchObject(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
namespace: query.targets[0].namespace,
|
||||
metricName: query.targets[0].metricName,
|
||||
dimensions: { InstanceId: ['i-12345678'] },
|
||||
statistics: query.targets[0].statistics,
|
||||
period: query.targets[0].period,
|
||||
}),
|
||||
])
|
||||
);
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate the correct query', async () => {
|
||||
await ctx.ds.query(query).toPromise();
|
||||
expect(datasourceRequestMock.mock.calls[0][0].data.queries).toMatchObject(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
namespace: query.targets[0].namespace,
|
||||
metricName: query.targets[0].metricName,
|
||||
dimensions: { InstanceId: ['i-12345678'] },
|
||||
statistics: query.targets[0].statistics,
|
||||
period: query.targets[0].period,
|
||||
}),
|
||||
])
|
||||
);
|
||||
});
|
||||
|
||||
it('should generate the correct query with interval variable', async () => {
|
||||
it('should generate the correct query with interval variable', done => {
|
||||
const period: CustomVariableModel = {
|
||||
...initialVariableModelState,
|
||||
id: 'period',
|
||||
@ -388,9 +394,10 @@ describe('CloudWatchDatasource', () => {
|
||||
hide: VariableHide.dontHide,
|
||||
type: 'custom',
|
||||
};
|
||||
const templateSrv = new TemplateSrv();
|
||||
templateSrv.init([period]);
|
||||
|
||||
const query = {
|
||||
const query: any = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
targets: [
|
||||
@ -409,11 +416,19 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
await ctx.ds.query(query).toPromise();
|
||||
expect(datasourceRequestMock.mock.calls[0][0].data.queries[0].period).toEqual('600');
|
||||
const { ds, fetchMock } = getTestContext({ response, templateSrv });
|
||||
|
||||
observableTester().subscribeAndExpectOnComplete({
|
||||
observable: ds.query(query),
|
||||
expect: () => {
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].period).toEqual('600');
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
it.each(['pNN.NN', 'p9', 'p99.', 'p99.999'])('should cancel query for invalid extended statistics (%s)', stat => {
|
||||
const { ds } = getTestContext({ response });
|
||||
const query = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
@ -432,85 +447,94 @@ describe('CloudWatchDatasource', () => {
|
||||
},
|
||||
],
|
||||
};
|
||||
expect(ctx.ds.query.bind(ctx.ds, query)).toThrow(/Invalid extended statistics/);
|
||||
|
||||
expect(ds.query.bind(ds, query)).toThrow(/Invalid extended statistics/);
|
||||
});
|
||||
|
||||
it('should return series list', done => {
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
const { ds } = getTestContext({ response });
|
||||
|
||||
observableTester().subscribeAndExpectOnNext({
|
||||
observable: ds.query(query),
|
||||
expect: result => {
|
||||
expect(getFrameDisplayName(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].values.buffer[0]).toBe(response.results.A.series[0].points[0][0]);
|
||||
done();
|
||||
});
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
describe('a correct cloudwatch url should be built for each time series in the response', () => {
|
||||
beforeEach(() => {
|
||||
datasourceRequestMock.mockImplementation(() => {
|
||||
return Promise.resolve({ data: response });
|
||||
});
|
||||
});
|
||||
|
||||
it('should be built correctly if theres one search expressions returned in meta for a given query row', done => {
|
||||
const { ds } = getTestContext({ response });
|
||||
|
||||
response.results['A'].meta.gmdMeta = [{ Expression: `REMOVE_EMPTY(SEARCH('some expression'))`, Period: '300' }];
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
|
||||
observableTester().subscribeAndExpectOnNext({
|
||||
observable: ds.query(query),
|
||||
expect: result => {
|
||||
expect(getFrameDisplayName(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].config.links[0].title).toBe('View in CloudWatch console');
|
||||
expect(decodeURIComponent(result.data[0].fields[1].config.links[0].url)).toContain(
|
||||
`region=us-east-1#metricsV2:graph={"view":"timeSeries","stacked":false,"title":"A","start":"2016-12-31T15:00:00.000Z","end":"2016-12-31T16:00:00.000Z","region":"us-east-1","metrics":[{"expression":"REMOVE_EMPTY(SEARCH(\'some expression\'))"}]}`
|
||||
);
|
||||
done();
|
||||
});
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
it('should be built correctly if theres two search expressions returned in meta for a given query row', done => {
|
||||
const { ds } = getTestContext({ response });
|
||||
|
||||
response.results['A'].meta.gmdMeta = [
|
||||
{ Expression: `REMOVE_EMPTY(SEARCH('first expression'))` },
|
||||
{ Expression: `REMOVE_EMPTY(SEARCH('second expression'))` },
|
||||
];
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
|
||||
observableTester().subscribeAndExpectOnNext({
|
||||
observable: ds.query(query),
|
||||
expect: result => {
|
||||
expect(getFrameDisplayName(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].config.links[0].title).toBe('View in CloudWatch console');
|
||||
expect(decodeURIComponent(result.data[0].fields[0].config.links[0].url)).toContain(
|
||||
`region=us-east-1#metricsV2:graph={"view":"timeSeries","stacked":false,"title":"A","start":"2016-12-31T15:00:00.000Z","end":"2016-12-31T16:00:00.000Z","region":"us-east-1","metrics":[{"expression":"REMOVE_EMPTY(SEARCH(\'first expression\'))"},{"expression":"REMOVE_EMPTY(SEARCH(\'second expression\'))"}]}`
|
||||
);
|
||||
done();
|
||||
});
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
it('should be built correctly if the query is a metric stat query', done => {
|
||||
const { ds } = getTestContext({ response });
|
||||
|
||||
response.results['A'].meta.gmdMeta = [{ Period: '300' }];
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
|
||||
observableTester().subscribeAndExpectOnNext({
|
||||
observable: ds.query(query),
|
||||
expect: result => {
|
||||
expect(getFrameDisplayName(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].config.links[0].title).toBe('View in CloudWatch console');
|
||||
expect(decodeURIComponent(result.data[0].fields[0].config.links[0].url)).toContain(
|
||||
`region=us-east-1#metricsV2:graph={\"view\":\"timeSeries\",\"stacked\":false,\"title\":\"A\",\"start\":\"2016-12-31T15:00:00.000Z\",\"end\":\"2016-12-31T16:00:00.000Z\",\"region\":\"us-east-1\",\"metrics\":[[\"AWS/EC2\",\"CPUUtilization\",\"InstanceId\",\"i-12345678\",{\"stat\":\"Average\",\"period\":\"300\"}]]}`
|
||||
);
|
||||
done();
|
||||
});
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be added at all if query is a math expression', done => {
|
||||
const { ds } = getTestContext({ response });
|
||||
|
||||
query.targets[0].expression = 'a * 2';
|
||||
response.results['A'].meta.searchExpressions = [];
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
|
||||
observableTester().subscribeAndExpectOnNext({
|
||||
observable: ds.query(query),
|
||||
expect: result => {
|
||||
expect(result.data[0].fields[1].config.links).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -527,7 +551,7 @@ describe('CloudWatchDatasource', () => {
|
||||
expression: '',
|
||||
};
|
||||
|
||||
const query = {
|
||||
const query: any = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
targets: [
|
||||
@ -576,55 +600,50 @@ describe('CloudWatchDatasource', () => {
|
||||
redux.setStore({
|
||||
dispatch: jest.fn(),
|
||||
} as any);
|
||||
|
||||
datasourceRequestMock.mockImplementation(() => {
|
||||
return Promise.reject(backendErrorResponse);
|
||||
});
|
||||
});
|
||||
|
||||
it('should display one alert error message per region+datasource combination', done => {
|
||||
const memoizedDebounceSpy = jest.spyOn(ctx.ds, 'debouncedAlert');
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.catch(() => {
|
||||
const { ds } = getTestContext({ response: backendErrorResponse, throws: true });
|
||||
const memoizedDebounceSpy = jest.spyOn(ds, 'debouncedAlert');
|
||||
|
||||
observableTester().subscribeAndExpectOnError({
|
||||
observable: ds.query(query),
|
||||
expect: err => {
|
||||
expect(memoizedDebounceSpy).toHaveBeenCalledWith('TestDatasource', 'us-east-1');
|
||||
expect(memoizedDebounceSpy).toHaveBeenCalledWith('TestDatasource', 'us-east-2');
|
||||
expect(memoizedDebounceSpy).toHaveBeenCalledWith('TestDatasource', 'eu-north-1');
|
||||
expect(memoizedDebounceSpy).toBeCalledTimes(3);
|
||||
done();
|
||||
});
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('when regions query is used', () => {
|
||||
beforeEach(() => {
|
||||
datasourceRequestMock.mockImplementation(() => {
|
||||
return Promise.resolve({});
|
||||
});
|
||||
ctx.ds = new CloudWatchDatasource(instanceSettings, templateSrv, timeSrv);
|
||||
ctx.ds.doMetricQueryRequest = jest.fn(() => []);
|
||||
});
|
||||
describe('and region param is left out', () => {
|
||||
it('should use the default region', done => {
|
||||
ctx.ds.metricFindQuery('metrics(testNamespace)').then(() => {
|
||||
expect(ctx.ds.doMetricQueryRequest).toHaveBeenCalledWith('metrics', {
|
||||
namespace: 'testNamespace',
|
||||
region: instanceSettings.jsonData.defaultRegion,
|
||||
});
|
||||
done();
|
||||
it('should use the default region', async () => {
|
||||
const { ds, instanceSettings } = getTestContext();
|
||||
ds.doMetricQueryRequest = jest.fn().mockResolvedValue([]);
|
||||
|
||||
await ds.metricFindQuery('metrics(testNamespace)');
|
||||
|
||||
expect(ds.doMetricQueryRequest).toHaveBeenCalledWith('metrics', {
|
||||
namespace: 'testNamespace',
|
||||
region: instanceSettings.jsonData.defaultRegion,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('and region param is defined by user', () => {
|
||||
it('should use the user defined region', done => {
|
||||
ctx.ds.metricFindQuery('metrics(testNamespace2, custom-region)').then(() => {
|
||||
expect(ctx.ds.doMetricQueryRequest).toHaveBeenCalledWith('metrics', {
|
||||
namespace: 'testNamespace2',
|
||||
region: 'custom-region',
|
||||
});
|
||||
done();
|
||||
it('should use the user defined region', async () => {
|
||||
const { ds } = getTestContext();
|
||||
ds.doMetricQueryRequest = jest.fn().mockResolvedValue([]);
|
||||
|
||||
await ds.metricFindQuery('metrics(testNamespace2, custom-region)');
|
||||
|
||||
expect(ds.doMetricQueryRequest).toHaveBeenCalledWith('metrics', {
|
||||
namespace: 'testNamespace2',
|
||||
region: 'custom-region',
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -633,27 +652,25 @@ describe('CloudWatchDatasource', () => {
|
||||
|
||||
describe('When query region is "default"', () => {
|
||||
it('should return the datasource region if empty or "default"', () => {
|
||||
const { ds, instanceSettings } = getTestContext();
|
||||
const defaultRegion = instanceSettings.jsonData.defaultRegion;
|
||||
|
||||
expect(ctx.ds.getActualRegion()).toBe(defaultRegion);
|
||||
expect(ctx.ds.getActualRegion('')).toBe(defaultRegion);
|
||||
expect(ctx.ds.getActualRegion('default')).toBe(defaultRegion);
|
||||
expect(ds.getActualRegion()).toBe(defaultRegion);
|
||||
expect(ds.getActualRegion('')).toBe(defaultRegion);
|
||||
expect(ds.getActualRegion('default')).toBe(defaultRegion);
|
||||
});
|
||||
|
||||
it('should return the specified region if specified', () => {
|
||||
expect(ctx.ds.getActualRegion('some-fake-region-1')).toBe('some-fake-region-1');
|
||||
});
|
||||
const { ds } = getTestContext();
|
||||
|
||||
let requestParams: { queries: CloudWatchQuery[] };
|
||||
beforeEach(() => {
|
||||
ctx.ds.performTimeSeriesQuery = jest.fn(request => {
|
||||
requestParams = request;
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
expect(ds.getActualRegion('some-fake-region-1')).toBe('some-fake-region-1');
|
||||
});
|
||||
|
||||
it('should query for the datasource region if empty or "default"', done => {
|
||||
const query = {
|
||||
const { ds, instanceSettings } = getTestContext();
|
||||
const performTimeSeriesQueryMock = jest.spyOn(ds, 'performTimeSeriesQuery').mockReturnValue(of({}));
|
||||
|
||||
const query: any = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
targets: [
|
||||
@ -672,36 +689,29 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
expect(requestParams.queries[0].region).toBe(instanceSettings.jsonData.defaultRegion);
|
||||
done();
|
||||
});
|
||||
observableTester().subscribeAndExpectOnComplete({
|
||||
observable: ds.query(query),
|
||||
expect: () => {
|
||||
expect(performTimeSeriesQueryMock.mock.calls[0][0].queries[0].region).toBe(
|
||||
instanceSettings.jsonData.defaultRegion
|
||||
);
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When interpolating variables', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
ctx.mockedTemplateSrv = {
|
||||
replace: jest.fn(),
|
||||
};
|
||||
|
||||
ctx.ds = new CloudWatchDatasource(
|
||||
instanceSettings,
|
||||
ctx.mockedTemplateSrv,
|
||||
(new TimeSrvStub() as unknown) as TimeSrv
|
||||
);
|
||||
});
|
||||
|
||||
it('should return an empty array if no queries are provided', () => {
|
||||
expect(ctx.ds.interpolateVariablesInQueries([], {})).toHaveLength(0);
|
||||
const templateSrv: any = { replace: jest.fn() };
|
||||
const { ds } = getTestContext({ templateSrv });
|
||||
|
||||
expect(ds.interpolateVariablesInQueries([], {})).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should replace correct variables in CloudWatchLogsQuery', () => {
|
||||
const templateSrv: any = { replace: jest.fn() };
|
||||
const { ds } = getTestContext({ templateSrv });
|
||||
const variableName = 'someVar';
|
||||
const logQuery: CloudWatchLogsQuery = {
|
||||
id: 'someId',
|
||||
@ -711,14 +721,16 @@ describe('CloudWatchDatasource', () => {
|
||||
region: `$${variableName}`,
|
||||
};
|
||||
|
||||
ctx.ds.interpolateVariablesInQueries([logQuery], {});
|
||||
ds.interpolateVariablesInQueries([logQuery], {});
|
||||
|
||||
// We interpolate `expression` and `region` in CloudWatchLogsQuery
|
||||
expect(ctx.mockedTemplateSrv.replace).toHaveBeenCalledWith(`$${variableName}`, {});
|
||||
expect(ctx.mockedTemplateSrv.replace).toHaveBeenCalledTimes(2);
|
||||
expect(templateSrv.replace).toHaveBeenCalledWith(`$${variableName}`, {});
|
||||
expect(templateSrv.replace).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('should replace correct variables in CloudWatchMetricsQuery', () => {
|
||||
const templateSrv: any = { replace: jest.fn() };
|
||||
const { ds } = getTestContext({ templateSrv });
|
||||
const variableName = 'someVar';
|
||||
const logQuery: CloudWatchMetricsQuery = {
|
||||
id: 'someId',
|
||||
@ -737,16 +749,16 @@ describe('CloudWatchDatasource', () => {
|
||||
statistics: [],
|
||||
};
|
||||
|
||||
ctx.ds.interpolateVariablesInQueries([logQuery], {});
|
||||
ds.interpolateVariablesInQueries([logQuery], {});
|
||||
|
||||
// We interpolate `expression`, `region`, `period`, `alias`, `metricName`, `nameSpace` and `dimensions` in CloudWatchMetricsQuery
|
||||
expect(ctx.mockedTemplateSrv.replace).toHaveBeenCalledWith(`$${variableName}`, {});
|
||||
expect(ctx.mockedTemplateSrv.replace).toHaveBeenCalledTimes(8);
|
||||
expect(templateSrv.replace).toHaveBeenCalledWith(`$${variableName}`, {});
|
||||
expect(templateSrv.replace).toHaveBeenCalledTimes(8);
|
||||
});
|
||||
});
|
||||
|
||||
describe('When performing CloudWatch query for extended statistics', () => {
|
||||
const query = {
|
||||
const query: any = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
targets: [
|
||||
@ -797,26 +809,22 @@ describe('CloudWatchDatasource', () => {
|
||||
},
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
datasourceRequestMock.mockImplementation(params => {
|
||||
return Promise.resolve({ data: response });
|
||||
});
|
||||
});
|
||||
|
||||
it('should return series list', done => {
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
const { ds } = getTestContext({ response });
|
||||
|
||||
observableTester().subscribeAndExpectOnNext({
|
||||
observable: ds.query(query),
|
||||
expect: result => {
|
||||
expect(getFrameDisplayName(result.data[0])).toBe(response.results.A.series[0].name);
|
||||
expect(result.data[0].fields[1].values.buffer[0]).toBe(response.results.A.series[0].points[0][0]);
|
||||
done();
|
||||
});
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('When performing CloudWatch query with template variables', () => {
|
||||
let requestParams: { queries: CloudWatchMetricsQuery[] };
|
||||
let templateSrv: TemplateSrv;
|
||||
beforeEach(() => {
|
||||
const var1: CustomVariableModel = {
|
||||
...initialVariableModelState,
|
||||
@ -880,18 +888,13 @@ describe('CloudWatchDatasource', () => {
|
||||
};
|
||||
const variables = [var1, var2, var3, var4];
|
||||
const state = convertToStoreState(variables);
|
||||
const _templateSrv = new TemplateSrv(getTemplateSrvDependencies(state));
|
||||
_templateSrv.init(variables);
|
||||
ctx.ds = new CloudWatchDatasource(instanceSettings, _templateSrv, timeSrv);
|
||||
|
||||
datasourceRequestMock.mockImplementation(params => {
|
||||
requestParams = params.data;
|
||||
return Promise.resolve({ data: {} });
|
||||
});
|
||||
templateSrv = new TemplateSrv(getTemplateSrvDependencies(state));
|
||||
templateSrv.init(variables);
|
||||
});
|
||||
|
||||
it('should generate the correct query for single template variable', done => {
|
||||
const query = {
|
||||
const { ds, fetchMock } = getTestContext({ templateSrv });
|
||||
const query: any = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
targets: [
|
||||
@ -910,17 +913,18 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
expect(requestParams.queries[0].dimensions['dim2']).toStrictEqual(['var2-foo']);
|
||||
done();
|
||||
});
|
||||
observableTester().subscribeAndExpectOnComplete({
|
||||
observable: ds.query(query),
|
||||
expect: () => {
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim2']).toStrictEqual(['var2-foo']);
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate the correct query in the case of one multilple template variables', done => {
|
||||
const query = {
|
||||
const { ds, fetchMock } = getTestContext({ templateSrv });
|
||||
const query: any = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
targets: [
|
||||
@ -945,19 +949,20 @@ describe('CloudWatchDatasource', () => {
|
||||
},
|
||||
};
|
||||
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
expect(requestParams.queries[0].dimensions['dim1']).toStrictEqual(['var1-foo']);
|
||||
expect(requestParams.queries[0].dimensions['dim2']).toStrictEqual(['var2-foo']);
|
||||
expect(requestParams.queries[0].dimensions['dim3']).toStrictEqual(['var3-foo', 'var3-baz']);
|
||||
done();
|
||||
});
|
||||
observableTester().subscribeAndExpectOnComplete({
|
||||
observable: ds.query(query),
|
||||
expect: () => {
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim1']).toStrictEqual(['var1-foo']);
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim2']).toStrictEqual(['var2-foo']);
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim3']).toStrictEqual(['var3-foo', 'var3-baz']);
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate the correct query in the case of multilple multi template variables', done => {
|
||||
const query = {
|
||||
const { ds, fetchMock } = getTestContext({ templateSrv });
|
||||
const query: any = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
targets: [
|
||||
@ -978,19 +983,20 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
expect(requestParams.queries[0].dimensions['dim1']).toStrictEqual(['var1-foo']);
|
||||
expect(requestParams.queries[0].dimensions['dim3']).toStrictEqual(['var3-foo', 'var3-baz']);
|
||||
expect(requestParams.queries[0].dimensions['dim4']).toStrictEqual(['var4-foo', 'var4-baz']);
|
||||
done();
|
||||
});
|
||||
observableTester().subscribeAndExpectOnComplete({
|
||||
observable: ds.query(query),
|
||||
expect: () => {
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim1']).toStrictEqual(['var1-foo']);
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim3']).toStrictEqual(['var3-foo', 'var3-baz']);
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim4']).toStrictEqual(['var4-foo', 'var4-baz']);
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate the correct query for multilple template variables, lack scopedVars', done => {
|
||||
const query = {
|
||||
const { ds, fetchMock } = getTestContext({ templateSrv });
|
||||
const query: any = {
|
||||
range: defaultTimeRange,
|
||||
rangeRaw: { from: 1483228800, to: 1483232400 },
|
||||
targets: [
|
||||
@ -1014,15 +1020,15 @@ describe('CloudWatchDatasource', () => {
|
||||
},
|
||||
};
|
||||
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
expect(requestParams.queries[0].dimensions['dim1']).toStrictEqual(['var1-foo']);
|
||||
expect(requestParams.queries[0].dimensions['dim2']).toStrictEqual(['var2-foo']);
|
||||
expect(requestParams.queries[0].dimensions['dim3']).toStrictEqual(['var3-foo', 'var3-baz']);
|
||||
done();
|
||||
});
|
||||
observableTester().subscribeAndExpectOnComplete({
|
||||
observable: ds.query(query),
|
||||
expect: () => {
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim1']).toStrictEqual(['var1-foo']);
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim2']).toStrictEqual(['var2-foo']);
|
||||
expect(fetchMock.mock.calls[0][0].data.queries[0].dimensions['dim3']).toStrictEqual(['var3-foo', 'var3-baz']);
|
||||
},
|
||||
done,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -1032,11 +1038,8 @@ describe('CloudWatchDatasource', () => {
|
||||
scenario.setup = async (setupCallback: any) => {
|
||||
beforeEach(async () => {
|
||||
await setupCallback();
|
||||
datasourceRequestMock.mockImplementation(args => {
|
||||
scenario.request = args.data;
|
||||
return Promise.resolve({ data: scenario.requestResponse });
|
||||
});
|
||||
ctx.ds.metricFindQuery(query).then((args: any) => {
|
||||
const { ds } = getTestContext({ response: scenario.requestResponse });
|
||||
ds.metricFindQuery(query).then((args: any) => {
|
||||
scenario.result = args;
|
||||
});
|
||||
});
|
||||
@ -1210,3 +1213,17 @@ function genMockFrames(numResponses: number): DataFrame[] {
|
||||
|
||||
return mockFrames;
|
||||
}
|
||||
|
||||
function createFetchResponse<T>(data: T): FetchResponse<T> {
|
||||
return {
|
||||
data,
|
||||
status: 200,
|
||||
url: 'http://localhost:3000/api/query',
|
||||
config: { url: 'http://localhost:3000/api/query' },
|
||||
type: 'basic',
|
||||
statusText: 'Ok',
|
||||
redirected: false,
|
||||
headers: ({} as unknown) as Headers,
|
||||
ok: true,
|
||||
};
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user