Commit 7f86172f authored by Mike Greiling's avatar Mike Greiling

Merge branch 'backport-ee-6036-graphalerts' into 'master'

Backport changes from gitlab-ee!7538

See merge request gitlab-org/gitlab-ce!22093
parents aea6b468 deb6b429
......@@ -82,6 +82,7 @@ export default {
showFlag: false,
showFlagContent: false,
timeSeries: [],
graphDrawData: {},
realPixelRatio: 1,
seriesUnderMouse: [],
};
......@@ -180,12 +181,12 @@ export default {
});
},
renderAxesPaths() {
this.timeSeries = createTimeSeries(
({ timeSeries: this.timeSeries, graphDrawData: this.graphDrawData } = createTimeSeries(
this.graphData.queries,
this.graphWidth,
this.graphHeight,
this.graphHeightOffset,
);
));
if (_.findWhere(this.timeSeries, { renderCanary: true })) {
this.timeSeries = this.timeSeries.map(series => ({ ...series, renderCanary: true }));
......@@ -288,6 +289,10 @@ export default {
:viewBox="innerViewBox"
class="graph-data"
>
<slot
name="additionalSvgContent"
:graphDrawData="graphDrawData"
/>
<graph-path
v-for="(path, index) in timeSeries"
:key="index"
......
......@@ -30,7 +30,7 @@ const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
const defaultStyleOrder = ['solid', 'dashed', 'dotted'];
function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle) {
function queryTimeSeries(query, graphDrawData, lineStyle) {
let usedColors = [];
let renderCanary = false;
const timeSeriesParsed = [];
......@@ -64,7 +64,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
// but we need a regularly-spaced set of time/value pairs
// this gives us a complete range of one minute intervals
// 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 datesWithoutGaps = d3.timeSecond.every(60)
.range(d3.timeMinute.offset(minX, -1), maxX)
......@@ -84,31 +84,6 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
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 seriesCustomizationData =
query.series != null && _.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
......@@ -144,10 +119,10 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
}));
timeSeriesParsed.push({
linePath: lineFunction(values),
areaPath: areaFunction(values),
timeSeriesScaleX,
timeSeriesScaleY,
linePath: graphDrawData.lineFunction(values),
areaPath: graphDrawData.areaBelowLine(values),
timeSeriesScaleX: graphDrawData.timeSeriesScaleX,
timeSeriesScaleY: graphDrawData.timeSeriesScaleY,
values: timeSeries.values,
max: maximumValue,
average: accum / timeSeries.values.length,
......@@ -164,7 +139,7 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
return timeSeriesParsed;
}
export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
function xyDomain(queries) {
const allValues = queries.reduce(
(allQueryResults, query) =>
allQueryResults.concat(
......@@ -176,10 +151,70 @@ export default function createTimeSeries(queries, graphWidth, graphHeight, graph
const xDom = d3.extent(allValues, d => d.time);
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];
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';
import axios from '~/lib/utils/axios_utils';
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', () => {
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(() => {
setFixtures(`
<div class="prometheus-graphs"></div>
<div class="nav-sidebar"></div>
<div class="nav-sidebar"></div>
`);
DashboardComponent = Vue.extend(Dashboard);
});
......
......@@ -8,7 +8,7 @@ const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeri
const defaultValuesComponent = {};
const timeSeries = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
const { timeSeries } = createTimeSeries(convertedMetrics[0].queries, 500, 300, 120);
defaultValuesComponent.timeSeries = timeSeries;
......
......@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data';
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', () => {
let vm;
......
......@@ -5,7 +5,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from '../mock_data';
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', () => {
let vm;
......
......@@ -13,7 +13,7 @@ const createComponent = (propsData) => {
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];
describe('Monitoring Paths', () => {
......
......@@ -8,6 +8,7 @@ export const metricsGroupsAPIResponse = {
priority: 1,
metrics: [
{
id: 5,
title: 'Memory usage',
weight: 1,
queries: [
......
......@@ -2,7 +2,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data';
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];
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