mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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
This commit is contained in:
parent
a1e8157969
commit
69691fbd6e
@ -1,6 +1,6 @@
|
|||||||
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>
|
|||||||
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>
|
|||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
return { data: data };
|
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: dataFrames };
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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', () => {
|
|||||||
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', () => {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
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', () => {
|
|||||||
|
|
||||||
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', () => {
|
|||||||
|
|
||||||
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', () => {
|
|||||||
|
|
||||||
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', () => {
|
|||||||
|
|
||||||
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', () => {
|
|||||||
|
|
||||||
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', () => {
|
|||||||
|
|
||||||
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', () => {
|
|||||||
|
|
||||||
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', () => {
|
|||||||
|
|
||||||
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', () => {
|
|||||||
|
|
||||||
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: [
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user