Commit 6aa030d8 authored by Illya Klymov's avatar Illya Klymov

Merge branch '287762-fix-multiple-vulnerabilities-requests' into 'master'

Fix simultaneous requests for vulnerabilities on pipeline security tab

See merge request gitlab-org/gitlab!48426
parents 3a31d21e 66eb8aff
---
title: Fix multiple simultaneous requests for vulnerabilities on pipeline security tab
merge_request: 48426
author:
type: fixed
...@@ -27,8 +27,8 @@ export default { ...@@ -27,8 +27,8 @@ export default {
this.filterQuery = { ...this.filterQuery, ...query }; this.filterQuery = { ...this.filterQuery, ...query };
this.emitFilterChange(); this.emitFilterChange();
}, },
// All the filters will emit @filter-changed when the page is first loaded, which will trigger // When this component is first shown, every filter will emit its own @filter-changed event at,
// this method multiple times. We'll debounce it so that it only runs once. // which will trigger this method multiple times. We'll debounce it so that it only runs once.
emitFilterChange: debounce(function emit() { emitFilterChange: debounce(function emit() {
this.$emit('filterChange', this.filterQuery); this.$emit('filterChange', this.filterQuery);
}), }),
......
...@@ -66,7 +66,6 @@ export default { ...@@ -66,7 +66,6 @@ export default {
created() { created() {
this.setPipelineId(this.pipelineId); this.setPipelineId(this.pipelineId);
this.setVulnerabilitiesEndpoint(this.vulnerabilitiesEndpoint); this.setVulnerabilitiesEndpoint(this.vulnerabilitiesEndpoint);
this.fetchVulnerabilities({ ...this.filters, page: this.pageInfo.page });
this.fetchPipelineJobs(); this.fetchPipelineJobs();
}, },
methods: { methods: {
...@@ -74,7 +73,6 @@ export default { ...@@ -74,7 +73,6 @@ export default {
'closeDismissalCommentBox', 'closeDismissalCommentBox',
'createIssue', 'createIssue',
'createMergeRequest', 'createMergeRequest',
'fetchVulnerabilities',
'openDismissalCommentBox', 'openDismissalCommentBox',
'setPipelineId', 'setPipelineId',
'setVulnerabilitiesEndpoint', 'setVulnerabilitiesEndpoint',
......
...@@ -12,6 +12,8 @@ import { ...@@ -12,6 +12,8 @@ import {
} from '~/vue_shared/security_reports/constants'; } from '~/vue_shared/security_reports/constants';
import * as types from './mutation_types'; import * as types from './mutation_types';
let vulnerabilitiesSource;
/** /**
* A lot of this file has duplicate actions in * A lot of this file has duplicate actions in
* ee/app/assets/javascripts/vue_shared/security_reports/store/actions.js * ee/app/assets/javascripts/vue_shared/security_reports/store/actions.js
...@@ -38,10 +40,17 @@ export const fetchVulnerabilities = ({ state, dispatch }, params = {}) => { ...@@ -38,10 +40,17 @@ export const fetchVulnerabilities = ({ state, dispatch }, params = {}) => {
return; return;
} }
dispatch('requestVulnerabilities'); dispatch('requestVulnerabilities');
// Cancel a pending request if there is one.
if (vulnerabilitiesSource) {
vulnerabilitiesSource.cancel();
}
vulnerabilitiesSource = axios.CancelToken.source();
axios({ axios({
method: 'GET', method: 'GET',
url: state.vulnerabilitiesEndpoint, url: state.vulnerabilitiesEndpoint,
cancelToken: vulnerabilitiesSource.token,
params, params,
}) })
.then((response) => { .then((response) => {
...@@ -49,7 +58,10 @@ export const fetchVulnerabilities = ({ state, dispatch }, params = {}) => { ...@@ -49,7 +58,10 @@ export const fetchVulnerabilities = ({ state, dispatch }, params = {}) => {
dispatch('receiveVulnerabilitiesSuccess', { headers, data }); dispatch('receiveVulnerabilitiesSuccess', { headers, data });
}) })
.catch((error) => { .catch((error) => {
dispatch('receiveVulnerabilitiesError', error?.response?.status); // Don't show an error message if the request was cancelled through the cancel token.
if (!axios.isCancel(error)) {
dispatch('receiveVulnerabilitiesError', error?.response?.status);
}
}); });
}; };
......
...@@ -3,13 +3,9 @@ import { SET_FILTER, SET_HIDE_DISMISSED } from '../modules/filters/mutation_type ...@@ -3,13 +3,9 @@ import { SET_FILTER, SET_HIDE_DISMISSED } from '../modules/filters/mutation_type
const refreshTypes = [`filters/${SET_FILTER}`, `filters/${SET_HIDE_DISMISSED}`]; const refreshTypes = [`filters/${SET_FILTER}`, `filters/${SET_HIDE_DISMISSED}`];
export default (store) => { export default (store) => {
const refreshVulnerabilities = (payload) => {
store.dispatch('vulnerabilities/fetchVulnerabilities', payload);
};
store.subscribe(({ type }) => { store.subscribe(({ type }) => {
if (refreshTypes.includes(type)) { if (refreshTypes.includes(type)) {
refreshVulnerabilities(store.state.filters.filters); store.dispatch('vulnerabilities/fetchVulnerabilities', store.state.filters.filters);
} }
}); });
}; };
...@@ -32,7 +32,7 @@ describe('Filter component', () => { ...@@ -32,7 +32,7 @@ describe('Filter component', () => {
wrapper = null; wrapper = null;
}); });
describe('severity', () => { describe('filters', () => {
beforeEach(() => { beforeEach(() => {
createWrapper(); createWrapper();
}); });
......
...@@ -164,6 +164,18 @@ describe('vulnerabilities actions', () => { ...@@ -164,6 +164,18 @@ describe('vulnerabilities actions', () => {
return testAction(actions.fetchVulnerabilities, {}, state); return testAction(actions.fetchVulnerabilities, {}, state);
}); });
}); });
describe('pending request', () => {
it('cancels the pending request when a new one is made', () => {
const dispatch = jest.fn();
const cancel = jest.fn();
jest.spyOn(axios.CancelToken, 'source').mockImplementation(() => ({ cancel }));
actions.fetchVulnerabilities({ state, dispatch });
actions.fetchVulnerabilities({ state, dispatch });
expect(cancel).toHaveBeenCalledTimes(1);
});
});
}); });
describe('receiveVulnerabilitiesSuccess', () => { describe('receiveVulnerabilitiesSuccess', () => {
...@@ -590,7 +602,7 @@ describe('vulnerability dismissal', () => { ...@@ -590,7 +602,7 @@ describe('vulnerability dismissal', () => {
); );
}); });
it('should load the previous page if there is no more vulnerabiliy on the current one and page > 1', () => { it('should load the previous page if there are no more vulnerabilities on the current one and page > 1', () => {
state.vulnerabilities = [mockDataVulnerabilities[0]]; state.vulnerabilities = [mockDataVulnerabilities[0]];
state.pageInfo.page = 3; state.pageInfo.page = 3;
......
...@@ -53,22 +53,16 @@ describe('vulnerabilities module mutations', () => { ...@@ -53,22 +53,16 @@ describe('vulnerabilities module mutations', () => {
}); });
describe('REQUEST_VULNERABILITIES', () => { describe('REQUEST_VULNERABILITIES', () => {
beforeEach(() => { it('should set properties to expected values', () => {
state.errorLoadingVulnerabilities = true; state.errorLoadingVulnerabilities = true;
state.loadingVulnerabilitiesErrorCode = 403; state.loadingVulnerabilitiesErrorCode = 403;
mutations[types.REQUEST_VULNERABILITIES](state); mutations[types.REQUEST_VULNERABILITIES](state);
});
it('should set `isLoadingVulnerabilities` to `true`', () => {
expect(state.isLoadingVulnerabilities).toBeTruthy();
});
it('should set `errorLoadingVulnerabilities` to `false`', () => { expect(state).toMatchObject({
expect(state.errorLoadingVulnerabilities).toBeFalsy(); isLoadingVulnerabilities: true,
}); errorLoadingVulnerabilities: false,
loadingVulnerabilitiesErrorCode: null,
it('should reset `loadingVulnerabilitiesErrorCode`', () => { });
expect(state.loadingVulnerabilitiesErrorCode).toBe(null);
}); });
}); });
......
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