diff options
Diffstat (limited to 'app/assets/javascripts/monitoring/stores/mutations.js')
-rw-r--r-- | app/assets/javascripts/monitoring/stores/mutations.js | 143 |
1 files changed, 116 insertions, 27 deletions
diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js index 696af5aed75..16a34a6c026 100644 --- a/app/assets/javascripts/monitoring/stores/mutations.js +++ b/app/assets/javascripts/monitoring/stores/mutations.js @@ -1,11 +1,85 @@ import Vue from 'vue'; import { slugify } from '~/lib/utils/text_utility'; import * as types from './mutation_types'; -import { normalizeMetrics, normalizeMetric, normalizeQueryResult } from './utils'; +import { normalizeMetric, normalizeQueryResult } from './utils'; +import { BACKOFF_TIMEOUT } from '../../lib/utils/common_utils'; +import { metricStates } from '../constants'; +import httpStatusCodes from '~/lib/utils/http_status'; -const normalizePanel = panel => panel.metrics.map(normalizeMetric); +const normalizePanelMetrics = (metrics, defaultLabel) => + metrics.map(metric => ({ + ...normalizeMetric(metric), + label: metric.label || defaultLabel, + })); + +/** + * Locate and return a metric in the dashboard by its id + * as generated by `uniqMetricsId()`. + * @param {String} metricId Unique id in the dashboard + * @param {Object} dashboard Full dashboard object + */ +const findMetricInDashboard = (metricId, dashboard) => { + let res = null; + dashboard.panel_groups.forEach(group => { + group.panels.forEach(panel => { + panel.metrics.forEach(metric => { + if (metric.metric_id === metricId) { + res = metric; + } + }); + }); + }); + return res; +}; + +/** + * Set a new state for a metric. + * + * Initally metric data is not populated, so `Vue.set` is + * used to add new properties to the metric. + * + * @param {Object} metric - Metric object as defined in the dashboard + * @param {Object} state - New state + * @param {Array|null} state.result - Array of results + * @param {String} state.error - Error code from metricStates + * @param {Boolean} state.loading - True if the metric is loading + */ +const setMetricState = (metric, { result = null, loading = false, state = null }) => { + Vue.set(metric, 'result', result); + Vue.set(metric, 'loading', loading); + Vue.set(metric, 'state', state); +}; + +/** + * Maps a backened error state to a `metricStates` constant + * @param {Object} error - Error from backend response + */ +const emptyStateFromError = error => { + if (!error) { + return metricStates.UNKNOWN_ERROR; + } + + // Special error responses + if (error.message === BACKOFF_TIMEOUT) { + return metricStates.TIMEOUT; + } + + // Axios error responses + const { response } = error; + if (response && response.status === httpStatusCodes.SERVICE_UNAVAILABLE) { + return metricStates.CONNECTION_FAILED; + } else if (response && response.status === httpStatusCodes.BAD_REQUEST) { + // Note: "error.response.data.error" may contain Prometheus error information + return metricStates.BAD_QUERY; + } + + return metricStates.UNKNOWN_ERROR; +}; export default { + /** + * Dashboard panels structure and global state + */ [types.REQUEST_METRICS_DATA](state) { state.emptyState = 'loading'; state.showEmptyState = true; @@ -13,28 +87,18 @@ export default { [types.RECEIVE_METRICS_DATA_SUCCESS](state, groupData) { state.dashboard.panel_groups = groupData.map((group, i) => { const key = `${slugify(group.group || 'default')}-${i}`; - let { metrics = [], panels = [] } = group; + let { panels = [] } = group; // each panel has metric information that needs to be normalized - panels = panels.map(panel => ({ ...panel, - metrics: normalizePanel(panel), - })); - - // for backwards compatibility, and to limit Vue template changes: - // for each group alias panels to metrics - // for each panel alias metrics to queries - metrics = panels.map(panel => ({ - ...panel, - queries: panel.metrics, + metrics: normalizePanelMetrics(panel.metrics, panel.y_label), })); return { ...group, panels, key, - metrics: normalizeMetrics(metrics), }; }); @@ -46,6 +110,10 @@ export default { state.emptyState = error ? 'unableToConnect' : 'noData'; state.showEmptyState = true; }, + + /** + * Deployments and environments + */ [types.RECEIVE_DEPLOYMENTS_DATA_SUCCESS](state, deployments) { state.deploymentData = deployments; }, @@ -58,26 +126,47 @@ export default { [types.RECEIVE_ENVIRONMENTS_DATA_FAILURE](state) { state.environments = []; }, - [types.SET_QUERY_RESULT](state, { metricId, result }) { - if (!metricId || !result || result.length === 0) { + + /** + * Individual panel/metric results + */ + [types.REQUEST_METRIC_RESULT](state, { metricId }) { + const metric = findMetricInDashboard(metricId, state.dashboard); + setMetricState(metric, { + loading: true, + state: metricStates.LOADING, + }); + }, + [types.RECEIVE_METRIC_RESULT_SUCCESS](state, { metricId, result }) { + if (!metricId) { return; } state.showEmptyState = false; - state.dashboard.panel_groups.forEach(group => { - group.metrics.forEach(metric => { - metric.queries.forEach(query => { - if (query.metric_id === metricId) { - state.metricsWithData.push(metricId); - // ensure dates/numbers are correctly formatted for charts - const normalizedResults = result.map(normalizeQueryResult); - Vue.set(query, 'result', Object.freeze(normalizedResults)); - } - }); + const metric = findMetricInDashboard(metricId, state.dashboard); + if (!result || result.length === 0) { + setMetricState(metric, { + state: metricStates.NO_DATA, }); + } else { + const normalizedResults = result.map(normalizeQueryResult); + setMetricState(metric, { + result: Object.freeze(normalizedResults), + state: metricStates.OK, + }); + } + }, + [types.RECEIVE_METRIC_RESULT_FAILURE](state, { metricId, error }) { + if (!metricId) { + return; + } + const metric = findMetricInDashboard(metricId, state.dashboard); + setMetricState(metric, { + state: emptyStateFromError(error), }); }, + [types.SET_ENDPOINTS](state, endpoints) { state.metricsEndpoint = endpoints.metricsEndpoint; state.environmentsEndpoint = endpoints.environmentsEndpoint; @@ -101,6 +190,6 @@ export default { }, [types.SET_PANEL_GROUP_METRICS](state, payload) { const panelGroup = state.dashboard.panel_groups.find(pg => payload.key === pg.key); - panelGroup.metrics = payload.metrics; + panelGroup.panels = payload.panels; }, }; |