diff options
author | Sarah Yasonik <syasonik@gitlab.com> | 2019-04-05 20:41:31 +0000 |
---|---|---|
committer | Clement Ho <clemmakesapps@gmail.com> | 2019-04-05 20:41:31 +0000 |
commit | f44737e453a35ef7361cd90af2b5a5ae09473992 (patch) | |
tree | 53a3489b547a4d605a59890e9e9fdd74e809e77a /app | |
parent | a5eb29c16045f5ae96ad107474f8fde974c3197e (diff) | |
download | gitlab-ce-f44737e453a35ef7361cd90af2b5a5ae09473992.tar.gz |
Support multiple queries per chart on metrics dash
Adding support for metrics alerts disabled multiple query support.
To avoid a data model refactor, this enables the visual of multiple
queries per chart on the front end, combining queries based on
metric group, title, and y-axis label.
This also adds support for adding and editing alerts based on the
query selected rather than the single metric associated with the chart.
Diffstat (limited to 'app')
3 files changed, 60 insertions, 13 deletions
diff --git a/app/assets/javascripts/monitoring/components/charts/area.vue b/app/assets/javascripts/monitoring/components/charts/area.vue index b0bbe272d1f..eb2ab3e135e 100644 --- a/app/assets/javascripts/monitoring/components/charts/area.vue +++ b/app/assets/javascripts/monitoring/components/charts/area.vue @@ -42,10 +42,10 @@ export default { required: false, default: () => [], }, - alertData: { - type: Object, + thresholds: { + type: Array, required: false, - default: () => ({}), + default: () => [], }, }, data() { @@ -64,6 +64,9 @@ export default { }, computed: { chartData() { + // Transforms & supplements query data to render appropriate labels & styles + // Input: [{ queryAttributes1 }, { queryAttributes2 }] + // Output: [{ seriesAttributes1 }, { seriesAttributes2 }] return this.graphData.queries.reduce((acc, query) => { const { appearance } = query; const lineType = @@ -121,6 +124,9 @@ export default { }, earliestDatapoint() { return this.chartData.reduce((acc, series) => { + if (!series.data.length) { + return acc; + } const [[timestamp]] = series.data.sort(([a], [b]) => { if (a < b) { return -1; @@ -235,7 +241,7 @@ export default { :data="chartData" :option="chartOptions" :format-tooltip-text="formatTooltipText" - :thresholds="alertData" + :thresholds="thresholds" :width="width" :height="height" @updated="onChartUpdated" diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index ba6a17827f7..f5019bc627e 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -1,5 +1,6 @@ <script> import { GlDropdown, GlDropdownItem } from '@gitlab/ui'; +import _ from 'underscore'; import { s__ } from '~/locale'; import Icon from '~/vue_shared/components/icon.vue'; import '~/vue_shared/mixins/is_ee'; @@ -142,8 +143,13 @@ export default { } }, methods: { - getGraphAlerts(graphId) { - return this.alertData ? this.alertData[graphId] || {} : {}; + getGraphAlerts(queries) { + if (!this.allAlerts) return {}; + const metricIdsForChart = queries.map(q => q.metricId); + return _.pick(this.allAlerts, alert => metricIdsForChart.includes(alert.metricId)); + }, + getGraphAlertValues(queries) { + return Object.values(this.getGraphAlerts(queries)); }, getGraphsData() { this.state = 'loading'; @@ -199,17 +205,15 @@ export default { :key="graphIndex" :graph-data="graphData" :deployment-data="store.deploymentData" - :alert-data="getGraphAlerts(graphData.id)" + :thresholds="getGraphAlertValues(graphData.queries)" :container-width="elWidth" group-id="monitor-area-chart" > <alert-widget - v-if="isEE && prometheusAlertsAvailable && alertsEndpoint && graphData.id" + v-if="isEE && prometheusAlertsAvailable && alertsEndpoint && graphData" :alerts-endpoint="alertsEndpoint" - :label="getGraphLabel(graphData)" - :current-alerts="getQueryAlerts(graphData)" - :custom-metric-id="graphData.id" - :alert-data="alertData[graphData.id]" + :relevant-queries="graphData.queries" + :alerts-to-manage="getGraphAlerts(graphData.queries)" @setAlerts="setAlerts" /> </monitor-area-chart> diff --git a/app/assets/javascripts/monitoring/stores/monitoring_store.js b/app/assets/javascripts/monitoring/stores/monitoring_store.js index 70635059bd9..9761fe168be 100644 --- a/app/assets/javascripts/monitoring/stores/monitoring_store.js +++ b/app/assets/javascripts/monitoring/stores/monitoring_store.js @@ -27,10 +27,47 @@ function removeTimeSeriesNoData(queries) { return queries.reduce((series, query) => series.concat(checkQueryEmptyData(query)), []); } +// Metrics and queries are currently stored 1:1, so `queries` is an array of length one. +// We want to group queries onto a single chart by title & y-axis label. +// This function will no longer be required when metrics:queries are 1:many, +// though there is no consequence if the function stays in use. +// @param metrics [Array<Object>] +// Ex) [ +// { id: 1, title: 'title', y_label: 'MB', queries: [{ ...query1Attrs }] }, +// { id: 2, title: 'title', y_label: 'MB', queries: [{ ...query2Attrs }] }, +// { id: 3, title: 'new title', y_label: 'MB', queries: [{ ...query3Attrs }] } +// ] +// @return [Array<Object>] +// Ex) [ +// { title: 'title', y_label: 'MB', queries: [{ metricId: 1, ...query1Attrs }, +// { metricId: 2, ...query2Attrs }] }, +// { title: 'new title', y_label: 'MB', queries: [{ metricId: 3, ...query3Attrs }]} +// ] +function groupQueriesByChartInfo(metrics) { + const metricsByChart = metrics.reduce((accumulator, metric) => { + const { id, queries, ...chart } = metric; + + const chartKey = `${chart.title}|${chart.y_label}`; + accumulator[chartKey] = accumulator[chartKey] || { ...chart, queries: [] }; + + queries.forEach(queryAttrs => + accumulator[chartKey].queries.push({ metricId: id.toString(), ...queryAttrs }), + ); + + return accumulator; + }, {}); + + return Object.values(metricsByChart); +} + function normalizeMetrics(metrics) { - return metrics.map(metric => { + const groupedMetrics = groupQueriesByChartInfo(metrics); + + return groupedMetrics.map(metric => { const queries = metric.queries.map(query => ({ ...query, + // custom metrics do not require a label, so we should ensure this attribute is defined + label: query.label || metric.y_label, result: query.result.map(result => ({ ...result, values: result.values.map(([timestamp, value]) => [ |