Commit ac78b6dd authored by Clement Ho's avatar Clement Ho

Merge branch 'ce-9929-cluster-monitoring-is-broken-after-echarts' into 'master'

Backport: Fix cluster health charts

See merge request gitlab-org/gitlab-ce!25823
parents 78a01253 b7c4ddfc
......@@ -4,6 +4,7 @@ import dateFormat from 'dateformat';
import { debounceByAnimationFrame } from '~/lib/utils/common_utils';
import { getSvgIconPathContent } from '~/lib/utils/icon_utils';
import Icon from '~/vue_shared/components/icon.vue';
import { chartHeight, graphTypes, lineTypes } from '../../constants';
let debouncedResize;
......@@ -19,7 +20,6 @@ export default {
required: true,
validator(data) {
return (
data.queries &&
Array.isArray(data.queries) &&
data.queries.filter(query => {
if (Array.isArray(query.result)) {
......@@ -51,21 +51,44 @@ export default {
return {
tooltip: {
title: '',
content: '',
content: [],
isDeployment: false,
sha: '',
},
width: 0,
height: 0,
height: chartHeight,
svgs: {},
primaryColor: null,
};
},
computed: {
chartData() {
return this.graphData.queries.reduce((accumulator, query) => {
accumulator[query.unit] = query.result.reduce((acc, res) => acc.concat(res.values), []);
return accumulator;
}, {});
return this.graphData.queries.map(query => {
const { appearance } = query;
const lineType =
appearance && appearance.line && appearance.line.type
? appearance.line.type
: lineTypes.default;
const lineColor = lineType === lineTypes.threshold ? this.primaryColor : undefined;
return {
name: this.formatLegendLabel(query),
data: this.concatenateResults(query.result),
lineStyle: {
type: lineType,
color: lineColor,
},
itemStyle: {
color: lineColor,
},
areaStyle: {
opacity:
appearance && appearance.area && typeof appearance.area.opacity === 'number'
? appearance.area.opacity
: undefined,
},
};
});
},
chartOptions() {
return {
......@@ -85,9 +108,6 @@ export default {
formatter: value => value.toFixed(3),
},
},
legend: {
formatter: this.xAxisLabel,
},
series: this.scatterSeries,
dataZoom: this.dataZoomConfig,
};
......@@ -98,8 +118,8 @@ export default {
return handleIcon ? { handleIcon } : {};
},
earliestDatapoint() {
return Object.values(this.chartData).reduce((acc, data) => {
const [[timestamp]] = data.sort(([a], [b]) => {
return this.chartData.reduce((acc, series) => {
const [[timestamp]] = series.data.sort(([a], [b]) => {
if (a < b) {
return -1;
}
......@@ -129,14 +149,14 @@ export default {
},
scatterSeries() {
return {
type: 'scatter',
type: graphTypes.deploymentData,
data: this.recentDeployments.map(deployment => [deployment.createdAt, 0]),
symbol: this.svgs.rocket,
symbolSize: 14,
};
itemStyle: {
color: this.primaryColor,
},
xAxisLabel() {
return this.graphData.queries.map(query => query.label).join(', ');
};
},
yAxisLabel() {
return `${this.graphData.y_label}`;
......@@ -155,18 +175,34 @@ export default {
this.setSvg('scroll-handle');
},
methods: {
concatenateResults(results) {
return results.reduce((acc, result) => acc.concat(result.values), []);
},
formatLegendLabel(query) {
return `${query.label}`;
},
formatTooltipText(params) {
const [seriesData] = params.seriesData;
this.tooltip.isDeployment = seriesData.componentSubType === 'scatter';
this.tooltip.title = dateFormat(params.value, 'dd mmm yyyy, h:MMTT');
if (this.tooltip.isDeployment) {
this.tooltip.content = [];
params.seriesData.forEach(seriesData => {
if (seriesData.componentSubType === graphTypes.deploymentData) {
this.tooltip.isDeployment = true;
const [deploy] = this.recentDeployments.filter(
deployment => deployment.createdAt === seriesData.value[0],
);
this.tooltip.sha = deploy.sha.substring(0, 8);
} else {
this.tooltip.content = `${this.yAxisLabel} ${seriesData.value[1].toFixed(3)}`;
const { seriesName } = seriesData;
// seriesData.value contains the chart's [x, y] value pair
// seriesData.value[1] is threfore the chart y value
const value = seriesData.value[1].toFixed(3);
this.tooltip.content.push({
name: seriesName,
value,
});
}
});
},
setSvg(name) {
getSvgIconPathContent(name)
......@@ -177,10 +213,12 @@ export default {
})
.catch(() => {});
},
onChartUpdated(chart) {
[this.primaryColor] = chart.getOption().color;
},
onResize() {
const { width, height } = this.$refs.areaChart.$el.getBoundingClientRect();
const { width } = this.$refs.areaChart.$el.getBoundingClientRect();
this.width = width;
this.height = height;
},
},
};
......@@ -201,6 +239,7 @@ export default {
:thresholds="alertData"
:width="width"
:height="height"
@updated="onChartUpdated"
>
<template slot="tooltipTitle">
<div v-if="tooltip.isDeployment">
......@@ -214,7 +253,13 @@ export default {
{{ tooltip.sha }}
</div>
<template v-else>
{{ tooltip.content }}
<div
v-for="(content, key) in tooltip.content"
:key="key"
class="d-flex justify-content-between"
>
{{ content.name }} {{ content.value }}
</div>
</template>
</template>
</gl-area-chart>
......
export const chartHeight = 300;
export const graphTypes = {
deploymentData: 'scatter',
};
export const lineTypes = {
default: 'solid',
threshold: 'dashed',
};
......@@ -75,15 +75,6 @@ describe('Area component', () => {
expect(shallowWrapperContainsSlotText(glAreaChart, 'tooltipTitle', mockTitle)).toBe(true);
});
it('recieves tooltip content', () => {
const mockContent = 'mockContent';
areaChart.vm.tooltip.content = mockContent;
expect(shallowWrapperContainsSlotText(glAreaChart, 'tooltipContent', mockContent)).toBe(
true,
);
});
describe('when tooltip is showing deployment data', () => {
beforeEach(() => {
areaChart.vm.tooltip.isDeployment = true;
......@@ -111,6 +102,7 @@ describe('Area component', () => {
const generateSeriesData = type => ({
seriesData: [
{
seriesName: areaChart.vm.chartData[0].name,
componentSubType: type,
value: [mockDate, 5.55555],
},
......@@ -128,7 +120,14 @@ describe('Area component', () => {
});
it('formats tooltip content', () => {
expect(areaChart.vm.tooltip.content).toBe('CPU 5.556');
expect(areaChart.vm.tooltip.content).toEqual([{ name: 'Core Usage', value: '5.556' }]);
expect(
shallowWrapperContainsSlotText(
areaChart.find(GlAreaChart),
'tooltipContent',
'Core Usage 5.556',
),
).toBe(true);
});
});
......@@ -168,12 +167,10 @@ describe('Area component', () => {
describe('onResize', () => {
const mockWidth = 233;
const mockHeight = 144;
beforeEach(() => {
spyOn(Element.prototype, 'getBoundingClientRect').and.callFake(() => ({
width: mockWidth,
height: mockHeight,
}));
areaChart.vm.onResize();
});
......@@ -181,22 +178,25 @@ describe('Area component', () => {
it('sets area chart width', () => {
expect(areaChart.vm.width).toBe(mockWidth);
});
it('sets area chart height', () => {
expect(areaChart.vm.height).toBe(mockHeight);
});
});
});
describe('computed', () => {
describe('chartData', () => {
let chartData;
const seriesData = () => chartData[0];
beforeEach(() => {
({ chartData } = areaChart.vm);
});
it('utilizes all data points', () => {
expect(Object.keys(areaChart.vm.chartData)).toEqual(['Cores']);
expect(areaChart.vm.chartData.Cores.length).toBe(297);
expect(chartData.length).toBe(1);
expect(seriesData().data.length).toBe(297);
});
it('creates valid data', () => {
const data = areaChart.vm.chartData.Cores;
const { data } = seriesData();
expect(
data.filter(([time, value]) => new Date(time).getTime() > 0 && typeof value === 'number')
......@@ -215,12 +215,6 @@ describe('Area component', () => {
});
});
describe('xAxisLabel', () => {
it('constructs a label for the chart x-axis', () => {
expect(areaChart.vm.xAxisLabel).toBe('Core Usage');
});
});
describe('yAxisLabel', () => {
it('constructs a label for the chart y-axis', () => {
expect(areaChart.vm.yAxisLabel).toBe('CPU');
......
......@@ -663,10 +663,10 @@
resolved "https://registry.yarnpkg.com/@gitlab/svgs/-/svgs-1.54.0.tgz#00320e845efd46716042cde0c348b990d4908daf"
integrity sha512-DR17iy8TM5IbXEacqiDP0p8SuC/J8EL+98xbfVz5BKvRsPHpeZJQNlBF/petIV5d+KWM5A9v3GZTY7uMU7z/JQ==
"@gitlab/ui@^2.2.0":
version "2.2.0"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-2.2.0.tgz#8e384d3fb3d84f2886eacea75feb05e0ea42adcc"
integrity sha512-CCr1CjFyeycm1vrTtRKng5VknWWTN3fFw48YQThz/rgg0viVtA12oqz7oqGGAC+AktnWXtA/cxkXjVNpKTmEpA==
"@gitlab/ui@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@gitlab/ui/-/ui-2.2.1.tgz#4b4f5c9234279a2ebeafc337c58eb6460aef963b"
integrity sha512-hxgZi1KRwd8EmVs1OeE/zcVH3GCE523GK4JWrTD8x9xPRS0O+NSIvbgqGBXp25UiG7jei9Up0X9BzZMTmZimug==
dependencies:
"@babel/standalone" "^7.0.0"
bootstrap-vue "^2.0.0-rc.11"
......
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