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 {
'isEmptyStage',
'selectedStage',
'selectedStageEvents',
'selectedStageError',
'stages',
'summary',
'startDate',
......@@ -72,6 +73,14 @@ export default {
selectedStageReady() {
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: {
...mapActions([
......@@ -206,9 +215,9 @@ export default {
<gl-empty-state
v-if="displayNotEnoughData"
class="js-empty-state"
:description="selectedStage.emptyStageText"
:description="emptyStageText"
:svg-path="noDataSvgPath"
:title="__('We don\'t have enough data to show this stage.')"
:title="emptyStageTitle"
/>
<component
:is="selectedStage.component"
......
......@@ -33,7 +33,14 @@ export const fetchStageData = ({ state: { requestPath, selectedStage, startDate
.get(`${requestPath}/events/${selectedStage.name}.json`, {
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));
};
......
......@@ -44,10 +44,11 @@ export default {
state.selectedStageEvents = decorateEvents(events, selectedStage);
state.hasError = false;
},
[types.RECEIVE_STAGE_DATA_ERROR](state) {
[types.RECEIVE_STAGE_DATA_ERROR](state, error) {
state.isLoadingStage = false;
state.isEmptyStage = true;
state.selectedStageEvents = [];
state.hasError = true;
state.selectedStageError = error;
},
};
......@@ -9,6 +9,7 @@ export default () => ({
stats: [],
selectedStage: {},
selectedStageEvents: [],
selectedStageError: '',
medians: {},
hasError: false,
isLoading: false,
......
......@@ -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 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 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', () => {
isEmptyStage: false,
selectedStageEvents,
selectedStage,
selectedStageError: '',
},
});
});
......@@ -133,6 +134,22 @@ describe('Value stream analytics component', () => {
it('renders the empty stage with `Not enough data` message', () => {
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', () => {
......
......@@ -106,6 +106,32 @@ describe('Project Value Stream Analytics actions', () => {
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', () => {
beforeEach(() => {
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