mirror of
https://github.com/grafana/grafana.git
synced 2025-02-09 23:16:16 -06:00
Datasource/CloudWatch: More robust handling of different query modes (#25691)
* Datasource/CloudWatch: More robust handling of different query modes A small refactor which changes how the CloudWatch datasource handles multiple queries with different query modes. Groundwork for future Logs/Metrics unification work.
This commit is contained in:
parent
ddcc0c3c80
commit
2ac1bfcc79
@ -46,9 +46,9 @@ export default class CloudWatchLink extends Component<Props, State> {
|
||||
start,
|
||||
timeType: 'ABSOLUTE',
|
||||
tz: 'UTC',
|
||||
editorString: query.expression,
|
||||
editorString: query.expression ?? '',
|
||||
isLiveTail: false,
|
||||
source: query.logGroupNames,
|
||||
source: query.logGroupNames ?? [],
|
||||
};
|
||||
|
||||
return encodeUrl(urlProps, datasource.getActualRegion(query.region));
|
||||
|
@ -220,18 +220,12 @@ export default class LogsCheatSheet extends PureComponent<ExploreStartPageProps,
|
||||
switchToMetrics = (query: CloudWatchLogsQuery) => {
|
||||
const { onClickExample, exploreId } = this.props;
|
||||
|
||||
const nextQuery: CloudWatchLogsQuery = {
|
||||
...(query as CloudWatchLogsQuery),
|
||||
apiMode: 'Logs',
|
||||
queryMode: 'Logs',
|
||||
};
|
||||
|
||||
dispatch(changeModeAction({ exploreId, mode: ExploreMode.Metrics }));
|
||||
onClickExample(nextQuery);
|
||||
onClickExample(query);
|
||||
};
|
||||
|
||||
onClickExample(query: CloudWatchLogsQuery) {
|
||||
if (query.expression.includes('stats')) {
|
||||
if (query.expression?.includes('stats')) {
|
||||
this.switchToMetrics(query);
|
||||
} else {
|
||||
this.props.onClickExample(query);
|
||||
@ -243,7 +237,9 @@ export default class LogsCheatSheet extends PureComponent<ExploreStartPageProps,
|
||||
<div
|
||||
className="cheat-sheet-item__example"
|
||||
key={expr}
|
||||
onClick={e => this.onClickExample({ refId: 'A', expression: expr } as CloudWatchLogsQuery)}
|
||||
onClick={e =>
|
||||
this.onClickExample({ refId: 'A', expression: expr, queryMode: 'Logs', region: 'default', id: 'A' })
|
||||
}
|
||||
>
|
||||
<pre>{renderHighlightedMarkup(expr, keyPrefix)}</pre>
|
||||
</div>
|
||||
|
@ -275,16 +275,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
};
|
||||
|
||||
switchToMetrics = () => {
|
||||
const { query, onChange, exploreId } = this.props;
|
||||
|
||||
if (onChange) {
|
||||
const nextQuery: CloudWatchLogsQuery = {
|
||||
...(query as CloudWatchLogsQuery),
|
||||
apiMode: 'Logs',
|
||||
};
|
||||
onChange(nextQuery);
|
||||
}
|
||||
|
||||
const { exploreId } = this.props;
|
||||
dispatch(changeModeAction({ exploreId, mode: ExploreMode.Metrics }));
|
||||
};
|
||||
|
||||
@ -409,7 +400,7 @@ export class CloudWatchLogsQueryField extends React.PureComponent<CloudWatchLogs
|
||||
<div className="gf-form gf-form--grow flex-shrink-1">
|
||||
<QueryField
|
||||
additionalPlugins={this.plugins}
|
||||
query={query.expression}
|
||||
query={query.expression ?? ''}
|
||||
onChange={this.onChangeQuery}
|
||||
onBlur={this.props.onBlur}
|
||||
onClick={this.onQueryFieldClick}
|
||||
|
@ -41,7 +41,6 @@ const setup = () => {
|
||||
const props: Props = {
|
||||
query: {
|
||||
queryMode: 'Metrics',
|
||||
apiMode: 'Metrics',
|
||||
refId: '',
|
||||
id: '',
|
||||
region: 'us-east-1',
|
||||
|
@ -1,15 +1,15 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { SelectableValue } from '@grafana/data';
|
||||
import { Segment, SegmentAsync } from '@grafana/ui';
|
||||
import { CloudWatchQuery, SelectableStrings, CloudWatchMetricsQuery } from '../types';
|
||||
import { SelectableStrings, CloudWatchMetricsQuery } from '../types';
|
||||
import { CloudWatchDatasource } from '../datasource';
|
||||
import { Stats, Dimensions, QueryInlineField } from '.';
|
||||
|
||||
export type Props = {
|
||||
query: CloudWatchQuery;
|
||||
query: CloudWatchMetricsQuery;
|
||||
datasource: CloudWatchDatasource;
|
||||
onRunQuery?: () => void;
|
||||
onChange: (value: CloudWatchQuery) => void;
|
||||
onChange: (value: CloudWatchMetricsQuery) => void;
|
||||
};
|
||||
|
||||
interface State {
|
||||
@ -66,7 +66,7 @@ export function MetricsQueryFieldsEditor({
|
||||
|
||||
const toOption = (value: any) => ({ label: value, value });
|
||||
|
||||
const onQueryChange = (query: CloudWatchQuery) => {
|
||||
const onQueryChange = (query: CloudWatchMetricsQuery) => {
|
||||
onChange(query);
|
||||
onRunQuery();
|
||||
};
|
||||
@ -98,7 +98,7 @@ export function MetricsQueryFieldsEditor({
|
||||
/>
|
||||
</QueryInlineField>
|
||||
|
||||
{query.expression.length === 0 && (
|
||||
{query.expression?.length === 0 && (
|
||||
<>
|
||||
<QueryInlineField label="Namespace">
|
||||
<Segment
|
||||
|
@ -1,4 +1,5 @@
|
||||
import React, { PureComponent } from 'react';
|
||||
import pick from 'lodash/pick';
|
||||
import { ExploreQueryFieldProps, ExploreMode } from '@grafana/data';
|
||||
import { Segment } from '@grafana/ui';
|
||||
import { CloudWatchQuery } from '../types';
|
||||
@ -17,7 +18,7 @@ const apiModes = {
|
||||
export class PanelQueryEditor extends PureComponent<Props> {
|
||||
render() {
|
||||
const { query } = this.props;
|
||||
const apiMode = query.apiMode ?? query.queryMode ?? 'Metrics';
|
||||
const apiMode = query.queryMode ?? 'Metrics';
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -25,9 +26,27 @@ export class PanelQueryEditor extends PureComponent<Props> {
|
||||
<Segment
|
||||
value={apiModes[apiMode]}
|
||||
options={Object.values(apiModes)}
|
||||
onChange={({ value }) =>
|
||||
this.props.onChange({ ...query, apiMode: (value as 'Metrics' | 'Logs') ?? 'Metrics' })
|
||||
}
|
||||
onChange={({ value }) => {
|
||||
const newMode = (value as 'Metrics' | 'Logs') ?? 'Metrics';
|
||||
if (newMode !== apiModes[apiMode].value) {
|
||||
const commonProps = pick(
|
||||
query,
|
||||
'id',
|
||||
'region',
|
||||
'namespace',
|
||||
'refId',
|
||||
'hide',
|
||||
'key',
|
||||
'queryType',
|
||||
'datasource'
|
||||
);
|
||||
|
||||
this.props.onChange({
|
||||
...commonProps,
|
||||
queryMode: newMode,
|
||||
} as CloudWatchQuery);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</QueryInlineField>
|
||||
{apiMode === ExploreMode.Logs ? (
|
||||
|
@ -7,26 +7,30 @@ describe('datasource', () => {
|
||||
describe('query', () => {
|
||||
it('should return error if log query and log groups is not specified', async () => {
|
||||
const { datasource } = setup();
|
||||
const response: DataQueryResponse = (await datasource.query({
|
||||
targets: [
|
||||
{
|
||||
queryMode: 'Logs' as 'Logs',
|
||||
},
|
||||
],
|
||||
} as any)) as any;
|
||||
const response: DataQueryResponse = (await datasource
|
||||
.query({
|
||||
targets: [
|
||||
{
|
||||
queryMode: 'Logs' as 'Logs',
|
||||
},
|
||||
],
|
||||
} as any)
|
||||
.toPromise()) as any;
|
||||
expect(response.error?.message).toBe('Log group is required');
|
||||
});
|
||||
|
||||
it('should return empty response if queries are hidden', async () => {
|
||||
const { datasource } = setup();
|
||||
const response: DataQueryResponse = (await datasource.query({
|
||||
targets: [
|
||||
{
|
||||
queryMode: 'Logs' as 'Logs',
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
} as any)) as any;
|
||||
const response: DataQueryResponse = (await datasource
|
||||
.query({
|
||||
targets: [
|
||||
{
|
||||
queryMode: 'Logs' as 'Logs',
|
||||
hide: true,
|
||||
},
|
||||
],
|
||||
} as any)
|
||||
.toPromise()) as any;
|
||||
expect(response.data).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
@ -39,7 +39,7 @@ import {
|
||||
MetricRequest,
|
||||
TSDBResponse,
|
||||
} from './types';
|
||||
import { empty, from, Observable } from 'rxjs';
|
||||
import { empty, from, Observable, of, merge } from 'rxjs';
|
||||
import { catchError, delay, expand, finalize, map, mergeMap, tap } from 'rxjs/operators';
|
||||
import { CloudWatchLanguageProvider } from './language_provider';
|
||||
|
||||
@ -101,55 +101,78 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
this.languageProvider = new CloudWatchLanguageProvider(this);
|
||||
}
|
||||
|
||||
query(options: DataQueryRequest<CloudWatchQuery>): Promise<DataQueryResponse> | Observable<DataQueryResponse> {
|
||||
query(options: DataQueryRequest<CloudWatchQuery>): Observable<DataQueryResponse> {
|
||||
options = angular.copy(options);
|
||||
|
||||
const firstTarget = options.targets[0];
|
||||
|
||||
let queries = options.targets.filter(item => item.id !== '' || item.hide !== true);
|
||||
const { logQueries, metricsQueries } = this.getTargetsByQueryMode(queries);
|
||||
|
||||
if (firstTarget.queryMode === 'Logs') {
|
||||
const logQueries: CloudWatchLogsQuery[] = queries.filter(item => item.queryMode === 'Logs') as any;
|
||||
|
||||
const validLogQueries = logQueries.filter(item => item.logGroupNames?.length);
|
||||
if (logQueries.length > validLogQueries.length) {
|
||||
return Promise.resolve({ data: [], error: { message: 'Log group is required' } });
|
||||
}
|
||||
|
||||
// No valid targets, return the empty result to save a round trip.
|
||||
if (_.isEmpty(validLogQueries)) {
|
||||
return Promise.resolve({ data: [] });
|
||||
}
|
||||
|
||||
const queryParams = validLogQueries.map((target: CloudWatchLogsQuery) => ({
|
||||
queryString: target.expression,
|
||||
refId: target.refId,
|
||||
logGroupNames: target.logGroupNames,
|
||||
region: this.replace(this.getActualRegion(target.region), options.scopedVars, true, 'region'),
|
||||
}));
|
||||
|
||||
return this.makeLogActionRequest('StartQuery', queryParams, options.scopedVars).pipe(
|
||||
mergeMap(dataFrames =>
|
||||
this.logsQuery(
|
||||
dataFrames.map(dataFrame => ({
|
||||
queryId: dataFrame.fields[0].values.get(0),
|
||||
region: dataFrame.meta?.custom?.['Region'] ?? 'default',
|
||||
refId: dataFrame.refId!,
|
||||
statsGroups: (options.targets.find(target => target.refId === dataFrame.refId)! as CloudWatchLogsQuery)
|
||||
.statsGroups,
|
||||
}))
|
||||
)
|
||||
),
|
||||
map(response => this.addDataLinksToLogsResponse(response, options))
|
||||
);
|
||||
const dataQueryResponses: Array<Observable<DataQueryResponse>> = [];
|
||||
if (logQueries.length > 0) {
|
||||
dataQueryResponses.push(this.handleLogQueries(logQueries, options));
|
||||
}
|
||||
|
||||
const metricQueries: MetricQuery[] = options.targets
|
||||
if (metricsQueries.length > 0) {
|
||||
dataQueryResponses.push(this.handleMetricQueries(metricsQueries, options));
|
||||
}
|
||||
|
||||
// No valid targets, return the empty result to save a round trip.
|
||||
if (_.isEmpty(dataQueryResponses)) {
|
||||
return of({
|
||||
data: [],
|
||||
state: LoadingState.Done,
|
||||
});
|
||||
}
|
||||
|
||||
return merge(...dataQueryResponses);
|
||||
}
|
||||
|
||||
handleLogQueries = (
|
||||
logQueries: CloudWatchLogsQuery[],
|
||||
options: DataQueryRequest<CloudWatchQuery>
|
||||
): Observable<DataQueryResponse> => {
|
||||
const validLogQueries = logQueries.filter(item => item.logGroupNames?.length);
|
||||
if (logQueries.length > validLogQueries.length) {
|
||||
return of({ data: [], error: { message: 'Log group is required' } });
|
||||
}
|
||||
|
||||
// No valid targets, return the empty result to save a round trip.
|
||||
if (_.isEmpty(validLogQueries)) {
|
||||
return of({ data: [], state: LoadingState.Done });
|
||||
}
|
||||
|
||||
const queryParams = validLogQueries.map((target: CloudWatchLogsQuery) => ({
|
||||
queryString: target.expression,
|
||||
refId: target.refId,
|
||||
logGroupNames: target.logGroupNames,
|
||||
region: this.replace(this.getActualRegion(target.region), options.scopedVars, true, 'region'),
|
||||
}));
|
||||
|
||||
return this.makeLogActionRequest('StartQuery', queryParams, options.scopedVars).pipe(
|
||||
mergeMap(dataFrames =>
|
||||
this.logsQuery(
|
||||
dataFrames.map(dataFrame => ({
|
||||
queryId: dataFrame.fields[0].values.get(0),
|
||||
region: dataFrame.meta?.custom?.['Region'] ?? 'default',
|
||||
refId: dataFrame.refId!,
|
||||
statsGroups: (logQueries.find(target => target.refId === dataFrame.refId)! as CloudWatchLogsQuery)
|
||||
.statsGroups,
|
||||
}))
|
||||
)
|
||||
),
|
||||
map(response => this.addDataLinksToLogsResponse(response, options))
|
||||
);
|
||||
};
|
||||
|
||||
handleMetricQueries = (
|
||||
metricQueries: CloudWatchMetricsQuery[],
|
||||
options: DataQueryRequest<CloudWatchQuery>
|
||||
): Observable<DataQueryResponse> => {
|
||||
const validMetricsQueries = metricQueries
|
||||
.filter(
|
||||
item =>
|
||||
item.queryMode !== 'Logs' &&
|
||||
((!!item.region && !!item.namespace && !!item.metricName && !_.isEmpty(item.statistics)) ||
|
||||
item.expression?.length > 0)
|
||||
(!!item.region && !!item.namespace && !!item.metricName && !_.isEmpty(item.statistics)) ||
|
||||
item.expression?.length > 0
|
||||
)
|
||||
.map(
|
||||
(item: CloudWatchMetricsQuery): MetricQuery => {
|
||||
@ -187,18 +210,18 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
);
|
||||
|
||||
// No valid targets, return the empty result to save a round trip.
|
||||
if (_.isEmpty(metricQueries)) {
|
||||
return Promise.resolve({ data: [] });
|
||||
if (_.isEmpty(validMetricsQueries)) {
|
||||
return of({ data: [] });
|
||||
}
|
||||
|
||||
const request = {
|
||||
from: options?.range?.from.valueOf().toString(),
|
||||
to: options?.range?.to.valueOf().toString(),
|
||||
queries: metricQueries,
|
||||
queries: validMetricsQueries,
|
||||
};
|
||||
|
||||
return this.performTimeSeriesQuery(request, options.range);
|
||||
}
|
||||
return from(this.performTimeSeriesQuery(request, options.range));
|
||||
};
|
||||
|
||||
logsQuery(
|
||||
queryParams: Array<{ queryId: string; refId: string; limit?: number; region: string; statsGroups?: string[] }>
|
||||
@ -279,9 +302,9 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
start,
|
||||
timeType: 'ABSOLUTE',
|
||||
tz: 'UTC',
|
||||
editorString: curTarget.expression,
|
||||
editorString: curTarget.expression ?? '',
|
||||
isLiveTail: false,
|
||||
source: curTarget.logGroupNames,
|
||||
source: curTarget.logGroupNames ?? [],
|
||||
};
|
||||
|
||||
const encodedUrl = encodeUrl(
|
||||
@ -902,11 +925,30 @@ export class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery, CloudWa
|
||||
|
||||
getQueryDisplayText(query: CloudWatchQuery) {
|
||||
if (query.queryMode === 'Logs') {
|
||||
return query.expression;
|
||||
return query.expression ?? '';
|
||||
} else {
|
||||
return JSON.stringify(query);
|
||||
}
|
||||
}
|
||||
|
||||
getTargetsByQueryMode = (targets: CloudWatchQuery[]) => {
|
||||
const logQueries: CloudWatchLogsQuery[] = [];
|
||||
const metricsQueries: CloudWatchMetricsQuery[] = [];
|
||||
|
||||
targets.forEach(query => {
|
||||
const mode = query.queryMode ?? 'Metrics';
|
||||
if (mode === 'Logs') {
|
||||
logQueries.push(query as CloudWatchLogsQuery);
|
||||
} else {
|
||||
metricsQueries.push(query as CloudWatchMetricsQuery);
|
||||
}
|
||||
});
|
||||
|
||||
return {
|
||||
logQueries,
|
||||
metricsQueries,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
function withTeardown<T = any>(observable: Observable<T>, onUnsubscribe: () => void): Observable<T> {
|
||||
|
@ -314,7 +314,7 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
|
||||
it('should generate the correct query', async () => {
|
||||
await ctx.ds.query(query);
|
||||
await ctx.ds.query(query).toPromise();
|
||||
expect(datasourceRequestMock.mock.calls[0][0].data.queries).toMatchObject(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
@ -365,7 +365,7 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
await ctx.ds.query(query);
|
||||
await ctx.ds.query(query).toPromise();
|
||||
expect(datasourceRequestMock.mock.calls[0][0].data.queries[0].period).toEqual('600');
|
||||
});
|
||||
|
||||
@ -392,11 +392,14 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
|
||||
it('should return series list', done => {
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
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();
|
||||
});
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
describe('a correct cloudwatch url should be built for each time series in the response', () => {
|
||||
@ -408,14 +411,17 @@ describe('CloudWatchDatasource', () => {
|
||||
|
||||
it('should be built correctly if theres one search expressions returned in meta for a given query row', done => {
|
||||
response.results['A'].meta.gmdMeta = [{ Expression: `REMOVE_EMPTY(SEARCH('some expression'))`, Period: '300' }];
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
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();
|
||||
});
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be built correctly if theres two search expressions returned in meta for a given query row', done => {
|
||||
@ -423,35 +429,44 @@ describe('CloudWatchDatasource', () => {
|
||||
{ Expression: `REMOVE_EMPTY(SEARCH('first expression'))` },
|
||||
{ Expression: `REMOVE_EMPTY(SEARCH('second expression'))` },
|
||||
];
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
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();
|
||||
});
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('should be built correctly if the query is a metric stat query', done => {
|
||||
response.results['A'].meta.gmdMeta = [{ Period: '300' }];
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
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();
|
||||
});
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be added at all if query is a math expression', done => {
|
||||
query.targets[0].expression = 'a * 2';
|
||||
response.results['A'].meta.searchExpressions = [];
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
expect(result.data[0].fields[1].config.links).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
expect(result.data[0].fields[1].config.links).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -525,13 +540,16 @@ describe('CloudWatchDatasource', () => {
|
||||
|
||||
it('should display one alert error message per region+datasource combination', done => {
|
||||
const memoizedDebounceSpy = jest.spyOn(ctx.ds, 'debouncedAlert');
|
||||
ctx.ds.query(query).catch(() => {
|
||||
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();
|
||||
});
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.catch(() => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -610,10 +628,13 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
expect(requestParams.queries[0].region).toBe(instanceSettings.jsonData.defaultRegion);
|
||||
done();
|
||||
});
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
expect(requestParams.queries[0].region).toBe(instanceSettings.jsonData.defaultRegion);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -676,11 +697,14 @@ describe('CloudWatchDatasource', () => {
|
||||
});
|
||||
|
||||
it('should return series list', done => {
|
||||
ctx.ds.query(query).then((result: any) => {
|
||||
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();
|
||||
});
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then((result: any) => {
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@ -787,10 +811,13 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
ctx.ds.query(query).then(() => {
|
||||
expect(requestParams.queries[0].dimensions['dim2']).toStrictEqual(['var2-foo']);
|
||||
done();
|
||||
});
|
||||
ctx.ds
|
||||
.query(query)
|
||||
.toPromise()
|
||||
.then(() => {
|
||||
expect(requestParams.queries[0].dimensions['dim2']).toStrictEqual(['var2-foo']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate the correct query in the case of one multilple template variables', done => {
|
||||
@ -819,12 +846,15 @@ describe('CloudWatchDatasource', () => {
|
||||
},
|
||||
};
|
||||
|
||||
ctx.ds.query(query).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();
|
||||
});
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate the correct query in the case of multilple multi template variables', done => {
|
||||
@ -849,12 +879,15 @@ describe('CloudWatchDatasource', () => {
|
||||
],
|
||||
};
|
||||
|
||||
ctx.ds.query(query).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();
|
||||
});
|
||||
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();
|
||||
});
|
||||
});
|
||||
|
||||
it('should generate the correct query for multilple template variables, lack scopedVars', done => {
|
||||
@ -882,12 +915,15 @@ describe('CloudWatchDatasource', () => {
|
||||
},
|
||||
};
|
||||
|
||||
ctx.ds.query(query).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();
|
||||
});
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -3,8 +3,6 @@ import { DataQuery, SelectableValue, DataSourceJsonData } from '@grafana/data';
|
||||
export interface CloudWatchMetricsQuery extends DataQuery {
|
||||
queryMode: 'Metrics';
|
||||
|
||||
apiMode: 'Logs' | 'Metrics'; // TEMP: Remove when logs/metrics unification is done
|
||||
|
||||
id: string;
|
||||
region: string;
|
||||
namespace: string;
|
||||
@ -37,12 +35,10 @@ export enum CloudWatchLogsQueryStatus {
|
||||
export interface CloudWatchLogsQuery extends DataQuery {
|
||||
queryMode: 'Logs';
|
||||
|
||||
apiMode: 'Logs' | 'Metrics'; // TEMP: Remove when logs/metrics unification is done
|
||||
id: string;
|
||||
region: string;
|
||||
namespace: string;
|
||||
expression: string;
|
||||
logGroupNames: string[];
|
||||
expression?: string;
|
||||
logGroupNames?: string[];
|
||||
statsGroups?: string[];
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user