Skip to content
This repository has been archived by the owner on Oct 20, 2021. It is now read-only.

Commit

Permalink
feat: Added a chart to display global Health Check average
Browse files Browse the repository at this point in the history
  • Loading branch information
aelamrani authored and brasseld committed Dec 12, 2017
1 parent 3d883f2 commit 5e750d2
Show file tree
Hide file tree
Showing 9 changed files with 179 additions and 40 deletions.
2 changes: 1 addition & 1 deletion build.json
@@ -1,3 +1,3 @@
{
"version": "1.11.4"
"version": "1.12.0-SNAPSHOT"
}
2 changes: 1 addition & 1 deletion package.json
@@ -1,6 +1,6 @@
{
"name": "gravitee-management-webui",
"version": "1.11.4",
"version": "1.12.0-SNAPSHOT",
"description": "Gravitee.io APIM - Portal",
"dependencies": {
"angular": "^1.5.0",
Expand Down
8 changes: 5 additions & 3 deletions src/components/chart/chart.directive.ts
Expand Up @@ -93,7 +93,7 @@ class ChartDirective {

for (i = 0; i < Highcharts.charts.length; i++) {
chart = Highcharts.charts[i];
if (chart) {
if (chart && chart.pointer) {
if (e.originalEvent) {
event = chart.pointer.normalize(e.originalEvent);
} else {
Expand Down Expand Up @@ -185,14 +185,16 @@ class ChartDirective {
formatter: function () {
//TODO: check this
//let s = '<b>' + Highcharts.dateFormat('%A, %b %d, %H:%M', new Date(this.x)) + '</b>';
let s = '<b>' + Highcharts.dateFormat('%A, %b %d, %H:%M', this.x) + '</b>';
let dateFormat = newOptions.dateFormat || '%A, %b %d, %H:%M';
let s = '<b>' + Highcharts.dateFormat(dateFormat, this.x) + '</b>';
if (_.filter(this.points, (point: any) => {
return point.y !== 0;
}).length) {
_.forEach(this.points, function (point) {
if (point.y) {
let name = ' ' + (point.series.options.labelPrefix ? point.series.options.labelPrefix + ' ' + point.series.name : point.series.name);
s += '<br /><span style="color:' + point.color + '">\u25CF</span>' + name + ': <b>' + point.y + '</b>';
s += '<br /><span style="color:' + point.color + '">\u25CF</span>' + name + ': <b>' + (point.series.options.decimalFormat?Highcharts.numberFormat(point.y, 2):point.y) +
(point.series.options.labelSuffix?point.series.options.labelSuffix:'') + '</b>';
}
});
}
Expand Down
37 changes: 4 additions & 33 deletions src/components/widget/widget-chart-line.component.ts
Expand Up @@ -23,39 +23,10 @@ const WidgetChartLineComponent: ng.IComponentOptions = {
require: {
parent: '^gvWidget'
},
controller: function($scope: ng.IScope, $rootScope) {
controller: function($scope: ng.IScope, $rootScope, ChartService) {
'ngInject';

this.$scope = $scope;

//from https://material.google.com/style/color.html#color-color-palette
//shade 500 & 900
//
// deep purple, lime, deep orange, pink, purple,
// light green, amber, Blue Grey, orange, teal,
// indigo, purple, red, cyan, brown
this.colorByBucket = [
'#673ab7', '#cddc39', '#ff5722', '#e91e63', '#9c27b0',
'#8bc34a', '#ffc107', '#607d8b', '#ff9800', '#009688',
'#3f51b5', '#9c27b0', '#f44336', '#00bcd4', '#795548',

'#311b92', '#827717', '#bf360c', '#880e4f', '#4a148c',
'#33691e', '#ff6f00', '#263238', '#e65100', '#004d40',
'#1a237e', '#4a148c', '#b71c1c', '#006064', '#3e2723'
];


//from https://material.google.com/style/color.html#color-color-palette
//shade 200 & 300
this.bgColorByBucket = [
'#b39ddb', '#e6ee9c', '#ffab91', '#f48fb1', '#ce93d8',
'#c5e1a5', '#ffe082', '#b0bec5', '#ffcc80', '#80cbc4',
'#9fa8da', '#ce93d8', '#ef9a9a', '#80deea', '#bcaaa4',

'#9575cd', '#dce775', '#ff8a65', '#f06292', '#ba68c8',
'#aed581', '#ffd54f', '#90a4ae', '#ffb74d', '#4db6ac',
'#7986cb', '#ba68c8', '#e57373', '#4dd0e1', '#a1887f'
];
this.$scope = $scope;

let that = this;
this.$onChanges = function(changes) {
Expand Down Expand Up @@ -97,8 +68,8 @@ const WidgetChartLineComponent: ng.IComponentOptions = {
_.forEach(value.buckets, function (bucket) {
if (bucket) {
i++;
let lineColor = that.colorByBucket[i % that.colorByBucket.length];
let bgColor = that.bgColorByBucket[i % that.bgColorByBucket.length];
let lineColor = ChartService.colorByBucket[i % ChartService.colorByBucket.length];
let bgColor = ChartService.bgColorByBucket[i % ChartService.bgColorByBucket.length];
let label = that.parent.widget.chart.labels ? that.parent.widget.chart.labels[idx] : (bucket.name);
if (value.metadata && value.metadata[bucket.name]) {
label = value.metadata[bucket.name].name;
Expand Down
3 changes: 3 additions & 0 deletions src/management/api/healthcheck/healthcheck-visualize.html
Expand Up @@ -35,6 +35,9 @@
</md-card-title-media>
</md-card-title>
<md-card-content>
<div>
<gravitee-chart options="healthCheckCtrl.chartData" zoom="true" type="areaspline" height="180"></gravitee-chart>
</div>
<div layout="row" layout-padding layout-align="center center">
<div layout="column" flex="15">
<h5>Last minute</h5>
Expand Down
96 changes: 94 additions & 2 deletions src/management/api/healthcheck/healthcheck.controller.ts
Expand Up @@ -14,18 +14,23 @@
* limitations under the License.
*/
import ApiService, { LogsQuery } from "../../../services/api.service";
import * as moment from "moment";
import * as _ from "lodash";

class ApiHealthCheckController {
private api: any;
private gateway: any;
private endpoint: any;
private logs: {total: string; logs: any[], metadata: any};
private query: LogsQuery;
private chartData: any;

constructor (
private ApiService: ApiService,
private $scope,
private $state: ng.ui.IStateService
private $state: ng.ui.IStateService,
private ChartService,
private $q
) {
'ngInject';
this.api = this.$scope.$parent.apiCtrl.api;
Expand All @@ -42,6 +47,8 @@ class ApiHealthCheckController {
this.updateChart();
}



updateChart() {
this.ApiService.apiHealth(this.api.id, 'availability')
.then(response => {this.endpoint.availabilities.data = response.data;});
Expand All @@ -63,10 +70,95 @@ class ApiHealthCheckController {
this.refresh();
}

refresh() {
refresh(averageFrom?, averageTo?) {
this.ApiService.apiHealthLogs(this.api.id, this.query).then((logs) => {
this.logs = logs.data;
});

let from = averageFrom || moment().subtract(1, 'months');
let to = averageTo || moment();
let interval = Math.floor((to - from)/30);
let promises = [
this.ApiService.apiHealthAverage(this.api.id, {from: from, to: to,
interval: interval, type: 'RESPONSE_TIME'}),
this.ApiService.apiHealthAverage(this.api.id, {from: from, to: to,
interval: interval, type: 'AVAILABILITY'})
];

this.$q.all(promises).then(responses => {
let i = 0, series = [];
_.forEach(responses, response => {
let values = response.data.values;
if (values && values.length > 0) {
_.forEach(values, value => {
_.forEach(value.buckets, bucket => {
if (bucket) {
let responseTimeLine = i == 0;
series.push({
name: 'Average of ' + (responseTimeLine?'response time':'availability'), data: bucket.data, color: responseTimeLine?'#337AB7':'#5CB85C',
type: responseTimeLine?'area':'column',
labelSuffix: responseTimeLine?'ms':'%',
decimalFormat: !responseTimeLine,
yAxis: i,
zones: responseTimeLine?[]:[{
value: 80,
color: '#D9534F'
}, {
value: 95,
color: '#F0AD4E'
}, {
color: '#5CB85C'
}]
});
}
});
});
}
i++;
});
this.chartData = {
plotOptions: {
series: {
pointStart: responses[0].data.timestamp.from,
pointInterval: responses[0].data.timestamp.interval
}
},
series: series,
xAxis: {
type: 'datetime',
dateTimeLabelFormats: {
month: '%e. %b',
year: '%b'
}
},
yAxis: [{
labels: {
format: '{value}ms'
},
title: {
text: 'Response time'
}
},{
title: {
text: 'Availability'
},
labels: {
format: '{value}%'
},
max: 100,
opposite: true
}],
chart: {
events: {
selection: (event) => {
if (!event.resetSelection) {
this.refresh(Math.floor(event.xAxis[0].min), Math.round(event.xAxis[0].max));
}
}
}
}
}
});
}

getMetadata(id) {
Expand Down
3 changes: 3 additions & 0 deletions src/management/management.module.ts
Expand Up @@ -340,6 +340,8 @@ import uiRouter from 'angular-ui-router';
import {permission, uiPermission} from 'angular-permission';
import ApiHeaderController from './api/header/api-header.controller';

import ChartService from '../services/chart.service';

angular.module('gravitee-management', [uiRouter, permission, uiPermission, 'ngMaterial', 'ramlConsoleApp', 'ng-showdown', 'swaggerUi',
'ngMdIcons', 'ui.codemirror', 'md.data.table', 'ngCookies', 'dragularModule', 'readMore',
'ngMessages', 'vAccordion', 'schemaForm', 'ngclipboard', 'ui.validate', 'angular-timeline',
Expand Down Expand Up @@ -472,6 +474,7 @@ angular.module('gravitee-management', [uiRouter, permission, uiPermission, 'ngMa
.service('RoleService', RoleService)
.service('TicketService', TicketService)
.service('AuditService', AuditService)
.service('ChartService', ChartService)
.directive('filecontent', () => DocumentationDirective)
.directive('noDirtyCheck', () => new FormDirective())
.directive('autofocus', () => new AutofocusDirective())
Expand Down
14 changes: 14 additions & 0 deletions src/services/api.service.ts
Expand Up @@ -334,6 +334,20 @@ class ApiService {
return this.$http.get(this.apisURL + api + '/health/logs/' + log);
}

apiHealthAverage(api, request) {
var url = this.apisURL + api + '/health/average?';

var keys = Object.keys(request);
_.forEach(keys, function (key) {
var val = request[key];
if (val !== undefined && val !== '') {
url += key + '=' + val + '&';
}
});

return this.$http.get(url, {timeout: 30000});
}

/*
* API ratings
*/
Expand Down
54 changes: 54 additions & 0 deletions src/services/chart.service.ts
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2015 The Gravitee team (http://gravitee.io)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
class ChartService {
private colorByBucket: [string];
private bgColorByBucket: [string];

constructor() {
'ngInject';

//from https://material.google.com/style/color.html#color-color-palette
//shade 500 & 900
//
// deep purple, lime, deep orange, pink, purple,
// light green, amber, Blue Grey, orange, teal,
// indigo, purple, red, cyan, brown
this.colorByBucket = [
'#673ab7', '#cddc39', '#ff5722', '#e91e63', '#9c27b0',
'#8bc34a', '#ffc107', '#607d8b', '#ff9800', '#009688',
'#3f51b5', '#9c27b0', '#f44336', '#00bcd4', '#795548',

'#311b92', '#827717', '#bf360c', '#880e4f', '#4a148c',
'#33691e', '#ff6f00', '#263238', '#e65100', '#004d40',
'#1a237e', '#4a148c', '#b71c1c', '#006064', '#3e2723'
];


//from https://material.google.com/style/color.html#color-color-palette
//shade 200 & 300
this.bgColorByBucket = [
'#b39ddb', '#e6ee9c', '#ffab91', '#f48fb1', '#ce93d8',
'#c5e1a5', '#ffe082', '#b0bec5', '#ffcc80', '#80cbc4',
'#9fa8da', '#ce93d8', '#ef9a9a', '#80deea', '#bcaaa4',

'#9575cd', '#dce775', '#ff8a65', '#f06292', '#ba68c8',
'#aed581', '#ffd54f', '#90a4ae', '#ffb74d', '#4db6ac',
'#7986cb', '#ba68c8', '#e57373', '#4dd0e1', '#a1887f'
];
}
}

export default ChartService;

0 comments on commit 5e750d2

Please sign in to comment.