diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-13 06:09:49 +0300 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-11-13 06:09:49 +0300 |
commit | b662e7d21db991d6f998ce5268ee843f85d2c631 (patch) | |
tree | 8433cc83290e7bcddb68bad4478216fd245781e4 /app | |
parent | a125ac8a014fc86f8c716229835ed0a0403a6e91 (diff) |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'app')
7 files changed, 134 insertions, 79 deletions
diff --git a/app/assets/javascripts/cycle_analytics/components/base.vue b/app/assets/javascripts/cycle_analytics/components/base.vue index 1d98a42ce58..36430e51dd2 100644 --- a/app/assets/javascripts/cycle_analytics/components/base.vue +++ b/app/assets/javascripts/cycle_analytics/components/base.vue @@ -2,10 +2,12 @@ import { GlLoadingIcon } from '@gitlab/ui'; import Cookies from 'js-cookie'; import { mapActions, mapState, mapGetters } from 'vuex'; +import { toYmd } from '~/analytics/shared/utils'; import PathNavigation from '~/cycle_analytics/components/path_navigation.vue'; import StageTable from '~/cycle_analytics/components/stage_table.vue'; import ValueStreamFilters from '~/cycle_analytics/components/value_stream_filters.vue'; import ValueStreamMetrics from '~/cycle_analytics/components/value_stream_metrics.vue'; +import UrlSync from '~/vue_shared/components/url_sync.vue'; import { __ } from '~/locale'; import { SUMMARY_METRICS_REQUEST, METRICS_REQUESTS } from '../constants'; @@ -19,6 +21,7 @@ export default { StageTable, ValueStreamFilters, ValueStreamMetrics, + UrlSync, }, props: { noDataSvgPath: { @@ -54,6 +57,9 @@ export default { 'pagination', ]), ...mapGetters(['pathNavigationData', 'filterParams']), + isLoaded() { + return !this.isLoading && !this.isLoadingStage; + }, displayStageEvents() { const { selectedStageEvents, isLoadingStage, isEmptyStage } = this; return selectedStageEvents.length && !isLoadingStage && !isEmptyStage; @@ -98,6 +104,16 @@ export default { metricsRequests() { return this.features?.cycleAnalyticsForGroups ? METRICS_REQUESTS : SUMMARY_METRICS_REQUEST; }, + query() { + return { + created_after: toYmd(this.createdAfter), + created_before: toYmd(this.createdBefore), + stage_id: this.selectedStage?.id || null, + sort: this.pagination?.sort || null, + direction: this.pagination?.direction || null, + page: this.pagination?.page || null, + }; + }, }, methods: { ...mapActions([ @@ -176,5 +192,6 @@ export default { :pagination="pagination" @handleUpdatePagination="onHandleUpdatePagination" /> + <url-sync v-if="isLoaded" :query="query" /> </div> </template> diff --git a/app/assets/javascripts/cycle_analytics/constants.js b/app/assets/javascripts/cycle_analytics/constants.js index d1e837b1af4..7d5822b0824 100644 --- a/app/assets/javascripts/cycle_analytics/constants.js +++ b/app/assets/javascripts/cycle_analytics/constants.js @@ -5,8 +5,6 @@ import { } from '~/api/analytics_api'; import { __, s__ } from '~/locale'; -export const DEFAULT_DAYS_IN_PAST = 30; -export const DEFAULT_DAYS_TO_DISPLAY = 30; export const OVERVIEW_STAGE_ID = 'overview'; export const DEFAULT_VALUE_STREAM = { diff --git a/app/assets/javascripts/cycle_analytics/index.js b/app/assets/javascripts/cycle_analytics/index.js index 34ef03409b8..3da8696edeb 100644 --- a/app/assets/javascripts/cycle_analytics/index.js +++ b/app/assets/javascripts/cycle_analytics/index.js @@ -1,44 +1,36 @@ import Vue from 'vue'; +import { + extractFilterQueryParameters, + extractPaginationQueryParameters, +} from '~/analytics/shared/utils'; import Translate from '../vue_shared/translate'; import CycleAnalytics from './components/base.vue'; -import { DEFAULT_DAYS_TO_DISPLAY } from './constants'; import createStore from './store'; -import { calculateFormattedDayInPast } from './utils'; +import { buildCycleAnalyticsInitialData } from './utils'; Vue.use(Translate); export default () => { const store = createStore(); const el = document.querySelector('#js-cycle-analytics'); - const { - noAccessSvgPath, - noDataSvgPath, - requestPath, - fullPath, - projectId, - groupId, - groupPath, - labelsPath, - milestonesPath, - } = el.dataset; + const { noAccessSvgPath, noDataSvgPath } = el.dataset; + const initialData = buildCycleAnalyticsInitialData({ ...el.dataset, gon }); - const { now, past } = calculateFormattedDayInPast(DEFAULT_DAYS_TO_DISPLAY); + const pagination = extractPaginationQueryParameters(window.location.search); + const { + selectedAuthor, + selectedMilestone, + selectedAssigneeList, + selectedLabelList, + } = extractFilterQueryParameters(window.location.search); store.dispatch('initializeVsa', { - projectId: parseInt(projectId, 10), - endpoints: { - requestPath, - fullPath, - labelsPath, - milestonesPath, - groupId: parseInt(groupId, 10), - groupPath, - }, - features: { - cycleAnalyticsForGroups: Boolean(gon?.licensed_features?.cycleAnalyticsForGroups), - }, - createdBefore: new Date(now), - createdAfter: new Date(past), + ...initialData, + selectedAuthor, + selectedMilestone, + selectedAssigneeList, + selectedLabelList, + pagination, }); // eslint-disable-next-line no-new @@ -52,7 +44,6 @@ export default () => { props: { noDataSvgPath, noAccessSvgPath, - fullPath, }, }), }); diff --git a/app/assets/javascripts/cycle_analytics/store/actions.js b/app/assets/javascripts/cycle_analytics/store/actions.js index 24b62849db7..e0156b24f9d 100644 --- a/app/assets/javascripts/cycle_analytics/store/actions.js +++ b/app/assets/javascripts/cycle_analytics/store/actions.js @@ -14,7 +14,7 @@ import * as types from './mutation_types'; export const setSelectedValueStream = ({ commit, dispatch }, valueStream) => { commit(types.SET_SELECTED_VALUE_STREAM, valueStream); - return Promise.all([dispatch('fetchValueStreamStages'), dispatch('fetchCycleAnalyticsData')]); + return dispatch('fetchValueStreamStages'); }; export const fetchValueStreamStages = ({ commit, state }) => { @@ -46,10 +46,8 @@ export const fetchValueStreams = ({ commit, dispatch, state }) => { } = state; commit(types.REQUEST_VALUE_STREAMS); - const stageRequests = ['setSelectedStage', 'fetchStageMedians', 'fetchStageCountValues']; return getProjectValueStreams(fullPath) .then(({ data }) => dispatch('receiveValueStreamsSuccess', data)) - .then(() => Promise.all(stageRequests.map((r) => dispatch(r)))) .catch(({ response: { status } }) => { commit(types.RECEIVE_VALUE_STREAMS_ERROR, status); }); @@ -153,33 +151,36 @@ export const fetchStageCountValues = ({ }); }; -export const setSelectedStage = ({ dispatch, commit, state: { stages } }, selectedStage = null) => { - const stage = selectedStage || stages[0]; - commit(types.SET_SELECTED_STAGE, stage); - return dispatch('fetchStageData'); +export const fetchValueStreamStageData = ({ dispatch }) => + Promise.all([ + dispatch('fetchCycleAnalyticsData'), + dispatch('fetchStageData'), + dispatch('fetchStageMedians'), + dispatch('fetchStageCountValues'), + ]); + +export const refetchStageData = async ({ dispatch, commit }) => { + commit(types.SET_LOADING, true); + await dispatch('fetchValueStreamStageData'); + commit(types.SET_LOADING, false); }; -export const setLoading = ({ commit }, value) => commit(types.SET_LOADING, value); - -const refetchStageData = (dispatch) => { - return Promise.resolve() - .then(() => dispatch('setLoading', true)) - .then(() => - Promise.all([ - dispatch('fetchCycleAnalyticsData'), - dispatch('fetchStageData'), - dispatch('fetchStageMedians'), - dispatch('fetchStageCountValues'), - ]), - ) - .finally(() => dispatch('setLoading', false)); +export const setSelectedStage = ({ dispatch, commit }, selectedStage = null) => { + commit(types.SET_SELECTED_STAGE, selectedStage); + return dispatch('refetchStageData'); }; -export const setFilters = ({ dispatch }) => refetchStageData(dispatch); +export const setFilters = ({ dispatch }) => dispatch('refetchStageData'); export const setDateRange = ({ dispatch, commit }, { createdAfter, createdBefore }) => { commit(types.SET_DATE_RANGE, { createdAfter, createdBefore }); - return refetchStageData(dispatch); + return dispatch('refetchStageData'); +}; + +export const setInitialStage = ({ dispatch, commit, state: { stages } }, stage) => { + const selectedStage = stage || stages[0]; + commit(types.SET_SELECTED_STAGE, selectedStage); + return dispatch('fetchValueStreamStageData'); }; export const updateStageTablePagination = ( @@ -190,12 +191,18 @@ export const updateStageTablePagination = ( return dispatch('fetchStageData', selectedStage.id); }; -export const initializeVsa = ({ commit, dispatch }, initialData = {}) => { +export const initializeVsa = async ({ commit, dispatch }, initialData = {}) => { commit(types.INITIALIZE_VSA, initialData); const { endpoints: { fullPath, groupPath, milestonesPath = '', labelsPath = '' }, + selectedAuthor, + selectedMilestone, + selectedAssigneeList, + selectedLabelList, + selectedStage = null, } = initialData; + dispatch('filters/setEndpoints', { labelsEndpoint: labelsPath, milestonesEndpoint: milestonesPath, @@ -203,7 +210,15 @@ export const initializeVsa = ({ commit, dispatch }, initialData = {}) => { projectEndpoint: fullPath, }); - return dispatch('setLoading', true) - .then(() => dispatch('fetchValueStreams')) - .finally(() => dispatch('setLoading', false)); + dispatch('filters/initialize', { + selectedAuthor, + selectedMilestone, + selectedAssigneeList, + selectedLabelList, + }); + + commit(types.SET_LOADING, true); + await dispatch('fetchValueStreams'); + await dispatch('setInitialStage', selectedStage); + commit(types.SET_LOADING, false); }; diff --git a/app/assets/javascripts/cycle_analytics/utils.js b/app/assets/javascripts/cycle_analytics/utils.js index 3c6267bac06..9af63f5f9cc 100644 --- a/app/assets/javascripts/cycle_analytics/utils.js +++ b/app/assets/javascripts/cycle_analytics/utils.js @@ -1,7 +1,4 @@ -import dateFormat from 'dateformat'; -import { dateFormats } from '~/analytics/shared/constants'; import { hideFlash } from '~/flash'; -import { getDateInPast } from '~/lib/utils/datetime/date_calculation_utility'; import { parseSeconds } from '~/lib/utils/datetime_utility'; import { formatTimeAsSummary } from '~/lib/utils/datetime/date_format_utility'; import { slugify } from '~/lib/utils/text_utility'; @@ -74,23 +71,6 @@ export const formatMedianValues = (medians = []) => export const filterStagesByHiddenStatus = (stages = [], isHidden = true) => stages.filter(({ hidden = false }) => hidden === isHidden); -const toIsoFormat = (d) => dateFormat(d, dateFormats.isoDate); - -/** - * Takes an integer specifying the number of days to subtract - * from the date specified will return the 2 dates, formatted as ISO dates - * - * @param {Number} daysInPast - Number of days in the past to subtract - * @param {Date} [today=new Date] - Date to subtract days from, defaults to today - * @returns {Object} Returns 'now' and the 'past' date formatted as ISO dates - */ -export const calculateFormattedDayInPast = (daysInPast, today = new Date()) => { - return { - now: toIsoFormat(today), - past: toIsoFormat(getDateInPast(today, daysInPast)), - }; -}; - /** * @typedef {Object} MetricData * @property {String} title - Title of the metric measured @@ -123,3 +103,43 @@ export const prepareTimeMetricsData = (data = [], popoverContent = {}) => description: popoverContent[key]?.description || '', }; }); + +const extractFeatures = (gon) => ({ + cycleAnalyticsForGroups: Boolean(gon?.licensed_features?.cycleAnalyticsForGroups), +}); + +/** + * Builds the initial data object for Value Stream Analytics with data loaded from the backend + * + * @param {Object} dataset - dataset object paseed to the frontend via data-* properties + * @returns {Object} - The initial data to load the app with + */ +export const buildCycleAnalyticsInitialData = ({ + fullPath, + requestPath, + projectId, + groupId, + groupPath, + labelsPath, + milestonesPath, + stage, + createdAfter, + createdBefore, + gon, +} = {}) => { + return { + projectId: parseInt(projectId, 10), + endpoints: { + requestPath, + fullPath, + labelsPath, + milestonesPath, + groupId: parseInt(groupId, 10), + groupPath, + }, + createdAfter: new Date(createdAfter), + createdBefore: new Date(createdBefore), + selectedStage: stage ? JSON.parse(stage) : null, + features: extractFeatures(gon), + }; +}; diff --git a/app/controllers/projects/cycle_analytics_controller.rb b/app/controllers/projects/cycle_analytics_controller.rb index db5ba51ee01..dc6a9a73d9e 100644 --- a/app/controllers/projects/cycle_analytics_controller.rb +++ b/app/controllers/projects/cycle_analytics_controller.rb @@ -6,8 +6,10 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController include CycleAnalyticsParams include GracefulTimeoutHandling include RedisTracking + extend ::Gitlab::Utils::Override before_action :authorize_read_cycle_analytics! + before_action :load_value_stream, only: :show track_redis_hll_event :show, name: 'p_analytics_valuestream' @@ -19,6 +21,7 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController def show @cycle_analytics = Analytics::CycleAnalytics::ProjectLevel.new(project: @project, options: options(cycle_analytics_project_params)) + @request_params ||= ::Gitlab::Analytics::CycleAnalytics::RequestParams.new(all_cycle_analytics_params) respond_to do |format| format.html do @@ -34,6 +37,15 @@ class Projects::CycleAnalyticsController < Projects::ApplicationController private + override :all_cycle_analytics_params + def all_cycle_analytics_params + super.merge({ project: @project, value_stream: @value_stream }) + end + + def load_value_stream + @value_stream = Analytics::CycleAnalytics::ProjectValueStream.build_default_value_stream(@project) + end + def cycle_analytics_json { summary: @cycle_analytics.summary, diff --git a/app/views/projects/cycle_analytics/show.html.haml b/app/views/projects/cycle_analytics/show.html.haml index f398ac6ede7..ba79f0ee3cb 100644 --- a/app/views/projects/cycle_analytics/show.html.haml +++ b/app/views/projects/cycle_analytics/show.html.haml @@ -1,4 +1,6 @@ - page_title _("Value Stream Analytics") +- data_attributes = @request_params.valid? ? @request_params.to_data_attributes : {} +- data_attributes.merge!(cycle_analytics_initial_data(@project, @group)) - add_page_specific_style 'page_bundles/cycle_analytics' -#js-cycle-analytics{ data: cycle_analytics_initial_data(@project, @group) } +#js-cycle-analytics{ data: data_attributes } |