From 3b8c905cf314d451dadcc68b0104dd1b2384c2af Mon Sep 17 00:00:00 2001 From: Simon Knox Date: Thu, 2 May 2019 12:15:24 +1000 Subject: Remove other chart types for now so area-chart displays When there is a v-if inside a v-for things do not go well --- .../monitoring/components/dashboard.vue | 22 +---- .../javascripts/monitoring/stores/actions.js | 19 ++-- .../javascripts/monitoring/stores/getters.js | 106 ++++++++++++++++++--- .../javascripts/monitoring/stores/mutations.js | 10 +- 4 files changed, 111 insertions(+), 46 deletions(-) diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 0a33fab99e5..294df5dc311 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -198,7 +198,8 @@ export default { this.showEmptyState = false; }) - .catch(() => { + .catch((e) => { + console.error(e) this.state = 'unableToConnect'; }); }, @@ -279,7 +280,6 @@ export default { > diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index 29a7cbca669..32c86c32d5d 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -94,7 +94,7 @@ export const fetchPrometheusMetrics = ({ state, dispatch }) => { * * @param {metric} metric */ -export const fetchPrometheusMetric = ({ commit }, metric) => { +export const fetchPrometheusMetric = ({ commit, getters }, metric) => { const queryType = Object.keys(metric).find(key => ['query', 'query_range'].includes(key)); const query = metric[queryType]; // TODO don't hardcode @@ -116,6 +116,12 @@ export const fetchPrometheusMetric = ({ commit }, metric) => { step, }; + prom(prometheusEndpoint, params).then(result => { + commit(types.SET_QUERY_RESULT, { metricId: metric.metric_id, result }); + }) +} + +function prom(prometheusEndpoint, params) { return backOffRequest(() => axios.get(prometheusEndpoint, { params })) .then(res => res.data) .then(response => { @@ -124,18 +130,11 @@ export const fetchPrometheusMetric = ({ commit }, metric) => { } const { resultType, result } = response.data; - + if (resultType === 'matrix') { if (result.length > 0) { - // TODO: maybe use Object.freeze here since results don't need to be reactive - commit(types.SET_QUERY_RESULT, { metricId: metric.metric_id, result }); + return result } } - // .then(res => { - // if (res.resultType === 'matrix') { - // if (res.result.length > 0) { - // panel.queries[0].result = res.result; - // panel.queries[0].metricId = panel.queries[0].metric_id; - }); } diff --git a/app/assets/javascripts/monitoring/stores/getters.js b/app/assets/javascripts/monitoring/stores/getters.js index 045356adc44..c106d5fe6b3 100644 --- a/app/assets/javascripts/monitoring/stores/getters.js +++ b/app/assets/javascripts/monitoring/stores/getters.js @@ -1,20 +1,104 @@ +import _ from 'underscore'; + +function sortMetrics(metrics) { + return _.chain(metrics) + .sortBy('title') + .sortBy('weight') + .value(); +} + +function checkQueryEmptyData(query) { + return { + ...query, + result: query.result.filter(timeSeries => { + const newTimeSeries = timeSeries; + const hasValue = series => + !Number.isNaN(series[1]) && (series[1] !== null || series[1] !== undefined); + const hasNonNullValue = timeSeries.values.find(hasValue); + + newTimeSeries.values = hasNonNullValue ? newTimeSeries.values : []; + + return newTimeSeries.values.length > 0; + }), + }; +} + +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] +// 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] +// 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 { queries, ...chart } = metric; + const metricId = chart.id ? chart.id.toString() : null; + + const chartKey = `${chart.title}|${chart.y_label}`; + accumulator[chartKey] = accumulator[chartKey] || { ...chart, queries: [] }; + + queries.forEach(queryAttrs => accumulator[chartKey].queries.push({ metricId, ...queryAttrs })); + + return accumulator; + }, {}); + + return Object.values(metricsByChart); +} + +function normalizeMetrics(metrics) { + 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]) => [ + new Date(timestamp * 1000).toISOString(), + Number(value), + ]), + })), + })); + + return { + ...metric, + queries: removeTimeSeriesNoData(queries), + }; + }); +} + export const groups = state => { - return state.groups.map(group => { + return state.groups.reduce((acc, group) => { group.panels.forEach(panel => { panel.queries = panel.metrics; panel.queries.forEach(metric => { const metricId = metric.metric_id; const result = state.queryResults[metricId]; - metric.result = result; + metric.result = result || []; }); }); - return group; - }) - - // panel.queries[0].result - - // queryResults: { - // metricId - // result - // } + + const metrics = normalizeMetrics(sortMetrics(group.panels)); + + return acc.concat({ + ...group, + metrics, + }); + }, []); } diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js index 05f273355f1..3cb370dda6a 100644 --- a/app/assets/javascripts/monitoring/stores/mutations.js +++ b/app/assets/javascripts/monitoring/stores/mutations.js @@ -110,12 +110,12 @@ export default { state.metricsEndpoint = endpoint; }, [types.SET_QUERY_RESULT](state, { metricId, result }) { - Vue.set(state.queryResults, metricId, result) + if (!metricId || !result) { + return; + } + Vue.set(state.queryResults, metricId, Object.freeze(result)); }, [types.SET_GROUPS](state, groups) { - state.groups = groups.map(group => ({ - ...group, - // metrics: normalizeMetrics(sortMetrics(group.metrics)), - })); + state.groups = groups; } }; -- cgit v1.2.1