Commit 69691fbd by Erik Sundell Committed by GitHub

Add data link from panel to cloudwatch console (#20061)

* Add data link from panel to cloudwatch console

* Change conf variable name

* Fixes according to pr feedback

* Cleanup. Fix broken tests
parent a1e81579
import angular, { IQService } from 'angular'; import angular, { IQService } from 'angular';
import _ from 'lodash'; import _ from 'lodash';
import { dateMath, ScopedVars } from '@grafana/data'; import { dateMath, ScopedVars, toDataFrame, TimeRange } from '@grafana/data';
import kbn from 'app/core/utils/kbn'; import kbn from 'app/core/utils/kbn';
import { CloudWatchQuery } from './types'; import { CloudWatchQuery } from './types';
import { DataSourceApi, DataQueryRequest, DataSourceInstanceSettings } from '@grafana/ui'; import { DataSourceApi, DataQueryRequest, DataSourceInstanceSettings } from '@grafana/ui';
...@@ -92,7 +92,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery> ...@@ -92,7 +92,7 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
queries: queries, queries: queries,
}; };
return this.performTimeSeriesQuery(request); return this.performTimeSeriesQuery(request, options.range);
} }
getPeriod(target: any, options: any, now?: number) { getPeriod(target: any, options: any, now?: number) {
...@@ -141,26 +141,75 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery> ...@@ -141,26 +141,75 @@ export default class CloudWatchDatasource extends DataSourceApi<CloudWatchQuery>
return period; return period;
} }
performTimeSeriesQuery(request: any) { buildCloudwatchConsoleUrl(
{ region, namespace, metricName, dimensions, statistics, period }: CloudWatchQuery,
start: string,
end: string,
title: string
) {
const conf = {
view: 'timeSeries',
stacked: false,
title,
start,
end,
region,
metrics: [
...statistics.map(stat => [
namespace,
metricName,
...Object.entries(dimensions).reduce((acc, [key, value]) => [...acc, key, value], []),
{
stat,
period,
},
]),
],
};
return `https://${region}.console.aws.amazon.com/cloudwatch/deeplink.js?region=${region}#metricsV2:graph=${encodeURIComponent(
JSON.stringify(conf)
)}`;
}
performTimeSeriesQuery(request: any, { from, to }: TimeRange) {
return this.awsRequest('/api/tsdb/query', request).then((res: any) => { return this.awsRequest('/api/tsdb/query', request).then((res: any) => {
const data = []; if (!res.results) {
return { data: [] };
if (res.results) {
for (const query of request.queries) {
const queryRes = res.results[query.refId];
if (queryRes) {
for (const series of queryRes.series) {
const s = { target: series.name, datapoints: series.points } as any;
if (queryRes.meta.unit) {
s.unit = queryRes.meta.unit;
}
data.push(s);
}
}
}
} }
const dataFrames = Object.values(request.queries).reduce((acc: any, queryRequest: any) => {
const queryResult = res.results[queryRequest.refId];
if (!queryResult) {
return acc;
}
const link = this.buildCloudwatchConsoleUrl(
queryRequest,
from.toISOString(),
to.toISOString(),
`query${queryRequest.refId}`
);
return [
...acc,
...queryResult.series.map(({ name, points, meta }: any) => {
const series = { target: name, datapoints: points };
const dataFrame = toDataFrame(meta && meta.unit ? { ...series, unit: meta.unit } : series);
for (const field of dataFrame.fields) {
field.config.links = [
{
url: link,
title: 'View in CloudWatch console',
targetBlank: true,
},
];
}
return dataFrame;
}),
];
}, []);
return { data: data }; return { data: dataFrames };
}); });
} }
......
...@@ -15,6 +15,8 @@ describe('CloudWatchDatasource', () => { ...@@ -15,6 +15,8 @@ describe('CloudWatchDatasource', () => {
} as DataSourceInstanceSettings; } as DataSourceInstanceSettings;
const templateSrv = new TemplateSrv(); const templateSrv = new TemplateSrv();
const start = 1483196400 * 1000;
const defaultTimeRange = { from: new Date(start), to: new Date(start + 3600 * 1000) };
const timeSrv = { const timeSrv = {
time: { from: 'now-1h', to: 'now' }, time: { from: 'now-1h', to: 'now' },
...@@ -39,7 +41,7 @@ describe('CloudWatchDatasource', () => { ...@@ -39,7 +41,7 @@ describe('CloudWatchDatasource', () => {
let requestParams: { queries: CloudWatchQuery[] }; let requestParams: { queries: CloudWatchQuery[] };
const query = { const query = {
range: { from: 'now-1h', to: 'now' }, range: defaultTimeRange,
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
{ {
...@@ -110,7 +112,7 @@ describe('CloudWatchDatasource', () => { ...@@ -110,7 +112,7 @@ describe('CloudWatchDatasource', () => {
]); ]);
const query = { const query = {
range: { from: 'now-1h', to: 'now' }, range: defaultTimeRange,
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
{ {
...@@ -136,7 +138,7 @@ describe('CloudWatchDatasource', () => { ...@@ -136,7 +138,7 @@ describe('CloudWatchDatasource', () => {
it.each(['pNN.NN', 'p9', 'p99.', 'p99.999'])('should cancel query for invalid extended statistics (%s)', stat => { it.each(['pNN.NN', 'p9', 'p99.', 'p99.999'])('should cancel query for invalid extended statistics (%s)', stat => {
const query = { const query = {
range: { from: 'now-1h', to: 'now' }, range: defaultTimeRange,
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
{ {
...@@ -157,8 +159,8 @@ describe('CloudWatchDatasource', () => { ...@@ -157,8 +159,8 @@ describe('CloudWatchDatasource', () => {
it('should return series list', done => { it('should return series list', done => {
ctx.ds.query(query).then((result: any) => { ctx.ds.query(query).then((result: any) => {
expect(result.data[0].target).toBe(response.results.A.series[0].name); expect(result.data[0].name).toBe(response.results.A.series[0].name);
expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]); expect(result.data[0].fields[0].values.buffer[0]).toBe(response.results.A.series[0].points[0][0]);
done(); done();
}); });
}); });
...@@ -187,7 +189,7 @@ describe('CloudWatchDatasource', () => { ...@@ -187,7 +189,7 @@ describe('CloudWatchDatasource', () => {
it('should query for the datasource region if empty or "default"', done => { it('should query for the datasource region if empty or "default"', done => {
const query = { const query = {
range: { from: 'now-1h', to: 'now' }, range: defaultTimeRange,
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
{ {
...@@ -213,7 +215,7 @@ describe('CloudWatchDatasource', () => { ...@@ -213,7 +215,7 @@ describe('CloudWatchDatasource', () => {
describe('When performing CloudWatch query for extended statistics', () => { describe('When performing CloudWatch query for extended statistics', () => {
const query = { const query = {
range: { from: 'now-1h', to: 'now' }, range: defaultTimeRange,
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
{ {
...@@ -260,8 +262,8 @@ describe('CloudWatchDatasource', () => { ...@@ -260,8 +262,8 @@ describe('CloudWatchDatasource', () => {
it('should return series list', done => { it('should return series list', done => {
ctx.ds.query(query).then((result: any) => { ctx.ds.query(query).then((result: any) => {
expect(result.data[0].target).toBe(response.results.A.series[0].name); expect(result.data[0].name).toBe(response.results.A.series[0].name);
expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]); expect(result.data[0].fields[0].values.buffer[0]).toBe(response.results.A.series[0].points[0][0]);
done(); done();
}); });
}); });
...@@ -316,7 +318,7 @@ describe('CloudWatchDatasource', () => { ...@@ -316,7 +318,7 @@ describe('CloudWatchDatasource', () => {
it('should generate the correct query for single template variable', done => { it('should generate the correct query for single template variable', done => {
const query = { const query = {
range: { from: 'now-1h', to: 'now' }, range: defaultTimeRange,
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
{ {
...@@ -341,7 +343,7 @@ describe('CloudWatchDatasource', () => { ...@@ -341,7 +343,7 @@ describe('CloudWatchDatasource', () => {
it('should generate the correct query for multilple template variables', done => { it('should generate the correct query for multilple template variables', done => {
const query = { const query = {
range: { from: 'now-1h', to: 'now' }, range: defaultTimeRange,
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
{ {
...@@ -377,7 +379,7 @@ describe('CloudWatchDatasource', () => { ...@@ -377,7 +379,7 @@ describe('CloudWatchDatasource', () => {
it('should generate the correct query for multilple template variables, lack scopedVars', done => { it('should generate the correct query for multilple template variables, lack scopedVars', done => {
const query = { const query = {
range: { from: 'now-1h', to: 'now' }, range: defaultTimeRange,
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
{ {
...@@ -412,7 +414,7 @@ describe('CloudWatchDatasource', () => { ...@@ -412,7 +414,7 @@ describe('CloudWatchDatasource', () => {
it('should generate the correct query for multilple template variables with expression', done => { it('should generate the correct query for multilple template variables with expression', done => {
const query: any = { const query: any = {
range: { from: 'now-1h', to: 'now' }, range: defaultTimeRange,
rangeRaw: { from: 1483228800, to: 1483232400 }, rangeRaw: { from: 1483228800, to: 1483232400 },
targets: [ targets: [
{ {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment