Skip to content

Commit aef644b

Browse files
committed
feat(cloudwatch): final polish to the cloudwatch editor, closes grafana#684
1 parent c34c3ac commit aef644b

File tree

4 files changed

+95
-155
lines changed

4 files changed

+95
-155
lines changed

public/app/plugins/datasource/cloudwatch/datasource.js

Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ function (angular, _) {
3535
query.namespace = templateSrv.replace(target.namespace, options.scopedVars);
3636
query.metricName = templateSrv.replace(target.metricName, options.scopedVars);
3737
query.dimensions = convertDimensionFormat(target.dimensions);
38-
query.statistics = getActivatedStatistics(target.statistics);
38+
query.statistics = target.statistics;
3939
query.period = parseInt(target.period, 10);
4040

4141
var range = end - start;
@@ -191,7 +191,7 @@ function (angular, _) {
191191
});
192192
}
193193

194-
return this.getDimensionValues(region, namespace, metricName, dimensions).then(transformSuggestData);
194+
return this.getDimensionValues(region, namespace, metricName, dimensions);
195195
}
196196

197197
var ebsVolumeIdsQuery = query.match(/^ebs_volume_ids\(([^,]+?),\s?([^,]+?)\)/);
@@ -243,63 +243,44 @@ function (angular, _) {
243243
};
244244

245245
function transformMetricData(md, options) {
246-
var result = [];
247-
248-
console.log(options);
249-
var dimensionPart = templateSrv.replace(JSON.stringify(options.dimensions));
250-
_.each(getActivatedStatistics(options.statistics), function(s) {
251-
var originalSettings = _.templateSettings;
252-
_.templateSettings = {
253-
interpolate: /\{\{(.+?)\}\}/g
254-
};
255-
var template = _.template(options.legendFormat);
256-
257-
var metricLabel;
258-
if (_.isEmpty(options.legendFormat)) {
259-
metricLabel = md.Label + '_' + s + dimensionPart;
260-
} else {
261-
var d = convertDimensionFormat(options.dimensions);
262-
metricLabel = template({
263-
Region: templateSrv.replace(options.region),
264-
Namespace: templateSrv.replace(options.namespace),
265-
MetricName: templateSrv.replace(options.metricName),
266-
Dimensions: d,
267-
Statistics: s
268-
});
269-
}
270-
271-
_.templateSettings = originalSettings;
246+
var aliasRegex = /\{\{(.+?)\}\}/g;
247+
var aliasPattern = options.alias || '{{metric}}_{{stat}}';
248+
var aliasData = {
249+
region: templateSrv.replace(options.region),
250+
namespace: templateSrv.replace(options.namespace),
251+
metric: templateSrv.replace(options.metricName),
252+
};
253+
_.extend(aliasData, options.dimensions);
254+
255+
return _.map(options.statistics, function(stat) {
256+
var dps = _.chain(md.Datapoints).map(function(dp) {
257+
return [dp[stat], new Date(dp.Timestamp).getTime()];
258+
})
259+
.sortBy(function(dp) {
260+
return dp[1];
261+
}).value();
272262

273-
var dps = _.map(md.Datapoints, function(value) {
274-
return [value[s], new Date(value.Timestamp).getTime()];
263+
aliasData.stat = stat;
264+
var seriesName = aliasPattern.replace(aliasRegex, function(match, g1) {
265+
if (aliasData[g1]) {
266+
return aliasData[g1];
267+
}
268+
return g1;
275269
});
276-
dps = _.sortBy(dps, function(dp) { return dp[1]; });
277270

278-
result.push({ target: metricLabel, datapoints: dps });
279-
});
280-
281-
return result;
282-
}
283-
284-
function getActivatedStatistics(statistics) {
285-
var activatedStatistics = [];
286-
_.each(statistics, function(v, k) {
287-
if (v) {
288-
activatedStatistics.push(k);
289-
}
271+
return {target: seriesName, datapoints: dps};
290272
});
291-
return activatedStatistics;
292273
}
293274

294275
function convertToCloudWatchTime(date) {
295276
return Math.round(date.valueOf() / 1000);
296277
}
297278

298279
function convertDimensionFormat(dimensions) {
299-
return _.map(_.keys(dimensions), function(key) {
280+
return _.map(dimensions, function(value, key) {
300281
return {
301282
Name: templateSrv.replace(key),
302-
Value: templateSrv.replace(dimensions[key])
283+
Value: templateSrv.replace(value)
303284
};
304285
});
305286
}

public/app/plugins/datasource/cloudwatch/partials/query.editor.html

Lines changed: 16 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
</ul>
3434

3535
<ul class="tight-form-list" role="menu">
36-
<li class="tight-form-item" style="width: 100px">
36+
<li class="tight-form-item query-keyword" style="width: 100px">
3737
Metric
3838
</li>
3939
<li>
@@ -45,77 +45,46 @@
4545
<li>
4646
<metric-segment segment="metricSegment" get-options="getMetrics()" on-change="metricChanged()"></metric-segment>
4747
</li>
48+
<li class="tight-form-item query-keyword">
49+
Stats
50+
</li>
51+
<li ng-repeat="segment in statSegments">
52+
<metric-segment segment="segment" get-options="getStatSegments(segment, $index)" on-change="statSegmentChanged(segment, $index)"></metric-segment>
53+
</li>
4854
</ul>
4955

5056
<div class="clearfix"></div>
5157
</div>
5258

5359
<div class="tight-form">
5460
<ul class="tight-form-list" role="menu">
55-
<li class="tight-form-item tight-form-align" style="width: 100px">
61+
<li class="tight-form-item query-keyword tight-form-align" style="width: 100px">
5662
Dimensions
5763
</li>
5864
<li ng-repeat="segment in dimSegments">
5965
<metric-segment segment="segment" get-options="getDimSegments(segment, $index)" on-change="dimSegmentChanged(segment, $index)"></metric-segment>
6066
</li>
6167
</ul>
68+
6269
<div class="clearfix"></div>
6370
</div>
6471

6572
<div class="tight-form">
6673
<ul class="tight-form-list" role="menu">
67-
<li class="tight-form-item tight-form-align" style="width: 100px">
68-
Statistics
74+
<li class="tight-form-item query-keyword tight-form-align" style="width: 100px">
75+
Alias
76+
<tip>{{metric}} {{stat}} {{namespace}} {{region}} {{<dimension name>}}</tip>
6977
</li>
70-
<li class="tight-form-item">
71-
<editor-checkbox text="Min" model="target.statistics.Minimum" change="statisticsOptionChanged()"></editor-checkbox>
72-
<editor-checkbox text="Max" model="target.statistics.Maximum" change="statisticsOptionChanged()"></editor-checkbox>
73-
<editor-checkbox text="Avg" model="target.statistics.Average" change="statisticsOptionChanged()"></editor-checkbox>
74-
<editor-checkbox text="Sum" model="target.statistics.Sum" change="statisticsOptionChanged()"></editor-checkbox>
75-
<editor-checkbox text="SampleCount" model="target.statistics.SampleCount" change="statisticsOptionChanged()"></editor-checkbox>
78+
<li>
79+
<input type="text" class="input-xlarge tight-form-input" ng-model="target.alias" spellcheck='false' ng-model-onblur ng-change="refreshMetricData()">
7680
</li>
77-
<li class="tight-form-item">
81+
<li class="tight-form-item query-keyword">
7882
Period
7983
</li>
8084
<li>
81-
<input type="text"
82-
class="input-mini tight-form-input"
83-
ng-model="target.period"
84-
data-placement="right"
85-
spellcheck='false'
86-
placeholder="period"
87-
data-min-length=0 data-items=100
88-
ng-model-onblur
89-
ng-change="refreshMetricData()"
90-
/>
91-
<a bs-tooltip="target.errors.period"
92-
style="color: rgb(229, 189, 28)"
93-
ng-show="target.errors.period">
94-
<i class="fa fa-warning"></i>
95-
</a>
85+
<input type="text" class="input-mini tight-form-input" ng-model="target.period" spellcheck='false' placeholder="period" ng-model-onblur ng-change="refreshMetricData()" />
9686
</li>
97-
</ul>
9887

99-
<div class="clearfix"></div>
100-
</div>
101-
102-
<div class="tight-form">
103-
<ul class="tight-form-list" role="menu">
104-
<li class="tight-form-item tight-form-align" style="width: 100px">
105-
Alias Pattern
106-
</li>
107-
<li>
108-
<input type="text"
109-
class="input-xxlarge tight-form-input"
110-
ng-model="target.legendFormat"
111-
spellcheck='false'
112-
placeholder="Syntax: {{Region}} {{Namespace}} {{MetricName}} {{Statistics}} {{Dimensions[N].Name}} {{Dimensions[N].Value}}"
113-
data-min-length=0 data-items=100
114-
ng-model-onblur
115-
ng-change="refreshMetricData()"
116-
>
117-
<tip>Syntax: {{Region}} {{Namespace}} {{MetricName}} {{Statistics}} {{Dimensions[N].Name}} {{Dimensions[N].Value}}</tip>
118-
</li>
11988
</ul>
12089

12190
<div class="clearfix"></div>

public/app/plugins/datasource/cloudwatch/query_ctrl.js

Lines changed: 47 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,15 @@ function (angular, _) {
1010
module.controller('CloudWatchQueryCtrl', function($scope, templateSrv, uiSegmentSrv, $q) {
1111

1212
$scope.init = function() {
13-
$scope.target.namespace = $scope.target.namespace || '';
14-
$scope.target.metricName = $scope.target.metricName || '';
15-
$scope.target.statistics = $scope.target.statistics || {Average: true};
16-
$scope.target.dimensions = $scope.target.dimensions || {};
17-
$scope.target.period = $scope.target.period || 60;
18-
$scope.target.region = $scope.target.region || $scope.datasource.getDefaultRegion();
13+
var target = $scope.target;
14+
target.namespace = target.namespace || '';
15+
target.metricName = target.metricName || '';
16+
target.statistics = target.statistics || ['Average'];
17+
target.dimensions = target.dimensions || {};
18+
target.period = target.period || 60;
19+
target.region = target.region || $scope.datasource.getDefaultRegion();
20+
21+
$scope.aliasSyntax = '{{metric}} {{stat}} {{namespace}} {{region}} {{<dimension name>}}';
1922

2023
$scope.regionSegment = uiSegmentSrv.getSegmentForValue($scope.target.region, 'select region');
2124
$scope.namespaceSegment = uiSegmentSrv.getSegmentForValue($scope.target.namespace, 'select namespace');
@@ -28,16 +31,48 @@ function (angular, _) {
2831
return memo;
2932
}, []);
3033

31-
$scope.fixSegments();
34+
$scope.statSegments = _.map($scope.target.statistics, function(stat) {
35+
return uiSegmentSrv.getSegmentForValue(stat);
36+
});
37+
38+
$scope.ensurePlusButton($scope.statSegments);
39+
$scope.ensurePlusButton($scope.dimSegments);
3240
$scope.removeDimSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove dimension --'});
41+
$scope.removeStatSegment = uiSegmentSrv.newSegment({fake: true, value: '-- remove stat --'});
42+
};
43+
44+
$scope.getStatSegments = function() {
45+
return $q.when([
46+
angular.copy($scope.removeStatSegment),
47+
uiSegmentSrv.getSegmentForValue('Average'),
48+
uiSegmentSrv.getSegmentForValue('Maximum'),
49+
uiSegmentSrv.getSegmentForValue('Minimum'),
50+
uiSegmentSrv.getSegmentForValue('Sum'),
51+
uiSegmentSrv.getSegmentForValue('SampleCount'),
52+
]);
53+
};
54+
55+
$scope.statSegmentChanged = function(segment, index) {
56+
if (segment.value === $scope.removeStatSegment.value) {
57+
$scope.statSegments.splice(index, 1);
58+
} else {
59+
segment.type = 'value';
60+
}
61+
62+
$scope.target.statistics = _.reduce($scope.statSegments, function(memo, seg) {
63+
if (!seg.fake) { memo.push(seg.value); } return memo;
64+
}, []);
65+
66+
$scope.ensurePlusButton($scope.statSegments);
67+
$scope.get_data();
3368
};
3469

35-
$scope.fixSegments = function() {
36-
var count = $scope.dimSegments.length;
37-
var lastSegment = $scope.dimSegments[Math.max(count-1, 0)];
70+
$scope.ensurePlusButton = function(segments) {
71+
var count = segments.length;
72+
var lastSegment = segments[Math.max(count-1, 0)];
3873

3974
if (!lastSegment || lastSegment.type !== 'plus-button') {
40-
$scope.dimSegments.push(uiSegmentSrv.newPlusButton());
75+
segments.push(uiSegmentSrv.newPlusButton());
4176
}
4277
};
4378

@@ -74,8 +109,8 @@ function (angular, _) {
74109
segment.cssClass = 'query-segment-key';
75110
}
76111

77-
$scope.fixSegments();
78112
$scope.syncDimSegmentsWithModel();
113+
$scope.ensurePlusButton($scope.dimSegments);
79114
$scope.get_data();
80115
};
81116

@@ -147,48 +182,6 @@ function (angular, _) {
147182
}
148183
};
149184

150-
$scope.addDimension = function() {
151-
if (!$scope.addDimensionMode) {
152-
$scope.addDimensionMode = true;
153-
return;
154-
}
155-
156-
if (!$scope.target.dimensions) {
157-
$scope.target.dimensions = {};
158-
}
159-
160-
$scope.target.dimensions[$scope.target.currentDimensionKey] = $scope.target.currentDimensionValue;
161-
$scope.target.escapedDimensions = this.escapeDimensions($scope.target.dimensions);
162-
$scope.target.currentDimensionKey = '';
163-
$scope.target.currentDimensionValue = '';
164-
$scope.refreshMetricData();
165-
166-
$scope.addDimensionMode = false;
167-
};
168-
169-
$scope.removeDimension = function(key) {
170-
key = key.replace(/\\\$/g, '$');
171-
delete $scope.target.dimensions[key];
172-
$scope.target.escapedDimensions = this.escapeDimensions($scope.target.dimensions);
173-
$scope.refreshMetricData();
174-
};
175-
176-
$scope.escapeDimensions = function(d) {
177-
var result = {};
178-
_.chain(d)
179-
.keys(d)
180-
.each(function(k) {
181-
var v = d[k];
182-
result[k.replace(/\$/g, '\uFF04')] = v.replace(/\$/g, '\$');
183-
});
184-
185-
return result;
186-
};
187-
188-
$scope.statisticsOptionChanged = function() {
189-
$scope.refreshMetricData();
190-
};
191-
192185
$scope.init();
193186

194187
});

public/app/plugins/datasource/cloudwatch/specs/datasource_specs.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,7 @@ describe('CloudWatchDatasource', function() {
3434
dimensions: {
3535
InstanceId: 'i-12345678'
3636
},
37-
statistics: {
38-
Average: true
39-
},
37+
statistics: ['Average'],
4038
period: 300
4139
}
4240
]
@@ -66,7 +64,7 @@ describe('CloudWatchDatasource', function() {
6664
expect(params.metricName).to.be(query.targets[0].metricName);
6765
expect(params.dimensions[0].Name).to.be(Object.keys(query.targets[0].dimensions)[0]);
6866
expect(params.dimensions[0].Value).to.be(query.targets[0].dimensions[Object.keys(query.targets[0].dimensions)[0]]);
69-
expect(params.statistics).to.eql(Object.keys(query.targets[0].statistics));
67+
expect(params.statistics).to.eql(query.targets[0].statistics);
7068
expect(params.period).to.be(query.targets[0].period);
7169
done();
7270
});
@@ -75,9 +73,8 @@ describe('CloudWatchDatasource', function() {
7573

7674
it('should return series list', function(done) {
7775
ctx.ds.query(query).then(function(result) {
78-
var s = Object.keys(query.targets[0].statistics)[0];
79-
expect(result.data[0].target).to.be(response.Label + '_' + s + JSON.stringify(query.targets[0].dimensions));
80-
expect(result.data[0].datapoints[0][0]).to.be(response.Datapoints[0][s]);
76+
expect(result.data[0].target).to.be('CPUUtilization_Average');
77+
expect(result.data[0].datapoints[0][0]).to.be(response.Datapoints[0]['Average']);
8178
done();
8279
});
8380
ctx.$rootScope.$apply();
@@ -167,7 +164,7 @@ describe('CloudWatchDatasource', function() {
167164
};
168165
});
169166

170-
it('should call __GetMetrics and return result', () => {
167+
it('should call __ListMetrics and return result', () => {
171168
expect(scenario.result[0].text).to.be('i-12345678');
172169
expect(scenario.request.data.action).to.be('ListMetrics');
173170
});

0 commit comments

Comments
 (0)