Commit 2cfc665e authored by Olena Horal-Koretska's avatar Olena Horal-Koretska

Merge branch...

Merge branch '262109-fe-add-severity-counters-to-group-and-instance-vulnerability-reports-refactor' into 'master'

Refactor severity vulnerabilities count to support group- and instance- reports

See merge request gitlab-org/gitlab!47743
parents 2f8d34fb a18ff69d
......@@ -9,6 +9,7 @@ import SecurityDashboardLayout from './security_dashboard_layout.vue';
import VulnerabilitiesCountList from './vulnerability_count_list.vue';
import Filters from './first_class_vulnerability_filters.vue';
import CsvExportButton from './csv_export_button.vue';
import { vulnerabilitiesSeverityCountScopes } from '../constants';
export const BANNER_COOKIE_KEY = 'hide_vulnerabilities_introduction_banner';
......@@ -48,7 +49,7 @@ export default {
shoudShowAutoFixUserCallout,
};
},
inject: ['dashboardDocumentation', 'autoFixDocumentation'],
inject: ['dashboardDocumentation', 'autoFixDocumentation', 'projectFullPath'],
methods: {
handleFilterChange(filters) {
this.filters = filters;
......@@ -58,6 +59,7 @@ export default {
this.shoudShowAutoFixUserCallout = false;
},
},
vulnerabilitiesSeverityCountScopes,
};
</script>
......@@ -71,12 +73,17 @@ export default {
/>
<security-dashboard-layout>
<template #header>
<div class="mt-4 d-flex">
<h4 class="flex-grow mt-0 mb-0">{{ __('Vulnerabilities') }}</h4>
<div class="gl-mt-6 gl-display-flex">
<h4 class="gl-flex-grow-1 gl-my-0">{{ __('Vulnerabilities') }}</h4>
<csv-export-button :vulnerabilities-export-endpoint="vulnerabilitiesExportEndpoint" />
</div>
<project-pipeline-status :pipeline="pipeline" />
<vulnerabilities-count-list :filters="filters" />
<vulnerabilities-count-list
class="gl-mt-6"
:scope="$options.vulnerabilitiesSeverityCountScopes.project"
:full-path="projectFullPath"
:filters="filters"
/>
</template>
<template #sticky>
<filters @filterChange="handleFilterChange" />
......
<script>
import vulnerabilitySeveritiesCountQuery from '../graphql/project_vulnerability_severities_count.graphql';
import vulnerabilitySeveritiesCountQuery from '../graphql/vulnerability_severities_count.graphql';
import VulnerabilityCountListLayout from './vulnerability_count_list_layout.vue';
import { vulnerabilitiesSeverityCountScopes } from '../constants';
export default {
components: {
VulnerabilityCountListLayout,
},
inject: ['projectFullPath'],
props: {
scope: {
type: String,
required: true,
validator: value => Object.values(vulnerabilitiesSeverityCountScopes).includes(value),
},
fullPath: {
type: String,
required: false,
default: '',
},
filters: {
type: Object,
required: false,
......@@ -27,12 +38,19 @@ export default {
vulnerabilitiesCount: {
query: vulnerabilitySeveritiesCountQuery,
variables() {
const { scope, fullPath } = this;
const { instance, group, project } = vulnerabilitiesSeverityCountScopes;
return {
fullPath: this.projectFullPath,
fullPath,
isInstance: scope === instance,
isGroup: scope === group,
isProject: scope === project,
...this.filters,
};
},
update: ({ project }) => project?.vulnerabilitySeveritiesCount || {},
update(data) {
return data[this.scope]?.vulnerabilitySeveritiesCount || {};
},
result() {
this.queryError = false;
},
......
......@@ -46,7 +46,7 @@ export default {
</script>
<template>
<div class="vulnerabilities-count-list mt-4">
<div class="vulnerabilities-count-list">
<gl-alert v-if="showAlert" class="mb-4" variant="danger" @dismiss="onErrorDismiss">
{{
s__(
......
export const COLLAPSE_SECURITY_REPORTS_SUMMARY_LOCAL_STORAGE_KEY =
'hide_pipelines_security_reports_summary_details';
export default () => ({});
export const vulnerabilitiesSeverityCountScopes = {
instance: 'instance',
group: 'group',
project: 'project',
};
#import "ee/security_dashboard/graphql/project.fragment.graphql"
#import "./vulnerability_severities_count.fragment.graphql"
#import "ee/security_dashboard/graphql/project_vulnerability_severities_count.fragment.graphql"
query projectsQuery {
instanceSecurityDashboard {
projects {
nodes {
...Project
...VulnerabilitySeveritiesCount
...ProjectVulnerabilitySeveritiesCount
}
}
}
......
#import "ee/security_dashboard/graphql/project.fragment.graphql"
#import "./vulnerability_severities_count.fragment.graphql"
#import "ee/security_dashboard/graphql/project_vulnerability_severities_count.fragment.graphql"
query groupVulnerabilityGrades($fullPath: ID!) {
group(fullPath: $fullPath) {
......@@ -8,7 +8,7 @@ query groupVulnerabilityGrades($fullPath: ID!) {
projects {
nodes {
...Project
...VulnerabilitySeveritiesCount
...ProjectVulnerabilitySeveritiesCount
}
}
}
......
#import "ee/security_dashboard/graphql/project.fragment.graphql"
#import "./vulnerability_severities_count.fragment.graphql"
#import "ee/security_dashboard/graphql/project_vulnerability_severities_count.fragment.graphql"
query instanceVulnerabilityGrades {
instanceSecurityDashboard {
......@@ -8,7 +8,7 @@ query instanceVulnerabilityGrades {
projects {
nodes {
...Project
...VulnerabilitySeveritiesCount
...ProjectVulnerabilitySeveritiesCount
}
}
}
......
#import "ee/security_dashboard/graphql/vulnerability_severities_count.fragment.graphql"
fragment ProjectVulnerabilitySeveritiesCount on Project {
vulnerabilitySeveritiesCount {
...VulnerabilitySeveritiesCount
}
}
query vulnerabilitySeveritiesCount(
$fullPath: ID!
$severity: [VulnerabilitySeverity!]
$reportType: [VulnerabilityReportType!]
$scanner: [String!]
$state: [VulnerabilityState!]
) {
project(fullPath: $fullPath) {
vulnerabilitySeveritiesCount(
severity: $severity
reportType: $reportType
scanner: $scanner
state: $state
) {
critical
high
low
medium
info
unknown
}
}
}
fragment VulnerabilitySeveritiesCount on Project {
vulnerabilitySeveritiesCount {
fragment VulnerabilitySeveritiesCount on VulnerabilitySeveritiesCount {
critical
high
info
low
medium
unknown
}
}
#import "ee/security_dashboard/graphql/vulnerability_severities_count.fragment.graphql"
query vulnerabilitySeveritiesCount(
$fullPath: ID = ""
$projectId: [ID!]
$severity: [VulnerabilitySeverity!]
$reportType: [VulnerabilityReportType!]
$scanner: [String!]
$state: [VulnerabilityState!]
$isGroup: Boolean = false
$isProject: Boolean = false
$isInstance: Boolean = false
) {
instance: instanceSecurityDashboard @include(if: $isInstance) {
vulnerabilitySeveritiesCount(
projectId: $projectId
severity: $severity
reportType: $reportType
scanner: $scanner
state: $state
) {
...VulnerabilitySeveritiesCount
}
}
group(fullPath: $fullPath) @include(if: $isGroup) {
vulnerabilitySeveritiesCount(
projectId: $projectId
severity: $severity
reportType: $reportType
scanner: $scanner
state: $state
) {
...VulnerabilitySeveritiesCount
}
}
project(fullPath: $fullPath) @include(if: $isProject) {
vulnerabilitySeveritiesCount(
severity: $severity
reportType: $reportType
scanner: $scanner
state: $state
) {
...VulnerabilitySeveritiesCount
}
}
}
......@@ -84,7 +84,8 @@ describe('First class Project Security Dashboard component', () => {
it('should pass down the properties correctly to the vulnerability count list', () => {
expect(findVulnerabilityCountList().props()).toEqual({
projectFullPath: props.projectFullPath,
scope: 'project',
fullPath: provide.projectFullPath,
filters,
});
});
......
......@@ -7,11 +7,9 @@ describe('Vulnerabilities count list component', () => {
const findVulnerabilityLayout = () => wrapper.find(VulnerabilityCountListLayout);
const createWrapper = ({ query } = {}) => {
const createWrapper = ({ query = { isLoading: false }, props = { scope: 'project' } } = {}) => {
return shallowMount(VulnerabilityCountList, {
provide: {
projectFullPath: '/root/security-project',
},
propsData: props,
mocks: {
$apollo: { queries: { vulnerabilitiesCount: query } },
},
......@@ -60,6 +58,27 @@ describe('Vulnerabilities count list component', () => {
});
});
describe.each`
givenScope | expectedContainedQueryVariables
${'instance'} | ${{ isInstance: true, isGroup: false, isProject: false }}
${'group'} | ${{ isInstance: false, isGroup: true, isProject: false }}
${'project'} | ${{ isInstance: false, isGroup: false, isProject: true }}
`(
'when the scope prop is set to "$givenScope"',
({ givenScope, expectedContainedQueryVariables }) => {
beforeEach(() => {
wrapper = createWrapper({ props: { scope: givenScope } });
return wrapper.vm.$nextTick();
});
it('should pass the correct variables to the GraphQL query', () => {
expect(
wrapper.vm.$options.apollo.vulnerabilitiesCount.variables.call(wrapper.vm),
).toMatchObject(expectedContainedQueryVariables);
});
},
);
describe('when there is an error', () => {
beforeEach(() => {
wrapper = createWrapper({ query: {} });
......
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