diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-07 12:09:13 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-07 12:09:13 +0000 |
commit | 211a8c3361ccf4eb92f36edbdcf15c98fcdcc8b7 (patch) | |
tree | 0ad37172721a39b0d57240bb1b4e70f200a0d93e /app/assets/javascripts/monitoring | |
parent | 456a7247f9e88fc2518b69a1a00e905c6db6d775 (diff) | |
download | gitlab-ce-211a8c3361ccf4eb92f36edbdcf15c98fcdcc8b7.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app/assets/javascripts/monitoring')
9 files changed, 130 insertions, 33 deletions
diff --git a/app/assets/javascripts/monitoring/components/charts/time_series.vue b/app/assets/javascripts/monitoring/components/charts/time_series.vue index e58d9f23201..8b2c5e44bb5 100644 --- a/app/assets/javascripts/monitoring/components/charts/time_series.vue +++ b/app/assets/javascripts/monitoring/components/charts/time_series.vue @@ -1,5 +1,5 @@ <script> -import { omit } from 'lodash'; +import { omit, throttle } from 'lodash'; import { GlLink, GlButton, GlTooltip, GlResizeObserverDirective } from '@gitlab/ui'; import { GlAreaChart, GlLineChart, GlChartSeriesLabel } from '@gitlab/ui/dist/charts'; import dateFormat from 'dateformat'; @@ -18,6 +18,13 @@ import { import { makeDataSeries } from '~/helpers/monitor_helper'; import { graphDataValidatorForValues } from '../../utils'; +const THROTTLED_DATAZOOM_WAIT = 1000; // miliseconds +const timestampToISODate = timestamp => new Date(timestamp).toISOString(); + +const events = { + datazoom: 'datazoom', +}; + export default { components: { GlAreaChart, @@ -98,6 +105,7 @@ export default { height: chartHeight, svgs: {}, primaryColor: null, + throttledDatazoom: null, }; }, computed: { @@ -245,6 +253,11 @@ export default { this.setSvg('rocket'); this.setSvg('scroll-handle'); }, + destroyed() { + if (this.throttledDatazoom) { + this.throttledDatazoom.cancel(); + } + }, methods: { formatLegendLabel(query) { return `${query.label}`; @@ -287,8 +300,39 @@ export default { console.error('SVG could not be rendered correctly: ', e); }); }, - onChartUpdated(chart) { - [this.primaryColor] = chart.getOption().color; + onChartUpdated(eChart) { + [this.primaryColor] = eChart.getOption().color; + }, + + onChartCreated(eChart) { + // Emit a datazoom event that corresponds to the eChart + // `datazoom` event. + + if (this.throttledDatazoom) { + // Chart can be created multiple times in this component's + // lifetime, remove previous handlers every time + // chart is created. + this.throttledDatazoom.cancel(); + } + + // Emitting is throttled to avoid flurries of calls when + // the user changes or scrolls the zoom bar. + this.throttledDatazoom = throttle( + () => { + const { startValue, endValue } = eChart.getOption().dataZoom[0]; + this.$emit(events.datazoom, { + start: timestampToISODate(startValue), + end: timestampToISODate(endValue), + }); + }, + THROTTLED_DATAZOOM_WAIT, + { + leading: false, + }, + ); + + eChart.off('datazoom'); + eChart.on('datazoom', this.throttledDatazoom); }, onResize() { if (!this.$refs.chart) return; @@ -331,6 +375,7 @@ export default { :height="height" :average-text="legendAverageText" :max-text="legendMaxText" + @created="onChartCreated" @updated="onChartUpdated" > <template v-if="tooltip.isDeployment"> diff --git a/app/assets/javascripts/monitoring/components/dashboard.vue b/app/assets/javascripts/monitoring/components/dashboard.vue index 391cd6dd15e..79f32b357fc 100644 --- a/app/assets/javascripts/monitoring/components/dashboard.vue +++ b/app/assets/javascripts/monitoring/components/dashboard.vue @@ -21,7 +21,6 @@ import createFlash from '~/flash'; import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { mergeUrlParams, redirectTo } from '~/lib/utils/url_utility'; import invalidUrl from '~/lib/utils/invalid_url'; -import { convertToFixedRange } from '~/lib/utils/datetime_range'; import Icon from '~/vue_shared/components/icon.vue'; import DateTimePicker from '~/vue_shared/components/date_time_picker/date_time_picker.vue'; @@ -102,6 +101,11 @@ export default { type: String, required: true, }, + logsPath: { + type: String, + required: false, + default: invalidUrl, + }, defaultBranch: { type: String, required: true, @@ -247,22 +251,20 @@ export default { dashboardsEndpoint: this.dashboardsEndpoint, currentDashboard: this.currentDashboard, projectPath: this.projectPath, + logsPath: this.logsPath, }); }, mounted() { if (!this.hasMetrics) { this.setGettingStartedEmptyState(); } else { - const { start, end } = convertToFixedRange(this.selectedTimeRange); - - this.fetchData({ - start, - end, - }); + this.setTimeRange(this.selectedTimeRange); + this.fetchData(); } }, methods: { ...mapActions('monitoringDashboard', [ + 'setTimeRange', 'fetchData', 'setGettingStartedEmptyState', 'setEndpoints', diff --git a/app/assets/javascripts/monitoring/components/embed.vue b/app/assets/javascripts/monitoring/components/embed.vue index e55de1c0105..49188a7af8f 100644 --- a/app/assets/javascripts/monitoring/components/embed.vue +++ b/app/assets/javascripts/monitoring/components/embed.vue @@ -19,14 +19,8 @@ export default { }, data() { const timeRange = timeRangeFromUrl(this.dashboardUrl) || defaultTimeRange; - const { start, end } = convertToFixedRange(timeRange); - const params = { - start, - end, - }; - return { - params, + timeRange: convertToFixedRange(timeRange), elWidth: 0, }; }, @@ -49,7 +43,9 @@ export default { }, mounted() { this.setInitialState(); - this.fetchMetricsData(this.params); + this.setTimeRange(this.timeRange); + this.fetchDashboard(); + sidebarMutationObserver = new MutationObserver(this.onSidebarMutation); sidebarMutationObserver.observe(document.querySelector('.layout-page'), { attributes: true, @@ -64,7 +60,8 @@ export default { }, methods: { ...mapActions('monitoringDashboard', [ - 'fetchMetricsData', + 'setTimeRange', + 'fetchDashboard', 'setEndpoints', 'setFeatureFlags', 'setShowErrorBanner', diff --git a/app/assets/javascripts/monitoring/components/panel_type.vue b/app/assets/javascripts/monitoring/components/panel_type.vue index 6751f3d31e8..0258a62b390 100644 --- a/app/assets/javascripts/monitoring/components/panel_type.vue +++ b/app/assets/javascripts/monitoring/components/panel_type.vue @@ -1,6 +1,7 @@ <script> import { mapState } from 'vuex'; import { pickBy } from 'lodash'; +import invalidUrl from '~/lib/utils/invalid_url'; import { GlDropdown, GlDropdownItem, @@ -18,7 +19,7 @@ import MonitorColumnChart from './charts/column.vue'; import MonitorStackedColumnChart from './charts/stacked_column.vue'; import MonitorEmptyChart from './charts/empty_chart.vue'; import TrackEventDirective from '~/vue_shared/directives/track_event'; -import { downloadCSVOptions, generateLinkToChartOptions } from '../utils'; +import { timeRangeToUrl, downloadCSVOptions, generateLinkToChartOptions } from '../utils'; export default { components: { @@ -58,8 +59,13 @@ export default { default: 'panel-type-chart', }, }, + data() { + return { + zoomedTimeRange: null, + }; + }, computed: { - ...mapState('monitoringDashboard', ['deploymentData', 'projectPath']), + ...mapState('monitoringDashboard', ['deploymentData', 'projectPath', 'logsPath', 'timeRange']), alertWidgetAvailable() { return IS_EE && this.prometheusAlertsAvailable && this.alertsEndpoint && this.graphData; }, @@ -70,6 +76,14 @@ export default { this.graphData.metrics[0].result.length > 0 ); }, + logsPathWithTimeRange() { + const timeRange = this.zoomedTimeRange || this.timeRange; + + if (this.logsPath && this.logsPath !== invalidUrl && timeRange) { + return timeRangeToUrl(timeRange, this.logsPath); + } + return null; + }, csvText() { const chartData = this.graphData.metrics[0].result[0].values; const yLabel = this.graphData.y_label; @@ -107,6 +121,10 @@ export default { }, downloadCSVOptions, generateLinkToChartOptions, + + onDatazoom({ start, end }) { + this.zoomedTimeRange = { start, end }; + }, }, }; </script> @@ -130,11 +148,13 @@ export default { <component :is="monitorChartComponent" v-else-if="graphDataHasMetrics" + ref="timeChart" :graph-data="graphData" :deployment-data="deploymentData" :project-path="projectPath" :thresholds="getGraphAlertValues(graphData.metrics)" :group-id="groupId" + @datazoom="onDatazoom" > <div class="d-flex align-items-center"> <alert-widget @@ -157,6 +177,15 @@ export default { <template slot="button-content"> <icon name="ellipsis_v" class="text-secondary" /> </template> + + <gl-dropdown-item + v-if="logsPathWithTimeRange" + ref="viewLogsLink" + :href="logsPathWithTimeRange" + > + {{ s__('Metrics|View logs') }} + </gl-dropdown-item> + <gl-dropdown-item v-track-event="downloadCSVOptions(graphData.title)" :href="downloadCsv" diff --git a/app/assets/javascripts/monitoring/stores/actions.js b/app/assets/javascripts/monitoring/stores/actions.js index 29000475bd4..3a052200ab9 100644 --- a/app/assets/javascripts/monitoring/stores/actions.js +++ b/app/assets/javascripts/monitoring/stores/actions.js @@ -1,6 +1,7 @@ import * as types from './mutation_types'; import axios from '~/lib/utils/axios_utils'; import createFlash from '~/flash'; +import { convertToFixedRange } from '~/lib/utils/datetime_range'; import { gqClient, parseEnvironmentsResponse, removeLeadingSlash } from './utils'; import trackDashboardLoad from '../monitoring_tracking_helper'; import getEnvironments from '../queries/getEnvironments.query.graphql'; @@ -32,6 +33,10 @@ export const setEndpoints = ({ commit }, endpoints) => { commit(types.SET_ENDPOINTS, endpoints); }; +export const setTimeRange = ({ commit }, timeRange) => { + commit(types.SET_TIME_RANGE, timeRange); +}; + export const filterEnvironments = ({ commit, dispatch }, searchTerm) => { commit(types.SET_ENVIRONMENTS_FILTER, searchTerm); dispatch('fetchEnvironmentsData'); @@ -63,19 +68,24 @@ export const receiveEnvironmentsDataSuccess = ({ commit }, data) => export const receiveEnvironmentsDataFailure = ({ commit }) => commit(types.RECEIVE_ENVIRONMENTS_DATA_FAILURE); -export const fetchData = ({ dispatch }, params) => { - dispatch('fetchMetricsData', params); +export const fetchData = ({ dispatch }) => { + dispatch('fetchDashboard'); dispatch('fetchDeploymentsData'); dispatch('fetchEnvironmentsData'); }; -export const fetchMetricsData = ({ dispatch }, params) => dispatch('fetchDashboard', params); - -export const fetchDashboard = ({ state, dispatch }, params) => { +export const fetchDashboard = ({ state, dispatch }) => { dispatch('requestMetricsDashboard'); + const params = {}; + + if (state.timeRange) { + const { start, end } = convertToFixedRange(state.timeRange); + params.start = start; + params.end = end; + } + if (state.currentDashboard) { - // eslint-disable-next-line no-param-reassign params.dashboard = state.currentDashboard; } diff --git a/app/assets/javascripts/monitoring/stores/mutation_types.js b/app/assets/javascripts/monitoring/stores/mutation_types.js index bdfaf42b35c..8873142accc 100644 --- a/app/assets/javascripts/monitoring/stores/mutation_types.js +++ b/app/assets/javascripts/monitoring/stores/mutation_types.js @@ -14,7 +14,7 @@ export const REQUEST_METRIC_RESULT = 'REQUEST_METRIC_RESULT'; export const RECEIVE_METRIC_RESULT_SUCCESS = 'RECEIVE_METRIC_RESULT_SUCCESS'; export const RECEIVE_METRIC_RESULT_FAILURE = 'RECEIVE_METRIC_RESULT_FAILURE'; -export const SET_TIME_WINDOW = 'SET_TIME_WINDOW'; +export const SET_TIME_RANGE = 'SET_TIME_RANGE'; export const SET_ALL_DASHBOARDS = 'SET_ALL_DASHBOARDS'; export const SET_ENDPOINTS = 'SET_ENDPOINTS'; export const SET_GETTING_STARTED_EMPTY_STATE = 'SET_GETTING_STARTED_EMPTY_STATE'; diff --git a/app/assets/javascripts/monitoring/stores/mutations.js b/app/assets/javascripts/monitoring/stores/mutations.js index 2a86a6a26d8..5f559290ff7 100644 --- a/app/assets/javascripts/monitoring/stores/mutations.js +++ b/app/assets/javascripts/monitoring/stores/mutations.js @@ -182,6 +182,10 @@ export default { state.dashboardsEndpoint = endpoints.dashboardsEndpoint; state.currentDashboard = endpoints.currentDashboard; state.projectPath = endpoints.projectPath; + state.logsPath = endpoints.logsPath || state.logsPath; + }, + [types.SET_TIME_RANGE](state, timeRange) { + state.timeRange = timeRange; }, [types.SET_GETTING_STARTED_EMPTY_STATE](state) { state.emptyState = 'gettingStarted'; diff --git a/app/assets/javascripts/monitoring/stores/state.js b/app/assets/javascripts/monitoring/stores/state.js index 9d3227e8aae..a2050f8e893 100644 --- a/app/assets/javascripts/monitoring/stores/state.js +++ b/app/assets/javascripts/monitoring/stores/state.js @@ -1,22 +1,31 @@ import invalidUrl from '~/lib/utils/invalid_url'; export default () => ({ + // API endpoints metricsEndpoint: null, deploymentsEndpoint: null, dashboardEndpoint: invalidUrl, + + // Dashboard request parameters + timeRange: null, + currentDashboard: null, + + // Dashboard data emptyState: 'gettingStarted', showEmptyState: true, showErrorBanner: true, - dashboard: { panel_groups: [], }, + allDashboards: [], + // Other project data deploymentData: [], environments: [], environmentsSearchTerm: '', environmentsLoading: false, - allDashboards: [], - currentDashboard: null, + + // GitLab paths to other pages projectPath: null, + logsPath: invalidUrl, }); diff --git a/app/assets/javascripts/monitoring/utils.js b/app/assets/javascripts/monitoring/utils.js index 915812596c6..b2fa44835e6 100644 --- a/app/assets/javascripts/monitoring/utils.js +++ b/app/assets/javascripts/monitoring/utils.js @@ -103,8 +103,9 @@ export const graphDataValidatorForAnomalyValues = graphData => { /** * Returns a time range from the current URL params * - * @returns {Object} The time range defined by the - * current URL, reading from `window.location.search` + * @returns {Object|null} The time range defined by the + * current URL, reading from search query or `window.location.search`. + * Returns `null` if no parameters form a time range. */ export const timeRangeFromUrl = (search = window.location.search) => { const params = queryToObject(search); |