Commit c334e13a authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Gracefully handle query timeouts for project VSA

When there is too much data to calculate a result for
a project level VSA stage, the query will timeout. The api
result still succeeds, but an error is returned in the query
result payload.

Changelog: added
parent 541a71db
...@@ -54,6 +54,7 @@ export default { ...@@ -54,6 +54,7 @@ export default {
'isEmptyStage', 'isEmptyStage',
'selectedStage', 'selectedStage',
'selectedStageEvents', 'selectedStageEvents',
'selectedStageError',
'stages', 'stages',
'summary', 'summary',
'startDate', 'startDate',
...@@ -72,6 +73,14 @@ export default { ...@@ -72,6 +73,14 @@ export default {
selectedStageReady() { selectedStageReady() {
return !this.isLoadingStage && this.selectedStage; return !this.isLoadingStage && this.selectedStage;
}, },
emptyStageTitle() {
return this.selectedStageError
? this.selectedStageError
: __("We don't have enough data to show this stage.");
},
emptyStageText() {
return !this.selectedStageError ? this.selectedStage.emptyStageText : '';
},
}, },
methods: { methods: {
...mapActions([ ...mapActions([
...@@ -206,9 +215,9 @@ export default { ...@@ -206,9 +215,9 @@ export default {
<gl-empty-state <gl-empty-state
v-if="displayNotEnoughData" v-if="displayNotEnoughData"
class="js-empty-state" class="js-empty-state"
:description="selectedStage.emptyStageText" :description="emptyStageText"
:svg-path="noDataSvgPath" :svg-path="noDataSvgPath"
:title="__('We don\'t have enough data to show this stage.')" :title="emptyStageTitle"
/> />
<component <component
:is="selectedStage.component" :is="selectedStage.component"
......
...@@ -33,7 +33,14 @@ export const fetchStageData = ({ state: { requestPath, selectedStage, startDate ...@@ -33,7 +33,14 @@ export const fetchStageData = ({ state: { requestPath, selectedStage, startDate
.get(`${requestPath}/events/${selectedStage.name}.json`, { .get(`${requestPath}/events/${selectedStage.name}.json`, {
params: { 'cycle_analytics[start_date]': startDate }, params: { 'cycle_analytics[start_date]': startDate },
}) })
.then(({ data }) => commit(types.RECEIVE_STAGE_DATA_SUCCESS, data)) .then(({ data }) => {
// when there's a query timeout, the request succeeds but the error is encoded in the response data
if (data?.error) {
commit(types.RECEIVE_STAGE_DATA_ERROR, data.error);
} else {
commit(types.RECEIVE_STAGE_DATA_SUCCESS, data);
}
})
.catch(() => commit(types.RECEIVE_STAGE_DATA_ERROR)); .catch(() => commit(types.RECEIVE_STAGE_DATA_ERROR));
}; };
......
...@@ -44,10 +44,11 @@ export default { ...@@ -44,10 +44,11 @@ export default {
state.selectedStageEvents = decorateEvents(events, selectedStage); state.selectedStageEvents = decorateEvents(events, selectedStage);
state.hasError = false; state.hasError = false;
}, },
[types.RECEIVE_STAGE_DATA_ERROR](state) { [types.RECEIVE_STAGE_DATA_ERROR](state, error) {
state.isLoadingStage = false; state.isLoadingStage = false;
state.isEmptyStage = true; state.isEmptyStage = true;
state.selectedStageEvents = []; state.selectedStageEvents = [];
state.hasError = true; state.hasError = true;
state.selectedStageError = error;
}, },
}; };
...@@ -9,6 +9,7 @@ export default () => ({ ...@@ -9,6 +9,7 @@ export default () => ({
stats: [], stats: [],
selectedStage: {}, selectedStage: {},
selectedStageEvents: [], selectedStageEvents: [],
selectedStageError: '',
medians: {}, medians: {},
hasError: false, hasError: false,
isLoading: false, isLoading: false,
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
exports[`Value stream analytics component isEmptyStage = true renders the empty stage with \`Not enough data\` message 1`] = `"<gl-empty-state-stub title=\\"We don't have enough data to show this stage.\\" svgpath=\\"path/to/no/data\\" description=\\"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.\\" class=\\"js-empty-state\\"></gl-empty-state-stub>"`; exports[`Value stream analytics component isEmptyStage = true renders the empty stage with \`Not enough data\` message 1`] = `"<gl-empty-state-stub title=\\"We don't have enough data to show this stage.\\" svgpath=\\"path/to/no/data\\" description=\\"The issue stage shows the time it takes from creating an issue to assigning the issue to a milestone, or add the issue to a list on your Issue Board. Begin creating issues to see data for this stage.\\" class=\\"js-empty-state\\"></gl-empty-state-stub>"`;
exports[`Value stream analytics component isEmptyStage = true with a selectedStageError renders the empty stage with \`There is too much data to calculate\` message 1`] = `"<gl-empty-state-stub title=\\"There is too much data to calculate\\" svgpath=\\"path/to/no/data\\" description=\\"\\" class=\\"js-empty-state\\"></gl-empty-state-stub>"`;
exports[`Value stream analytics component isLoading = true renders the path navigation component with prop \`loading\` set to true 1`] = `"<path-navigation-stub loading=\\"true\\" stages=\\"\\" selectedstage=\\"[object Object]\\" class=\\"js-path-navigation gl-w-full gl-pb-2\\"></path-navigation-stub>"`; exports[`Value stream analytics component isLoading = true renders the path navigation component with prop \`loading\` set to true 1`] = `"<path-navigation-stub loading=\\"true\\" stages=\\"\\" selectedstage=\\"[object Object]\\" class=\\"js-path-navigation gl-w-full gl-pb-2\\"></path-navigation-stub>"`;
exports[`Value stream analytics component without enough permissions renders the empty stage with \`You need permission\` message 1`] = `"<gl-empty-state-stub title=\\"You need permission.\\" svgpath=\\"path/to/no/access\\" description=\\"Want to see the data? Please ask an administrator for access.\\" class=\\"js-empty-state\\"></gl-empty-state-stub>"`; exports[`Value stream analytics component without enough permissions renders the empty stage with \`You need permission\` message 1`] = `"<gl-empty-state-stub title=\\"You need permission.\\" svgpath=\\"path/to/no/access\\" description=\\"Want to see the data? Please ask an administrator for access.\\" class=\\"js-empty-state\\"></gl-empty-state-stub>"`;
...@@ -55,6 +55,7 @@ describe('Value stream analytics component', () => { ...@@ -55,6 +55,7 @@ describe('Value stream analytics component', () => {
isEmptyStage: false, isEmptyStage: false,
selectedStageEvents, selectedStageEvents,
selectedStage, selectedStage,
selectedStageError: '',
}, },
}); });
}); });
...@@ -133,6 +134,22 @@ describe('Value stream analytics component', () => { ...@@ -133,6 +134,22 @@ describe('Value stream analytics component', () => {
it('renders the empty stage with `Not enough data` message', () => { it('renders the empty stage with `Not enough data` message', () => {
expect(findEmptyStage().html()).toMatchSnapshot(); expect(findEmptyStage().html()).toMatchSnapshot();
}); });
describe('with a selectedStageError', () => {
beforeEach(() => {
wrapper = createComponent({
initialState: {
selectedStage,
isEmptyStage: true,
selectedStageError: 'There is too much data to calculate',
},
});
});
it('renders the empty stage with `There is too much data to calculate` message', () => {
expect(findEmptyStage().html()).toMatchSnapshot();
});
});
}); });
describe('without enough permissions', () => { describe('without enough permissions', () => {
......
...@@ -106,6 +106,32 @@ describe('Project Value Stream Analytics actions', () => { ...@@ -106,6 +106,32 @@ describe('Project Value Stream Analytics actions', () => {
expectedActions: [], expectedActions: [],
})); }));
describe('with a successful request, but an error in the payload', () => {
const tooMuchDataError = 'Too much data';
beforeEach(() => {
state = {
requestPath: mockRequestPath,
startDate: mockStartDate,
selectedStage,
};
mock = new MockAdapter(axios);
mock.onGet(mockStagePath).reply(httpStatusCodes.OK, { error: tooMuchDataError });
});
it(`commits the 'RECEIVE_STAGE_DATA_ERROR' mutation`, () =>
testAction({
action: actions.fetchStageData,
state,
payload: { error: tooMuchDataError },
expectedMutations: [
{ type: 'REQUEST_STAGE_DATA' },
{ type: 'RECEIVE_STAGE_DATA_ERROR', payload: tooMuchDataError },
],
expectedActions: [],
}));
});
describe('with a failing request', () => { describe('with a failing request', () => {
beforeEach(() => { beforeEach(() => {
state = { state = {
......
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