summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/monitoring/stores/mutations.js
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets/javascripts/monitoring/stores/mutations.js')
-rw-r--r--app/assets/javascripts/monitoring/stores/mutations.js143
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;
},
};