Commit deb6b429 authored by Simon Knox's avatar Simon Knox Committed by Mike Greiling

Backport changes from gitlab-ee!7538

parent aea6b468
...@@ -82,6 +82,7 @@ export default { ...@@ -82,6 +82,7 @@ export default {
showFlag: false, showFlag: false,
showFlagContent: false, showFlagContent: false,
timeSeries: [], timeSeries: [],
graphDrawData: {},
realPixelRatio: 1, realPixelRatio: 1,
seriesUnderMouse: [], seriesUnderMouse: [],
}; };
...@@ -180,12 +181,12 @@ export default { ...@@ -180,12 +181,12 @@ export default {
}); });
}, },
renderAxesPaths() { renderAxesPaths() {
this.timeSeries = createTimeSeries( ({ timeSeries: this.timeSeries, graphDrawData: this.graphDrawData } = createTimeSeries(
this.graphData.queries, this.graphData.queries,
this.graphWidth, this.graphWidth,
this.graphHeight, this.graphHeight,
this.graphHeightOffset, this.graphHeightOffset,
); ));
if (_.findWhere(this.timeSeries, { renderCanary: true })) { if (_.findWhere(this.timeSeries, { renderCanary: true })) {
this.timeSeries = this.timeSeries.map(series => ({ ...series, renderCanary: true })); this.timeSeries = this.timeSeries.map(series => ({ ...series, renderCanary: true }));
...@@ -288,6 +289,10 @@ export default { ...@@ -288,6 +289,10 @@ export default {
:viewBox="innerViewBox" :viewBox="innerViewBox"
class="graph-data" class="graph-data"
> >
<slot
name="additionalSvgContent"
:graphDrawData="graphDrawData"
/>
<graph-path <graph-path
v-for="(path, index) in timeSeries" v-for="(path, index) in timeSeries"
:key="index" :key="index"
......
...@@ -30,7 +30,7 @@ const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple']; ...@@ -30,7 +30,7 @@ const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
const defaultStyleOrder = ['solid', 'dashed', 'dotted']; const defaultStyleOrder = ['solid', 'dashed', 'dotted'];
function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle) { function queryTimeSeries(query, graphDrawData, lineStyle) {
let usedColors = []; let usedColors = [];
let renderCanary = false; let renderCanary = false;
const timeSeriesParsed = []; const timeSeriesParsed = [];
...@@ -64,7 +64,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom ...@@ -64,7 +64,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
// but we need a regularly-spaced set of time/value pairs // but we need a regularly-spaced set of time/value pairs
// this gives us a complete range of one minute intervals // this gives us a complete range of one minute intervals
// offset the same amount as the original data // offset the same amount as the original data
const [minX, maxX] = xDom; const [minX, maxX] = graphDrawData.xDom;
const offset = d3.timeMinute(minX) - Number(minX); const offset = d3.timeMinute(minX) - Number(minX);
const datesWithoutGaps = d3.timeSecond.every(60) const datesWithoutGaps = d3.timeSecond.every(60)
.range(d3.timeMinute.offset(minX, -1), maxX) .range(d3.timeMinute.offset(minX, -1), maxX)
...@@ -84,31 +84,6 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom ...@@ -84,31 +84,6 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
renderCanary = true; renderCanary = true;
} }
const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]);
const timeSeriesScaleY = d3.scaleLinear().range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(xDom);
timeSeriesScaleX.ticks(d3.timeMinute, 60);
timeSeriesScaleY.domain(yDom);
const defined = d => !Number.isNaN(d.value) && d.value != null;
const lineFunction = d3
.line()
.defined(defined)
.curve(d3.curveLinear) // d3 v4 uses curbe instead of interpolate
.x(d => timeSeriesScaleX(d.time))
.y(d => timeSeriesScaleY(d.value));
const areaFunction = d3
.area()
.defined(defined)
.curve(d3.curveLinear)
.x(d => timeSeriesScaleX(d.time))
.y0(graphHeight - graphHeightOffset)
.y1(d => timeSeriesScaleY(d.value));
const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]]; const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData = const seriesCustomizationData =
query.series != null && _.findWhere(query.series[0].when, { value: timeSeriesMetricLabel }); query.series != null && _.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
...@@ -144,10 +119,10 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom ...@@ -144,10 +119,10 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
})); }));
timeSeriesParsed.push({ timeSeriesParsed.push({
linePath: lineFunction(values), linePath: graphDrawData.lineFunction(values),
areaPath: areaFunction(values), areaPath: graphDrawData.areaBelowLine(values),
timeSeriesScaleX, timeSeriesScaleX: graphDrawData.timeSeriesScaleX,
timeSeriesScaleY, timeSeriesScaleY: graphDrawData.timeSeriesScaleY,
values: timeSeries.values, values: timeSeries.values,
max: maximumValue, max: maximumValue,
average: accum / timeSeries.values.length, average: accum / timeSeries.values.length,
...@@ -164,7 +139,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom ...@@ -164,7 +139,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
return timeSeriesParsed; return timeSeriesParsed;
} }
export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) { function xyDomain(queries) {
const allValues = queries.reduce( const allValues = queries.reduce(
(allQueryResults, query) => (allQueryResults, query) =>
allQueryResults.concat( allQueryResults.concat(
...@@ -176,10 +151,70 @@ export default function createTimeSeries(queries, graphWidth, graphHeight, graph ...@@ -176,10 +151,70 @@ export default function createTimeSeries(queries, graphWidth, graphHeight, graph
const xDom = d3.extent(allValues, d => d.time); const xDom = d3.extent(allValues, d => d.time);
const yDom = [0, d3.max(allValues.map(d => d.value))]; const yDom = [0, d3.max(allValues.map(d => d.value))];
return queries.reduce((series, query, index) => { return {
xDom,
yDom,
};
}
export function generateGraphDrawData(queries, graphWidth, graphHeight, graphHeightOffset) {
const { xDom, yDom } = xyDomain(queries);
const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]);
const timeSeriesScaleY = d3.scaleLinear().range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(xDom);
timeSeriesScaleX.ticks(d3.timeMinute, 60);
timeSeriesScaleY.domain(yDom);
const defined = d => !Number.isNaN(d.value) && d.value != null;
const lineFunction = d3
.line()
.defined(defined)
.curve(d3.curveLinear) // d3 v4 uses curbe instead of interpolate
.x(d => timeSeriesScaleX(d.time))
.y(d => timeSeriesScaleY(d.value));
const areaBelowLine = d3
.area()
.defined(defined)
.curve(d3.curveLinear)
.x(d => timeSeriesScaleX(d.time))
.y0(graphHeight - graphHeightOffset)
.y1(d => timeSeriesScaleY(d.value));
const areaAboveLine = d3
.area()
.defined(defined)
.curve(d3.curveLinear)
.x(d => timeSeriesScaleX(d.time))
.y0(0)
.y1(d => timeSeriesScaleY(d.value));
return {
lineFunction,
areaBelowLine,
areaAboveLine,
xDom,
yDom,
timeSeriesScaleX,
timeSeriesScaleY,
};
}
export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
const graphDrawData = generateGraphDrawData(queries, graphWidth, graphHeight, graphHeightOffset);
const timeSeries = queries.reduce((series, query, index) => {
const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length]; const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length];
return series.concat( return series.concat(
queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle), queryTimeSeries(query, graphDrawData, lineStyle),
); );
}, []); }, []);
return {
timeSeries,
graphDrawData,
};
} }
...@@ -4,30 +4,32 @@ import Dashboard from '~/monitoring/components/dashboard.vue'; ...@@ -4,30 +4,32 @@ import Dashboard from '~/monitoring/components/dashboard.vue';
import axios from '~/lib/utils/axios_utils'; import axios from '~/lib/utils/axios_utils';
import { metricsGroupsAPIResponse, mockApiEndpoint, environmentData } from './mock_data'; import { metricsGroupsAPIResponse, mockApiEndpoint, environmentData } from './mock_data';
const propsData = {
hasMetrics: false,
documentationPath: '/path/to/docs',
settingsPath: '/path/to/settings',
clustersPath: '/path/to/clusters',
tagsPath: '/path/to/tags',
projectPath: '/path/to/project',
metricsEndpoint: mockApiEndpoint,
deploymentEndpoint: null,
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyNoDataSvgPath: '/path/to/no-data.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
environmentsEndpoint: '/root/hello-prometheus/environments/35',
currentEnvironmentName: 'production',
};
export default propsData;
describe('Dashboard', () => { describe('Dashboard', () => {
let DashboardComponent; let DashboardComponent;
const propsData = {
hasMetrics: false,
documentationPath: '/path/to/docs',
settingsPath: '/path/to/settings',
clustersPath: '/path/to/clusters',
tagsPath: '/path/to/tags',
projectPath: '/path/to/project',
metricsEndpoint: mockApiEndpoint,
deploymentEndpoint: null,
emptyGettingStartedSvgPath: '/path/to/getting-started.svg',
emptyLoadingSvgPath: '/path/to/loading.svg',
emptyNoDataSvgPath: '/path/to/no-data.svg',
emptyUnableToConnectSvgPath: '/path/to/unable-to-connect.svg',
environmentsEndpoint: '/root/hello-prometheus/environments/35',
currentEnvironmentName: 'production',
};
beforeEach(() => { beforeEach(() => {
setFixtures(` setFixtures(`
<div class="prometheus-graphs"></div> <div class="prometheus-graphs"></div>
<div class="nav-sidebar"></div> <div class="nav-sidebar"></div>
`); `);
DashboardComponent = Vue.extend(Dashboard); DashboardComponent = Vue.extend(Dashboard);
}); });
......
...@@ -8,7 +8,7 @@ const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeri ...@@ -8,7 +8,7 @@ const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeri
const defaultValuesComponent = {}; const defaultValuesComponent = {};
const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120); const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
defaultValuesComponent.timeSeries = timeSeries; defaultValuesComponent.timeSeries = timeSeries;
......
...@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series'; ...@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data'; import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120); const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
describe('TrackInfo component', () => { describe('TrackInfo component', () => {
let vm; let vm;
......
...@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series'; ...@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data'; import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120); const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
describe('TrackLine component', () => { describe('TrackLine component', () => {
let vm; let vm;
......
...@@ -13,7 +13,7 @@ const createComponent = (propsData) => { ...@@ -13,7 +13,7 @@ const createComponent = (propsData) => {
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const timeSeries = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120); const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
const firstTimeSeries = timeSeries[0]; const firstTimeSeries = timeSeries[0];
describe('Monitoring Paths', () => { describe('Monitoring Paths', () => {
......
...@@ -8,6 +8,7 @@ export const metricsGroupsAPIResponse = { ...@@ -8,6 +8,7 @@ export const metricsGroupsAPIResponse = {
priority: 1, priority: 1,
metrics: [ metrics: [
{ {
id: 5,
title: 'Memory usage', title: 'Memory usage',
weight: 1, weight: 1,
queries: [ queries: [
......
...@@ -2,7 +2,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series'; ...@@ -2,7 +2,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data'; import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const timeSeries = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120); const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
const firstTimeSeries = timeSeries[0]; const firstTimeSeries = timeSeries[0];
describe('Multiple time series', () => { describe('Multiple time series', () => {
......
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