Commit 9936151e authored by Savas Vedova's avatar Savas Vedova

Add export button to group dashboard

- Add tests
- Add documentation
parent c6158f0a
......@@ -120,6 +120,21 @@ vulnerabilities are not included either.
Read more on how to [interact with the vulnerabilities](../index.md#interacting-with-the-vulnerabilities).
### Export vulnerabilities
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/213013) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 13.1.
You can export all your vulnerabilities as CSV by clicking the **{upload}** **Export** button
located at the top right of the **Group Security Dashboard**. After the report builds, the CSV
report downloads to your local machine. The report contains all vulnerabilities for the projects
defined in the **Group Security Dashboard**, as filters don't apply to the export function.
NOTE: **Note:**
It may take several minutes for the download to start if your project contains thousands of
vulnerabilities. Don't close the page until the download finishes.
![CSV Export Button](img/group_security_dashboard_export_csv_v13_1.png)
## Instance Security Dashboard
> [Introduced](https://gitlab.com/gitlab-org/gitlab/-/issues/6953) in [GitLab Ultimate](https://about.gitlab.com/pricing/) 12.8.
......
......@@ -3,6 +3,7 @@ import SecurityDashboardLayout from 'ee/security_dashboard/components/security_d
import GroupSecurityVulnerabilities from 'ee/security_dashboard/components/first_class_group_security_dashboard_vulnerabilities.vue';
import Filters from 'ee/security_dashboard/components/first_class_vulnerability_filters.vue';
import VulnerabilityChart from 'ee/security_dashboard/components/first_class_vulnerability_chart.vue';
import CsvExportButton from './csv_export_button.vue';
import VulnerabilitySeverity from './vulnerability_severity.vue';
import vulnerabilityHistoryQuery from '../graphql/group_vulnerability_history.graphql';
......@@ -13,6 +14,7 @@ export default {
VulnerabilitySeverity,
VulnerabilityChart,
Filters,
CsvExportButton,
},
props: {
dashboardDocumentation: {
......@@ -31,6 +33,10 @@ export default {
type: String,
required: true,
},
vulnerabilitiesExportEndpoint: {
type: String,
required: true,
},
},
data() {
return {
......@@ -52,6 +58,12 @@ export default {
<template>
<security-dashboard-layout>
<template #header>
<header class="page-title-holder flex-fill d-flex align-items-center">
<h2 class="page-title flex-grow">{{ s__('SecurityReports|Group Security Dashboard') }}</h2>
<csv-export-button :vulnerabilities-export-endpoint="vulnerabilitiesExportEndpoint" />
</header>
</template>
<template #sticky>
<filters :projects="projects" @filterChange="handleFilterChange" />
</template>
......
......@@ -46,7 +46,6 @@ export default {
type: String,
required: true,
},
vulnerabilitiesExportEndpoint: {
type: String,
required: true,
......
......@@ -42,6 +42,7 @@ export default (
securityDashboardHelpPath: el.dataset.securityDashboardHelpPath,
projectAddEndpoint: el.dataset.projectAddEndpoint,
projectListEndpoint: el.dataset.projectListEndpoint,
vulnerabilitiesExportEndpoint: el.dataset.vulnerabilitiesExportEndpoint,
};
let component;
......@@ -49,7 +50,6 @@ export default (
if (dashboardType === DASHBOARD_TYPES.PROJECT) {
component = FirstClassProjectSecurityDashboard;
props.projectFullPath = el.dataset.projectFullPath;
props.vulnerabilitiesExportEndpoint = el.dataset.vulnerabilitiesExportEndpoint;
props.userCalloutId = el.dataset.userCalloutId;
props.userCalloutsPath = el.dataset.userCalloutsPath;
props.showIntroductionBanner = parseBoolean(el.dataset.showIntroductionBanner);
......@@ -60,7 +60,6 @@ export default (
} else if (dashboardType === DASHBOARD_TYPES.INSTANCE) {
component = FirstClassInstanceSecurityDashboard;
props.vulnerableProjectsEndpoint = el.dataset.vulnerableProjectsEndpoint;
props.vulnerabilitiesExportEndpoint = el.dataset.vulnerabilitiesExportEndpoint;
}
const router = createRouter();
......
---
title: Add csv export button to group security dashboard
merge_request: 34374
author:
type: added
......@@ -4,6 +4,7 @@ import FirstClassGroupDashboard from 'ee/security_dashboard/components/first_cla
import FirstClassGroupVulnerabilities from 'ee/security_dashboard/components/first_class_group_security_dashboard_vulnerabilities.vue';
import VulnerabilitySeverity from 'ee/security_dashboard/components/vulnerability_severity.vue';
import VulnerabilityChart from 'ee/security_dashboard/components/first_class_vulnerability_chart.vue';
import CsvExportButton from 'ee/security_dashboard/components/csv_export_button.vue';
import Filters from 'ee/security_dashboard/components/first_class_vulnerability_filters.vue';
describe('First Class Group Dashboard Component', () => {
......@@ -13,10 +14,12 @@ describe('First Class Group Dashboard Component', () => {
const emptyStateSvgPath = 'empty-state-path';
const groupFullPath = 'group-full-path';
const vulnerableProjectsEndpoint = '/vulnerable/projects';
const vulnerabilitiesExportEndpoint = '/vulnerabilities/exports';
const findGroupVulnerabilities = () => wrapper.find(FirstClassGroupVulnerabilities);
const findVulnerabilitySeverity = () => wrapper.find(VulnerabilitySeverity);
const findVulnerabilityChart = () => wrapper.find(VulnerabilityChart);
const findCsvExportButton = () => wrapper.find(CsvExportButton);
const findFilters = () => wrapper.find(Filters);
const createWrapper = () => {
......@@ -26,6 +29,7 @@ describe('First Class Group Dashboard Component', () => {
emptyStateSvgPath,
groupFullPath,
vulnerableProjectsEndpoint,
vulnerabilitiesExportEndpoint,
},
stubs: {
SecurityDashboardLayout,
......@@ -78,4 +82,10 @@ describe('First Class Group Dashboard Component', () => {
it('displays the vulnerability severity in an aside', () => {
expect(findVulnerabilitySeverity().exists()).toBe(true);
});
it('displays the csv export button', () => {
expect(findCsvExportButton().props('vulnerabilitiesExportEndpoint')).toBe(
vulnerabilitiesExportEndpoint,
);
});
});
......@@ -19647,6 +19647,9 @@ msgstr ""
msgid "SecurityReports|False positive"
msgstr ""
msgid "SecurityReports|Group Security Dashboard"
msgstr ""
msgid "SecurityReports|Hide dismissed"
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