summaryrefslogtreecommitdiff
path: root/app/assets/javascripts/monitoring/stores
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-08-20 18:42:06 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-08-20 18:42:06 +0000
commit6e4e1050d9dba2b7b2523fdd1768823ab85feef4 (patch)
tree78be5963ec075d80116a932011d695dd33910b4e /app/assets/javascripts/monitoring/stores
parent1ce776de4ae122aba3f349c02c17cebeaa8ecf07 (diff)
downloadgitlab-ce-6e4e1050d9dba2b7b2523fdd1768823ab85feef4.tar.gz
Add latest changes from gitlab-org/gitlab@13-3-stable-ee
Diffstat (limited to 'app/assets/javascripts/monitoring/stores')
-rw-r--r--app/assets/javascripts/monitoring/stores/actions.js130
-rw-r--r--app/assets/javascripts/monitoring/stores/embed_group/actions.js3
-rw-r--r--app/assets/javascripts/monitoring/stores/embed_group/getters.js3
-rw-r--r--app/assets/javascripts/monitoring/stores/embed_group/mutation_types.js3
-rw-r--r--app/assets/javascripts/monitoring/stores/getters.js3
-rw-r--r--app/assets/javascripts/monitoring/stores/mutation_types.js14
-rw-r--r--app/assets/javascripts/monitoring/stores/mutations.js76
-rw-r--r--app/assets/javascripts/monitoring/stores/state.js13
-rw-r--r--app/assets/javascripts/monitoring/stores/utils.js12
9 files changed, 188 insertions, 69 deletions
diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js
index a441882a47d..16a685305dc 100644
--- a/app/assets/javascripts/monitoring/stores/actions.js
+++ b/app/assets/javascripts/monitoring/stores/actions.js
@@ -1,7 +1,7 @@
import * as Sentry from '@sentry/browser';
import * as types from './mutation_types';
import axios from '~/lib/utils/axios_utils';
-import createFlash from '~/flash';
+import { deprecatedCreateFlash as createFlash } from '~/flash';
import { convertToFixedRange } from '~/lib/utils/datetime_range';
import {
gqClient,
@@ -13,16 +13,14 @@ import trackDashboardLoad from '../monitoring_tracking_helper';
import getEnvironments from '../queries/getEnvironments.query.graphql';
import getAnnotations from '../queries/getAnnotations.query.graphql';
import getDashboardValidationWarnings from '../queries/getDashboardValidationWarnings.query.graphql';
-import statusCodes from '../../lib/utils/http_status';
-import { backOff, convertObjectPropsToCamelCase } from '../../lib/utils/common_utils';
+import { convertObjectPropsToCamelCase } from '../../lib/utils/common_utils';
import { s__, sprintf } from '../../locale';
+import { getDashboard, getPrometheusQueryData } from '../requests';
-import {
- PROMETHEUS_TIMEOUT,
- ENVIRONMENT_AVAILABLE_STATE,
- DEFAULT_DASHBOARD_PATH,
- VARIABLE_TYPES,
-} from '../constants';
+import { ENVIRONMENT_AVAILABLE_STATE, OVERVIEW_DASHBOARD_PATH, VARIABLE_TYPES } from '../constants';
+
+const axiosCancelToken = axios.CancelToken;
+let cancelTokenSource;
function prometheusMetricQueryParams(timeRange) {
const { start, end } = convertToFixedRange(timeRange);
@@ -38,29 +36,18 @@ function prometheusMetricQueryParams(timeRange) {
};
}
-function backOffRequest(makeRequestCallback) {
- return backOff((next, stop) => {
- makeRequestCallback()
- .then(resp => {
- if (resp.status === statusCodes.NO_CONTENT) {
- next();
- } else {
- stop(resp);
- }
- })
- .catch(stop);
- }, PROMETHEUS_TIMEOUT);
-}
-
-function getPrometheusQueryData(prometheusEndpoint, params) {
- return backOffRequest(() => axios.get(prometheusEndpoint, { params }))
- .then(res => res.data)
- .then(response => {
- if (response.status === 'error') {
- throw new Error(response.error);
- }
- return response.data;
- });
+/**
+ * Extract error messages from API or HTTP request errors.
+ *
+ * - API errors are in `error.response.data.message`
+ * - HTTP (axios) errors are in `error.messsage`
+ *
+ * @param {Object} error
+ * @returns {String} User friendly error message
+ */
+function extractErrorMessage(error) {
+ const message = error?.response?.data?.message;
+ return message ?? error.message;
}
// Setup
@@ -126,8 +113,7 @@ export const fetchDashboard = ({ state, commit, dispatch, getters }) => {
params.dashboard = getters.fullDashboardPath;
}
- return backOffRequest(() => axios.get(state.dashboardEndpoint, { params }))
- .then(resp => resp.data)
+ return getDashboard(state.dashboardEndpoint, params)
.then(response => {
dispatch('receiveMetricsDashboardSuccess', { response });
/**
@@ -329,7 +315,7 @@ export const receiveEnvironmentsDataFailure = ({ commit }) => {
export const fetchAnnotations = ({ state, dispatch, getters }) => {
const { start } = convertToFixedRange(state.timeRange);
- const dashboardPath = getters.fullDashboardPath || DEFAULT_DASHBOARD_PATH;
+ const dashboardPath = getters.fullDashboardPath || OVERVIEW_DASHBOARD_PATH;
return gqClient
.mutate({
mutation: getAnnotations,
@@ -362,12 +348,12 @@ export const receiveAnnotationsFailure = ({ commit }) => commit(types.RECEIVE_AN
export const fetchDashboardValidationWarnings = ({ state, dispatch, getters }) => {
/**
- * Normally, the default dashboard won't throw any validation warnings.
+ * Normally, the overview dashboard won't throw any validation warnings.
*
- * However, if a bug sneaks into the default dashboard making it invalid,
+ * However, if a bug sneaks into the overview dashboard making it invalid,
* this might come handy for our clients
*/
- const dashboardPath = getters.fullDashboardPath || DEFAULT_DASHBOARD_PATH;
+ const dashboardPath = getters.fullDashboardPath || OVERVIEW_DASHBOARD_PATH;
return gqClient
.mutate({
mutation: getDashboardValidationWarnings,
@@ -484,12 +470,10 @@ export const fetchVariableMetricLabelValues = ({ state, commit }, { defaultQuery
if (variable.type === VARIABLE_TYPES.metric_label_values) {
const { prometheusEndpointPath, label } = variable.options;
- const optionsRequest = backOffRequest(() =>
- axios.get(prometheusEndpointPath, {
- params: { start_time, end_time },
- }),
- )
- .then(({ data }) => data.data)
+ const optionsRequest = getPrometheusQueryData(prometheusEndpointPath, {
+ start_time,
+ end_time,
+ })
.then(data => {
commit(types.UPDATE_VARIABLE_METRIC_LABEL_VALUES, { variable, label, data });
})
@@ -507,5 +491,59 @@ export const fetchVariableMetricLabelValues = ({ state, commit }, { defaultQuery
return Promise.all(optionsRequests);
};
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
+// Panel Builder
+
+export const setPanelPreviewTimeRange = ({ commit }, timeRange) => {
+ commit(types.SET_PANEL_PREVIEW_TIME_RANGE, timeRange);
+};
+
+export const fetchPanelPreview = ({ state, commit, dispatch }, panelPreviewYml) => {
+ if (!panelPreviewYml) {
+ return null;
+ }
+
+ commit(types.SET_PANEL_PREVIEW_IS_SHOWN, true);
+ commit(types.REQUEST_PANEL_PREVIEW, panelPreviewYml);
+
+ return axios
+ .post(state.panelPreviewEndpoint, { panel_yaml: panelPreviewYml })
+ .then(({ data }) => {
+ commit(types.RECEIVE_PANEL_PREVIEW_SUCCESS, data);
+
+ dispatch('fetchPanelPreviewMetrics');
+ })
+ .catch(error => {
+ commit(types.RECEIVE_PANEL_PREVIEW_FAILURE, extractErrorMessage(error));
+ });
+};
+
+export const fetchPanelPreviewMetrics = ({ state, commit }) => {
+ if (cancelTokenSource) {
+ cancelTokenSource.cancel();
+ }
+ cancelTokenSource = axiosCancelToken.source();
+
+ const defaultQueryParams = prometheusMetricQueryParams(state.panelPreviewTimeRange);
+
+ state.panelPreviewGraphData.metrics.forEach((metric, index) => {
+ commit(types.REQUEST_PANEL_PREVIEW_METRIC_RESULT, { index });
+
+ const params = { ...defaultQueryParams };
+ if (metric.step) {
+ params.step = metric.step;
+ }
+ return getPrometheusQueryData(metric.prometheusEndpointPath, params, {
+ cancelToken: cancelTokenSource.token,
+ })
+ .then(data => {
+ commit(types.RECEIVE_PANEL_PREVIEW_METRIC_RESULT_SUCCESS, { index, data });
+ })
+ .catch(error => {
+ Sentry.captureException(error);
+
+ commit(types.RECEIVE_PANEL_PREVIEW_METRIC_RESULT_FAILURE, { index, error });
+ // Continue to throw error so the panel builder can notify using createFlash
+ throw error;
+ });
+ });
+};
diff --git a/app/assets/javascripts/monitoring/stores/embed_group/actions.js b/app/assets/javascripts/monitoring/stores/embed_group/actions.js
index cbe0950d954..4a7572bdbd9 100644
--- a/app/assets/javascripts/monitoring/stores/embed_group/actions.js
+++ b/app/assets/javascripts/monitoring/stores/embed_group/actions.js
@@ -1,5 +1,4 @@
import * as types from './mutation_types';
+// eslint-disable-next-line import/prefer-default-export
export const addModule = ({ commit }, data) => commit(types.ADD_MODULE, data);
-
-export default () => {};
diff --git a/app/assets/javascripts/monitoring/stores/embed_group/getters.js b/app/assets/javascripts/monitoring/stores/embed_group/getters.js
index 9b08cf762c1..096d8d03096 100644
--- a/app/assets/javascripts/monitoring/stores/embed_group/getters.js
+++ b/app/assets/javascripts/monitoring/stores/embed_group/getters.js
@@ -1,4 +1,3 @@
+// eslint-disable-next-line import/prefer-default-export
export const metricsWithData = (state, getters, rootState, rootGetters) =>
state.modules.map(module => rootGetters[`${module}/metricsWithData`]().length);
-
-export default () => {};
diff --git a/app/assets/javascripts/monitoring/stores/embed_group/mutation_types.js b/app/assets/javascripts/monitoring/stores/embed_group/mutation_types.js
index e7a425d3623..7fd3f0f8647 100644
--- a/app/assets/javascripts/monitoring/stores/embed_group/mutation_types.js
+++ b/app/assets/javascripts/monitoring/stores/embed_group/mutation_types.js
@@ -1,3 +1,2 @@
+// eslint-disable-next-line import/prefer-default-export
export const ADD_MODULE = 'ADD_MODULE';
-
-export default () => {};
diff --git a/app/assets/javascripts/monitoring/stores/getters.js b/app/assets/javascripts/monitoring/stores/getters.js
index 3aa711a0509..8ed83cf02fe 100644
--- a/app/assets/javascripts/monitoring/stores/getters.js
+++ b/app/assets/javascripts/monitoring/stores/getters.js
@@ -170,6 +170,3 @@ export const getCustomVariablesParams = state =>
*/
export const fullDashboardPath = state =>
normalizeCustomDashboardPath(state.currentDashboard, state.customDashboardBasePath);
-
-// prevent babel-plugin-rewire from generating an invalid default during karma tests
-export default () => {};
diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js
index d408628fc4d..1d7279912cc 100644
--- a/app/assets/javascripts/monitoring/stores/mutation_types.js
+++ b/app/assets/javascripts/monitoring/stores/mutation_types.js
@@ -46,3 +46,17 @@ export const SET_SHOW_ERROR_BANNER = 'SET_SHOW_ERROR_BANNER';
export const SET_PANEL_GROUP_METRICS = 'SET_PANEL_GROUP_METRICS';
export const SET_ENVIRONMENTS_FILTER = 'SET_ENVIRONMENTS_FILTER';
export const SET_EXPANDED_PANEL = 'SET_EXPANDED_PANEL';
+
+// Panel preview
+export const REQUEST_PANEL_PREVIEW = 'REQUEST_PANEL_PREVIEW';
+export const RECEIVE_PANEL_PREVIEW_SUCCESS = 'RECEIVE_PANEL_PREVIEW_SUCCESS';
+export const RECEIVE_PANEL_PREVIEW_FAILURE = 'RECEIVE_PANEL_PREVIEW_FAILURE';
+
+export const REQUEST_PANEL_PREVIEW_METRIC_RESULT = 'REQUEST_PANEL_PREVIEW_METRIC_RESULT';
+export const RECEIVE_PANEL_PREVIEW_METRIC_RESULT_SUCCESS =
+ 'RECEIVE_PANEL_PREVIEW_METRIC_RESULT_SUCCESS';
+export const RECEIVE_PANEL_PREVIEW_METRIC_RESULT_FAILURE =
+ 'RECEIVE_PANEL_PREVIEW_METRIC_RESULT_FAILURE';
+
+export const SET_PANEL_PREVIEW_TIME_RANGE = 'SET_PANEL_PREVIEW_TIME_RANGE';
+export const SET_PANEL_PREVIEW_IS_SHOWN = 'SET_PANEL_PREVIEW_IS_SHOWN';
diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js
index 744441c8935..09a5861b475 100644
--- a/app/assets/javascripts/monitoring/stores/mutations.js
+++ b/app/assets/javascripts/monitoring/stores/mutations.js
@@ -1,9 +1,9 @@
import Vue from 'vue';
import { pick } from 'lodash';
import * as types from './mutation_types';
-import { mapToDashboardViewModel, normalizeQueryResponseData } from './utils';
+import { mapToDashboardViewModel, mapPanelToViewModel, normalizeQueryResponseData } from './utils';
import httpStatusCodes from '~/lib/utils/http_status';
-import { BACKOFF_TIMEOUT } from '../../lib/utils/common_utils';
+import { BACKOFF_TIMEOUT } from '~/lib/utils/common_utils';
import { dashboardEmptyStates, endpointKeys, initialStateKeys, metricStates } from '../constants';
import { optionsFromSeriesData } from './variable_mapping';
@@ -53,6 +53,14 @@ const emptyStateFromError = error => {
return metricStates.UNKNOWN_ERROR;
};
+export const metricStateFromData = data => {
+ if (data?.result?.length) {
+ const result = normalizeQueryResponseData(data);
+ return { state: metricStates.OK, result: Object.freeze(result) };
+ }
+ return { state: metricStates.NO_DATA, result: null };
+};
+
export default {
/**
* Dashboard panels structure and global state
@@ -154,17 +162,11 @@ export default {
},
[types.RECEIVE_METRIC_RESULT_SUCCESS](state, { metricId, data }) {
const metric = findMetricInDashboard(metricId, state.dashboard);
- metric.loading = false;
+ const metricState = metricStateFromData(data);
- if (!data.result || data.result.length === 0) {
- metric.state = metricStates.NO_DATA;
- metric.result = null;
- } else {
- const result = normalizeQueryResponseData(data);
-
- metric.state = metricStates.OK;
- metric.result = Object.freeze(result);
- }
+ metric.loading = false;
+ metric.state = metricState.state;
+ metric.result = metricState.result;
},
[types.RECEIVE_METRIC_RESULT_FAILURE](state, { metricId, error }) {
const metric = findMetricInDashboard(metricId, state.dashboard);
@@ -218,4 +220,54 @@ export default {
// Add new options with assign to ensure Vue reactivity
Object.assign(variable.options, { values });
},
+
+ [types.REQUEST_PANEL_PREVIEW](state, panelPreviewYml) {
+ state.panelPreviewIsLoading = true;
+
+ state.panelPreviewYml = panelPreviewYml;
+ state.panelPreviewGraphData = null;
+ state.panelPreviewError = null;
+ },
+ [types.RECEIVE_PANEL_PREVIEW_SUCCESS](state, payload) {
+ state.panelPreviewIsLoading = false;
+
+ state.panelPreviewGraphData = mapPanelToViewModel(payload);
+ state.panelPreviewError = null;
+ },
+ [types.RECEIVE_PANEL_PREVIEW_FAILURE](state, error) {
+ state.panelPreviewIsLoading = false;
+
+ state.panelPreviewGraphData = null;
+ state.panelPreviewError = error;
+ },
+
+ [types.REQUEST_PANEL_PREVIEW_METRIC_RESULT](state, { index }) {
+ const metric = state.panelPreviewGraphData.metrics[index];
+
+ metric.loading = true;
+ if (!metric.result) {
+ metric.state = metricStates.LOADING;
+ }
+ },
+ [types.RECEIVE_PANEL_PREVIEW_METRIC_RESULT_SUCCESS](state, { index, data }) {
+ const metric = state.panelPreviewGraphData.metrics[index];
+ const metricState = metricStateFromData(data);
+
+ metric.loading = false;
+ metric.state = metricState.state;
+ metric.result = metricState.result;
+ },
+ [types.RECEIVE_PANEL_PREVIEW_METRIC_RESULT_FAILURE](state, { index, error }) {
+ const metric = state.panelPreviewGraphData.metrics[index];
+
+ metric.loading = false;
+ metric.state = emptyStateFromError(error);
+ metric.result = null;
+ },
+ [types.SET_PANEL_PREVIEW_TIME_RANGE](state, timeRange) {
+ state.panelPreviewTimeRange = timeRange;
+ },
+ [types.SET_PANEL_PREVIEW_IS_SHOWN](state, isPreviewShown) {
+ state.panelPreviewIsShown = isPreviewShown;
+ },
};
diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js
index 89738756ffe..ef8b1adb624 100644
--- a/app/assets/javascripts/monitoring/stores/state.js
+++ b/app/assets/javascripts/monitoring/stores/state.js
@@ -1,12 +1,14 @@
import invalidUrl from '~/lib/utils/invalid_url';
import { timezones } from '../format_date';
import { dashboardEmptyStates } from '../constants';
+import { defaultTimeRange } from '~/vue_shared/constants';
export default () => ({
// API endpoints
deploymentsEndpoint: null,
dashboardEndpoint: invalidUrl,
dashboardsEndpoint: invalidUrl,
+ panelPreviewEndpoint: invalidUrl,
// Dashboard request parameters
timeRange: null,
@@ -59,6 +61,15 @@ export default () => ({
* via the dashboard yml file.
*/
links: [],
+
+ // Panel editor / builder
+ panelPreviewYml: '',
+ panelPreviewIsLoading: false,
+ panelPreviewGraphData: null,
+ panelPreviewError: null,
+ panelPreviewTimeRange: defaultTimeRange,
+ panelPreviewIsShown: false,
+
// Other project data
dashboardTimezone: timezones.LOCAL,
annotations: [],
@@ -69,9 +80,11 @@ export default () => ({
currentEnvironmentName: null,
// GitLab paths to other pages
+ externalDashboardUrl: '',
projectPath: null,
operationsSettingsPath: '',
logsPath: invalidUrl,
+ addDashboardDocumentationPath: '',
// static paths
customDashboardBasePath: '',
diff --git a/app/assets/javascripts/monitoring/stores/utils.js b/app/assets/javascripts/monitoring/stores/utils.js
index 51562593ee8..df7f22e622f 100644
--- a/app/assets/javascripts/monitoring/stores/utils.js
+++ b/app/assets/javascripts/monitoring/stores/utils.js
@@ -176,7 +176,11 @@ export const mapPanelToViewModel = ({
field,
metrics = [],
links = [],
+ min_value,
max_value,
+ split,
+ thresholds,
+ format,
}) => {
// Both `x_axis.name` and `x_label` are supported for now
// https://gitlab.com/gitlab-org/gitlab/issues/210521
@@ -195,7 +199,11 @@ export const mapPanelToViewModel = ({
yAxis,
xAxis,
field,
+ minValue: min_value,
maxValue: max_value,
+ split,
+ thresholds,
+ format,
links: links.map(mapLinksToViewModel),
metrics: mapToMetricsViewModel(metrics),
};
@@ -465,9 +473,9 @@ export const addPrefixToCustomVariableParams = name => `variables[${name}]`;
* metrics dashboard to work with custom dashboard file names instead
* of the entire path.
*
- * If dashboard is empty, it is the default dashboard.
+ * If dashboard is empty, it is the overview dashboard.
* If dashboard is set, it usually is a custom dashboard unless
- * explicitly it is set to default dashboard path.
+ * explicitly it is set to overview dashboard path.
*
* @param {String} dashboard dashboard path
* @param {String} dashboardPrefix custom dashboard directory prefix