Commit 247cee62 authored by samdbeckham's avatar samdbeckham

Adds error states for the group sec dashboard

- Adds seperate error states for count and list failures
- Displays different errors when one, or both fails
- Updates tests to match
parent 029b9639
...@@ -7,7 +7,6 @@ import Tab from '~/vue_shared/components/tabs/tab.vue'; ...@@ -7,7 +7,6 @@ import Tab from '~/vue_shared/components/tabs/tab.vue';
import IssueModal from 'ee/vue_shared/security_reports/components/modal.vue'; import IssueModal from 'ee/vue_shared/security_reports/components/modal.vue';
import SecurityDashboardTable from './security_dashboard_table.vue'; import SecurityDashboardTable from './security_dashboard_table.vue';
import VulnerabilityCountList from './vulnerability_count_list.vue'; import VulnerabilityCountList from './vulnerability_count_list.vue';
import SvgBlankState from '~/pipelines/components/blank_state.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
import popover from '~/vue_shared/directives/popover'; import popover from '~/vue_shared/directives/popover';
...@@ -20,7 +19,6 @@ export default { ...@@ -20,7 +19,6 @@ export default {
Icon, Icon,
IssueModal, IssueModal,
SecurityDashboardTable, SecurityDashboardTable,
SvgBlankState,
Tab, Tab,
Tabs, Tabs,
VulnerabilityCountList, VulnerabilityCountList,
...@@ -30,14 +28,6 @@ export default { ...@@ -30,14 +28,6 @@ export default {
type: String, type: String,
required: true, required: true,
}, },
emptyStateSvgPath: {
type: String,
required: true,
},
errorStateSvgPath: {
type: String,
required: true,
},
vulnerabilitiesEndpoint: { vulnerabilitiesEndpoint: {
type: String, type: String,
required: true, required: true,
...@@ -49,7 +39,7 @@ export default { ...@@ -49,7 +39,7 @@ export default {
}, },
computed: { computed: {
...mapGetters('vulnerabilities', ['vulnerabilitiesCountByReportType']), ...mapGetters('vulnerabilities', ['vulnerabilitiesCountByReportType']),
...mapState('vulnerabilities', ['hasError', 'modal']), ...mapState('vulnerabilities', ['modal']),
sastCount() { sastCount() {
return this.vulnerabilitiesCountByReportType('sast'); return this.vulnerabilitiesCountByReportType('sast');
}, },
...@@ -96,50 +86,39 @@ export default { ...@@ -96,50 +86,39 @@ export default {
<template> <template>
<div> <div>
<div class="flash-container"></div> <vulnerability-count-list />
<svg-blank-state <tabs stop-propagation>
v-if="hasError" <tab active>
:svg-path="errorStateSvgPath" <template slot="title">
:message="s__(`Security Reports|There was an error fetching the dashboard. <span>{{ __('SAST') }}</span>
Please try again in a few moments or contact your support team.`)" <span
/> v-if="sastCount"
<div v-else> class="badge badge-pill"
<vulnerability-count-list /> >
<tabs stop-propagation> {{ sastCount }}
<tab active> </span>
<template slot="title"> <span
<span>{{ __('SAST') }}</span> v-popover="popoverOptions"
<span class="text-muted prepend-left-4"
v-if="sastCount" :aria-label="__('help')"
class="badge badge-pill" >
> <icon
{{ sastCount }} name="question"
</span> class="vertical-align-middle"
<span />
v-popover="popoverOptions" </span>
class="text-muted prepend-left-4" </template>
:aria-label="__('help')"
>
<icon
name="question"
class="vertical-align-middle"
/>
</span>
</template>
<security-dashboard-table <security-dashboard-table />
:empty-state-svg-path="emptyStateSvgPath" </tab>
/> </tabs>
</tab> <issue-modal
</tabs> :modal="modal"
<issue-modal :can-create-issue-permission="true"
:modal="modal" :can-create-feedback-permission="true"
:can-create-issue-permission="true" @createNewIssue="createIssue({ vulnerability: modal.vulnerability })"
:can-create-feedback-permission="true" @dismissIssue="dismissVulnerability({ vulnerability: modal.vulnerability })"
@createNewIssue="createIssue({ vulnerability: modal.vulnerability })" @revertDismissIssue="undoDismissal({ vulnerability: modal.vulnerability })"
@dismissIssue="dismissVulnerability({ vulnerability: modal.vulnerability })" />
@revertDismissIssue="undoDismissal({ vulnerability: modal.vulnerability })"
/>
</div>
</div> </div>
</template> </template>
...@@ -10,10 +10,19 @@ export default { ...@@ -10,10 +10,19 @@ export default {
SecurityDashboardTableRow, SecurityDashboardTableRow,
}, },
computed: { computed: {
...mapState('vulnerabilities', ['vulnerabilities', 'pageInfo', 'isLoadingVulnerabilities']), ...mapState('vulnerabilities', [
'vulnerabilities',
'pageInfo',
'isLoadingVulnerabilities',
'errorLoadingVulnerabilities',
'errorLoadingVulnerabilitiesCount',
]),
showPagination() { showPagination() {
return this.pageInfo && this.pageInfo.total; return this.pageInfo && this.pageInfo.total;
}, },
showError() {
return this.errorLoadingVulnerabilities && !this.errorLoadingVulnerabilitiesCount;
},
}, },
created() { created() {
this.fetchVulnerabilities(); this.fetchVulnerabilities();
...@@ -49,6 +58,16 @@ export default { ...@@ -49,6 +58,16 @@ export default {
{{ s__('Reports|Confidence') }} {{ s__('Reports|Confidence') }}
</div> </div>
</div> </div>
<div class="flash-container">
<div
v-if="showError"
class="flash-alert">
<div class="flash-text container-fluid container-limited limit-container-width">
{{ s__('Security Dashboard|Error fetching the vulnerability list. Please check your network connection and try again.') }}
</div>
</div>
</div>
<div v-if="isLoadingVulnerabilities"> <div v-if="isLoadingVulnerabilities">
<security-dashboard-table-row <security-dashboard-table-row
......
...@@ -33,7 +33,7 @@ export default { ...@@ -33,7 +33,7 @@ export default {
{{ severity }} {{ severity }}
</div> </div>
<div class="vulnerability-count-body"> <div class="vulnerability-count-body">
<span v-if="isLoading">&nbsp;</span> <span v-if="isLoading">&mdash;</span>
<span v-else>{{ count }}</span> <span v-else>{{ count }}</span>
</div> </div>
</div> </div>
......
...@@ -12,19 +12,39 @@ export default { ...@@ -12,19 +12,39 @@ export default {
}, },
computed: { computed: {
...mapGetters('vulnerabilities', ['vulnerabilitiesCountBySeverity']), ...mapGetters('vulnerabilities', ['vulnerabilitiesCountBySeverity']),
...mapState('vulnerabilities', ['isLoadingVulnerabilitiesCount']), ...mapState('vulnerabilities', [
'isLoadingVulnerabilitiesCount',
'errorLoadingVulnerabilitiesCount',
'errorLoadingVulnerabilities',
]),
counts() { counts() {
return SEVERITIES.map(severity => { return SEVERITIES.map(severity => {
const count = this.vulnerabilitiesCountBySeverity(severity); const count = this.vulnerabilitiesCountBySeverity(severity);
return { severity, count }; return { severity, count };
}); });
}, },
dashboardError() {
return this.errorLoadingVulnerabilitiesCount && this.errorLoadingVulnerabilities;
},
countError() {
return this.errorLoadingVulnerabilitiesCount && !this.errorLoadingVulnerabilities;
},
}, },
}; };
</script> </script>
<template> <template>
<div class="vulnerabilities-count-list"> <div class="vulnerabilities-count-list">
<div class="flash-container">
<div
v-if="dashboardError"
class="flash-alert">
<div class="flash-text container-fluid container-limited limit-container-width">
{{ s__('Security Dashboard|Error fetching the dashboard data. Please check your network connection and try again.') }}
</div>
</div>
</div>
<div class="row"> <div class="row">
<div <div
v-for="count in counts" v-for="count in counts"
...@@ -38,6 +58,16 @@ export default { ...@@ -38,6 +58,16 @@ export default {
/> />
</div> </div>
</div> </div>
<div class="flash-container">
<div
v-if="countError"
class="flash-alert">
<div class="flash-text container-fluid container-limited limit-container-width">
{{ s__('Security Dashboard|Error fetching the vulnerability counts. Please check your network connection and try again.') }}
</div>
</div>
</div>
</div> </div>
</template> </template>
......
...@@ -112,9 +112,16 @@ export const receiveCreateIssueSuccess = ({ commit }, payload) => { ...@@ -112,9 +112,16 @@ export const receiveCreateIssueSuccess = ({ commit }, payload) => {
commit(types.RECEIVE_CREATE_ISSUE_SUCCESS, payload); commit(types.RECEIVE_CREATE_ISSUE_SUCCESS, payload);
}; };
export const receiveCreateIssueError = ({ commit }) => { export const receiveCreateIssueError = ({ commit }, { flashError }) => {
commit(types.RECEIVE_CREATE_ISSUE_ERROR); commit(types.RECEIVE_CREATE_ISSUE_ERROR);
createFlash(s__('Security Reports|There was an error creating the issue.'));
if (flashError) {
createFlash(
s__('Security Reports|There was an error creating the issue.'),
'alert',
document.querySelector('.ci-table'),
);
}
}; };
export const dismissVulnerability = ({ dispatch }, { vulnerability, flashError }) => { export const dismissVulnerability = ({ dispatch }, { vulnerability, flashError }) => {
...@@ -152,7 +159,11 @@ export const receiveDismissVulnerabilitySuccess = ({ commit }, payload) => { ...@@ -152,7 +159,11 @@ export const receiveDismissVulnerabilitySuccess = ({ commit }, payload) => {
export const receiveDismissVulnerabilityError = ({ commit }, { flashError }) => { export const receiveDismissVulnerabilityError = ({ commit }, { flashError }) => {
commit(types.RECEIVE_DISMISS_VULNERABILITY_ERROR); commit(types.RECEIVE_DISMISS_VULNERABILITY_ERROR);
if (flashError) { if (flashError) {
createFlash(s__('Security Reports|There was an error dismissing the issue.')); createFlash(
s__('Security Reports|There was an error dismissing the vulnerability.'),
'alert',
document.querySelector('.ci-table'),
);
} }
}; };
...@@ -185,7 +196,11 @@ export const receiveUndoDismissalSuccess = ({ commit }, payload) => { ...@@ -185,7 +196,11 @@ export const receiveUndoDismissalSuccess = ({ commit }, payload) => {
export const receiveUndoDismissalError = ({ commit }, { flashError }) => { export const receiveUndoDismissalError = ({ commit }, { flashError }) => {
commit(types.RECEIVE_UNDO_DISMISSAL_ERROR); commit(types.RECEIVE_UNDO_DISMISSAL_ERROR);
if (flashError) { if (flashError) {
createFlash(s__('Security Reports|There was an error undoing this dismissal.')); createFlash(
s__('Security Reports|There was an error undoing this dismissal.'),
'alert',
document.querySelector('.ci-table'),
);
} }
}; };
......
...@@ -9,7 +9,7 @@ export default { ...@@ -9,7 +9,7 @@ export default {
}, },
[types.REQUEST_VULNERABILITIES](state) { [types.REQUEST_VULNERABILITIES](state) {
state.isLoadingVulnerabilities = true; state.isLoadingVulnerabilities = true;
state.hasError = false; state.errorLoadingVulnerabilities = false;
}, },
[types.RECEIVE_VULNERABILITIES_SUCCESS](state, payload) { [types.RECEIVE_VULNERABILITIES_SUCCESS](state, payload) {
state.isLoadingVulnerabilities = false; state.isLoadingVulnerabilities = false;
...@@ -18,14 +18,14 @@ export default { ...@@ -18,14 +18,14 @@ export default {
}, },
[types.RECEIVE_VULNERABILITIES_ERROR](state) { [types.RECEIVE_VULNERABILITIES_ERROR](state) {
state.isLoadingVulnerabilities = false; state.isLoadingVulnerabilities = false;
state.hasError = true; state.errorLoadingVulnerabilities = true;
}, },
[types.SET_VULNERABILITIES_COUNT_ENDPOINT](state, payload) { [types.SET_VULNERABILITIES_COUNT_ENDPOINT](state, payload) {
state.vulnerabilitiesCountEndpoint = payload; state.vulnerabilitiesCountEndpoint = payload;
}, },
[types.REQUEST_VULNERABILITIES_COUNT](state) { [types.REQUEST_VULNERABILITIES_COUNT](state) {
state.isLoadingVulnerabilitiesCount = true; state.isLoadingVulnerabilitiesCount = true;
state.hasError = false; state.errorLoadingVulnerabilitiesCount = false;
}, },
[types.RECEIVE_VULNERABILITIES_COUNT_SUCCESS](state, payload) { [types.RECEIVE_VULNERABILITIES_COUNT_SUCCESS](state, payload) {
state.isLoadingVulnerabilitiesCount = false; state.isLoadingVulnerabilitiesCount = false;
...@@ -33,7 +33,7 @@ export default { ...@@ -33,7 +33,7 @@ export default {
}, },
[types.RECEIVE_VULNERABILITIES_COUNT_ERROR](state) { [types.RECEIVE_VULNERABILITIES_COUNT_ERROR](state) {
state.isLoadingVulnerabilitiesCount = false; state.isLoadingVulnerabilitiesCount = false;
state.hasError = true; state.errorLoadingVulnerabilitiesCount = true;
}, },
[types.SET_MODAL_DATA](state, payload) { [types.SET_MODAL_DATA](state, payload) {
const { vulnerability } = payload; const { vulnerability } = payload;
......
import { s__ } from '~/locale'; import { s__ } from '~/locale';
export default () => ({ export default () => ({
hasError: false,
isLoadingVulnerabilities: true, isLoadingVulnerabilities: true,
errorLoadingVulnerabilities: false,
isLoadingVulnerabilitiesCount: true, isLoadingVulnerabilitiesCount: true,
errorLoadingVulnerabilitiesCount: false,
pageInfo: {}, pageInfo: {},
vulnerabilities: [], vulnerabilities: [],
vulnerabilitiesCount: {}, vulnerabilitiesCount: {},
......
---
title: Adds split error states for the group security dashboard
merge_request: 8208
author:
type: changed
...@@ -21,7 +21,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -21,7 +21,7 @@ describe('vulnerabilities module mutations', () => {
beforeEach(() => { beforeEach(() => {
state = { state = {
...createState(), ...createState(),
hasError: true, errorLoadingVulnerabilities: true,
}; };
mutations[types.REQUEST_VULNERABILITIES](state); mutations[types.REQUEST_VULNERABILITIES](state);
}); });
...@@ -30,8 +30,8 @@ describe('vulnerabilities module mutations', () => { ...@@ -30,8 +30,8 @@ describe('vulnerabilities module mutations', () => {
expect(state.isLoadingVulnerabilities).toBeTruthy(); expect(state.isLoadingVulnerabilities).toBeTruthy();
}); });
it('should set `hasError` to `false`', () => { it('should set `errorLoadingVulnerabilities` to `false`', () => {
expect(state.hasError).toBeFalsy(); expect(state.errorLoadingVulnerabilities).toBeFalsy();
}); });
}); });
...@@ -88,7 +88,7 @@ describe('vulnerabilities module mutations', () => { ...@@ -88,7 +88,7 @@ describe('vulnerabilities module mutations', () => {
beforeEach(() => { beforeEach(() => {
state = { state = {
...createState(), ...createState(),
hasError: true, errorLoadingVulnerabilitiesCount: true,
}; };
mutations[types.REQUEST_VULNERABILITIES_COUNT](state); mutations[types.REQUEST_VULNERABILITIES_COUNT](state);
}); });
...@@ -97,8 +97,8 @@ describe('vulnerabilities module mutations', () => { ...@@ -97,8 +97,8 @@ describe('vulnerabilities module mutations', () => {
expect(state.isLoadingVulnerabilitiesCount).toBeTruthy(); expect(state.isLoadingVulnerabilitiesCount).toBeTruthy();
}); });
it('should set `hasError` to `false`', () => { it('should set `errorLoadingVulnerabilitiesCount` to `false`', () => {
expect(state.hasError).toBeFalsy(); expect(state.errorLoadingVulnerabilitiesCount).toBeFalsy();
}); });
}); });
......
...@@ -7016,6 +7016,15 @@ msgstr "" ...@@ -7016,6 +7016,15 @@ msgstr ""
msgid "Security Dashboard" msgid "Security Dashboard"
msgstr "" msgstr ""
msgid "Security Dashboard|Error fetching the dashboard data. Please check your network connection and try again."
msgstr ""
msgid "Security Dashboard|Error fetching the vulnerability counts. Please check your network connection and try again."
msgstr ""
msgid "Security Dashboard|Error fetching the vulnerability list. Please check your network connection and try again."
msgstr ""
msgid "Security Dashboard|Issue Created" msgid "Security Dashboard|Issue Created"
msgstr "" msgstr ""
...@@ -7037,15 +7046,9 @@ msgstr "" ...@@ -7037,15 +7046,9 @@ msgstr ""
msgid "Security Reports|There was an error creating the issue." msgid "Security Reports|There was an error creating the issue."
msgstr "" msgstr ""
msgid "Security Reports|There was an error dismissing the issue."
msgstr ""
msgid "Security Reports|There was an error dismissing the vulnerability." msgid "Security Reports|There was an error dismissing the vulnerability."
msgstr "" msgstr ""
msgid "Security Reports|There was an error fetching the dashboard. Please try again in a few moments or contact your support team."
msgstr ""
msgid "Security Reports|There was an error undoing the dismissal." msgid "Security Reports|There was an error undoing the dismissal."
msgstr "" msgstr ""
......
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