Commit c72565ae authored by Kushal Pandya's avatar Kushal Pandya

Merge branch 'fix-cycle-analytics-403-error-message' into 'master'

Fix display 403 errors for cycle analytics

See merge request gitlab-org/gitlab!22987
parents 3964175f d7143c36
...@@ -85,9 +85,15 @@ export default { ...@@ -85,9 +85,15 @@ export default {
return this.selectedGroup && !this.errorCode; return this.selectedGroup && !this.errorCode;
}, },
shouldDisplayDurationChart() { shouldDisplayDurationChart() {
return !this.isLoadingDurationChart && !this.isLoading; return this.featureFlags.hasDurationChart && !this.hasNoAccessError;
}, },
shouldDisplayTasksByTypeChart() { shouldDisplayTasksByTypeChart() {
return this.featureFlags.hasTasksByTypeChart && !this.hasNoAccessError;
},
isDurationChartLoaded() {
return !this.isLoadingDurationChart && !this.isLoading;
},
isTasksByTypeChartLoaded() {
return !this.isLoading && !this.isLoadingTasksByTypeChart; return !this.isLoading && !this.isLoadingTasksByTypeChart;
}, },
hasDateRangeSet() { hasDateRangeSet() {
...@@ -280,8 +286,8 @@ export default { ...@@ -280,8 +286,8 @@ export default {
/> />
</div> </div>
</div> </div>
<template v-if="featureFlags.hasDurationChart"> <template v-if="shouldDisplayDurationChart">
<template v-if="shouldDisplayDurationChart"> <template v-if="isDurationChartLoaded">
<div class="mt-3 d-flex"> <div class="mt-3 d-flex">
<h4 class="mt-0">{{ s__('CycleAnalytics|Days to completion') }}</h4> <h4 class="mt-0">{{ s__('CycleAnalytics|Days to completion') }}</h4>
<stage-dropdown-filter <stage-dropdown-filter
...@@ -304,9 +310,9 @@ export default { ...@@ -304,9 +310,9 @@ export default {
</template> </template>
<gl-loading-icon v-else-if="!isLoading" size="md" class="my-4 py-4" /> <gl-loading-icon v-else-if="!isLoading" size="md" class="my-4 py-4" />
</template> </template>
<template v-if="featureFlags.hasTasksByTypeChart"> <template v-if="shouldDisplayTasksByTypeChart">
<div class="js-tasks-by-type-chart"> <div class="js-tasks-by-type-chart">
<div v-if="shouldDisplayTasksByTypeChart"> <div v-if="isTasksByTypeChartLoaded">
<tasks-by-type-chart <tasks-by-type-chart
:chart-data="tasksByTypeChartData" :chart-data="tasksByTypeChartData"
:filters="selectedTasksByTypeFilters" :filters="selectedTasksByTypeFilters"
......
...@@ -11,6 +11,14 @@ const removeError = () => { ...@@ -11,6 +11,14 @@ const removeError = () => {
hideFlash(flashEl); hideFlash(flashEl);
} }
}; };
const handleErrorOrRethrow = ({ action, error }) => {
if (error?.response?.status === httpStatus.FORBIDDEN) {
throw error;
}
action();
};
export const setFeatureFlags = ({ commit }, featureFlags) => export const setFeatureFlags = ({ commit }, featureFlags) =>
commit(types.SET_FEATURE_FLAGS, featureFlags); commit(types.SET_FEATURE_FLAGS, featureFlags);
export const setSelectedGroup = ({ commit }, group) => commit(types.SET_SELECTED_GROUP, group); export const setSelectedGroup = ({ commit }, group) => commit(types.SET_SELECTED_GROUP, group);
...@@ -85,7 +93,12 @@ export const fetchStageMedianValues = ({ state, dispatch, getters }) => { ...@@ -85,7 +93,12 @@ export const fetchStageMedianValues = ({ state, dispatch, getters }) => {
return Promise.all(stageIds.map(stageId => fetchStageMedian(currentGroupPath, stageId, params))) return Promise.all(stageIds.map(stageId => fetchStageMedian(currentGroupPath, stageId, params)))
.then(data => dispatch('receiveStageMedianValuesSuccess', data)) .then(data => dispatch('receiveStageMedianValuesSuccess', data))
.catch(err => dispatch('receiveStageMedianValuesError', err)); .catch(error =>
handleErrorOrRethrow({
error,
action: () => dispatch('receiveStageMedianValuesError', error),
}),
);
}; };
export const requestCycleAnalyticsData = ({ commit }) => commit(types.REQUEST_CYCLE_ANALYTICS_DATA); export const requestCycleAnalyticsData = ({ commit }) => commit(types.REQUEST_CYCLE_ANALYTICS_DATA);
...@@ -150,7 +163,9 @@ export const fetchSummaryData = ({ state, dispatch, getters }) => { ...@@ -150,7 +163,9 @@ export const fetchSummaryData = ({ state, dispatch, getters }) => {
return Api.cycleAnalyticsSummaryData({ group_id: fullPath, created_after, created_before }) return Api.cycleAnalyticsSummaryData({ group_id: fullPath, created_after, created_before })
.then(({ data }) => dispatch('receiveSummaryDataSuccess', data)) .then(({ data }) => dispatch('receiveSummaryDataSuccess', data))
.catch(error => dispatch('receiveSummaryDataError', error)); .catch(error =>
handleErrorOrRethrow({ error, action: () => dispatch('receiveSummaryDataError', error) }),
);
}; };
export const requestGroupStagesAndEvents = ({ commit }) => export const requestGroupStagesAndEvents = ({ commit }) =>
...@@ -174,7 +189,9 @@ export const fetchGroupLabels = ({ dispatch, state }) => { ...@@ -174,7 +189,9 @@ export const fetchGroupLabels = ({ dispatch, state }) => {
return Api.groupLabels(fullPath) return Api.groupLabels(fullPath)
.then(data => dispatch('receiveGroupLabelsSuccess', data)) .then(data => dispatch('receiveGroupLabelsSuccess', data))
.catch(error => dispatch('receiveGroupLabelsError', error)); .catch(error =>
handleErrorOrRethrow({ error, action: () => dispatch('receiveGroupLabelsError', error) }),
);
}; };
export const receiveGroupStagesAndEventsError = ({ commit }, error) => { export const receiveGroupStagesAndEventsError = ({ commit }, error) => {
...@@ -209,7 +226,12 @@ export const fetchGroupStagesAndEvents = ({ state, dispatch, getters }) => { ...@@ -209,7 +226,12 @@ export const fetchGroupStagesAndEvents = ({ state, dispatch, getters }) => {
nestQueryStringKeys({ start_date: created_after, project_ids }, 'cycle_analytics'), nestQueryStringKeys({ start_date: created_after, project_ids }, 'cycle_analytics'),
) )
.then(({ data }) => dispatch('receiveGroupStagesAndEventsSuccess', data)) .then(({ data }) => dispatch('receiveGroupStagesAndEventsSuccess', data))
.catch(error => dispatch('receiveGroupStagesAndEventsError', error)); .catch(error =>
handleErrorOrRethrow({
error,
action: () => dispatch('receiveGroupStagesAndEventsError', error),
}),
);
}; };
export const requestCreateCustomStage = ({ commit }) => commit(types.REQUEST_CREATE_CUSTOM_STAGE); export const requestCreateCustomStage = ({ commit }) => commit(types.REQUEST_CREATE_CUSTOM_STAGE);
......
...@@ -14,6 +14,7 @@ import 'bootstrap'; ...@@ -14,6 +14,7 @@ import 'bootstrap';
import '~/gl_dropdown'; import '~/gl_dropdown';
import Scatterplot from 'ee/analytics/shared/components/scatterplot.vue'; import Scatterplot from 'ee/analytics/shared/components/scatterplot.vue';
import Daterange from 'ee/analytics/shared/components/daterange.vue'; import Daterange from 'ee/analytics/shared/components/daterange.vue';
import TasksByTypeChart from 'ee/analytics/cycle_analytics/components/tasks_by_type_chart.vue';
import waitForPromises from 'helpers/wait_for_promises'; import waitForPromises from 'helpers/wait_for_promises';
import httpStatusCodes from '~/lib/utils/http_status'; import httpStatusCodes from '~/lib/utils/http_status';
import * as mockData from '../mock_data'; import * as mockData from '../mock_data';
...@@ -105,6 +106,10 @@ describe('Cycle Analytics component', () => { ...@@ -105,6 +106,10 @@ describe('Cycle Analytics component', () => {
expect(wrapper.find(Scatterplot).exists()).toBe(flag); expect(wrapper.find(Scatterplot).exists()).toBe(flag);
}; };
const displaysTasksByType = flag => {
expect(wrapper.find(TasksByTypeChart).exists()).toBe(flag);
};
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
wrapper = createComponent(); wrapper = createComponent();
...@@ -289,11 +294,10 @@ describe('Cycle Analytics component', () => { ...@@ -289,11 +294,10 @@ describe('Cycle Analytics component', () => {
describe('the user does not have access to the group', () => { describe('the user does not have access to the group', () => {
beforeEach(() => { beforeEach(() => {
mock = new MockAdapter(axios); mock = new MockAdapter(axios);
wrapper.vm.$store.dispatch('setSelectedGroup', { mock.onAny().reply(403);
...mockData.group,
});
wrapper.vm.$store.state.errorCode = 403; wrapper.vm.onGroupSelect(mockData.group);
return waitForPromises();
}); });
it('renders the no access information', () => { it('renders the no access information', () => {
...@@ -322,6 +326,14 @@ describe('Cycle Analytics component', () => { ...@@ -322,6 +326,14 @@ describe('Cycle Analytics component', () => {
it('does not display the add stage button', () => { it('does not display the add stage button', () => {
expect(wrapper.find('.js-add-stage-button').exists()).toBe(false); expect(wrapper.find('.js-add-stage-button').exists()).toBe(false);
}); });
it('does not display the tasks by type chart', () => {
displaysTasksByType(false);
});
it('does not display the duration chart', () => {
displaysDurationScatterPlot(false);
});
}); });
describe('with customizableCycleAnalytics=true', () => { describe('with customizableCycleAnalytics=true', () => {
...@@ -366,6 +378,7 @@ describe('Cycle Analytics component', () => { ...@@ -366,6 +378,7 @@ describe('Cycle Analytics component', () => {
wrapper.destroy(); wrapper.destroy();
mock.restore(); mock.restore();
}); });
it('displays the tasks by type chart', () => { it('displays the tasks by type chart', () => {
expect(wrapper.find('.js-tasks-by-type-chart').exists()).toBe(true); expect(wrapper.find('.js-tasks-by-type-chart').exists()).toBe(true);
}); });
......
...@@ -60,6 +60,7 @@ describe('TasksByTypeChart', () => { ...@@ -60,6 +60,7 @@ describe('TasksByTypeChart', () => {
}, },
}); });
}); });
it('should render the no data available message', () => { it('should render the no data available message', () => {
expect(wrapper.html()).toMatchSnapshot(); expect(wrapper.html()).toMatchSnapshot();
}); });
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment