Skip to content
代码片段 群组 项目
提交 affa007f 编辑于 作者: Savas Vedova's avatar Savas Vedova 提交者: David Pisek
浏览文件

Enable group level vulnerability report grouping

Introduce the feature behind the `:group_level_vulnerability_report_grouping`
feature flag.
上级 1b70913d
No related branches found
No related tags found
无相关合并请求
---
name: group_level_vulnerability_report_grouping
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/137778
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/432778
milestone: '16.7'
type: development
group: group::threat insights
default_enabled: false
...@@ -8,6 +8,9 @@ export default { ...@@ -8,6 +8,9 @@ export default {
VulnerabilityReportTabs, VulnerabilityReportTabs,
}, },
inject: ['hasProjects'], inject: ['hasProjects'],
provide: {
isGroupVulnerabilityReport: true,
},
}; };
</script> </script>
......
...@@ -11,6 +11,7 @@ import { createAlert } from '~/alert'; ...@@ -11,6 +11,7 @@ import { createAlert } from '~/alert';
import countsQuery from 'ee/security_dashboard/graphql/queries/vulnerability_severities_count.query.graphql'; import countsQuery from 'ee/security_dashboard/graphql/queries/vulnerability_severities_count.query.graphql';
import SummaryHighlights from 'ee/vue_shared/security_reports/components/summary_highlights.vue'; import SummaryHighlights from 'ee/vue_shared/security_reports/components/summary_highlights.vue';
import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue'; import UserCalloutDismisser from '~/vue_shared/components/user_callout_dismisser.vue';
import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin';
import { __, s__ } from '~/locale'; import { __, s__ } from '~/locale';
import { getIdFromGraphQLId } from '~/graphql_shared/utils'; import { getIdFromGraphQLId } from '~/graphql_shared/utils';
import { scrollToElement, contentTop } from '~/lib/utils/common_utils'; import { scrollToElement, contentTop } from '~/lib/utils/common_utils';
...@@ -54,10 +55,14 @@ export default { ...@@ -54,10 +55,14 @@ export default {
GlSprintf, GlSprintf,
UserCalloutDismisser, UserCalloutDismisser,
}, },
mixins: [glFeatureFlagsMixin()],
inject: { inject: {
fullPath: { fullPath: {
default: '', default: '',
}, },
isGroupVulnerabilityReport: {
default: false,
},
isProjectVulnerabilityReport: { isProjectVulnerabilityReport: {
default: false, default: false,
}, },
...@@ -109,7 +114,18 @@ export default { ...@@ -109,7 +114,18 @@ export default {
return this.filterDropdowns.includes(FILTERS.PROJECT); return this.filterDropdowns.includes(FILTERS.PROJECT);
}, },
shouldShowGroupByButton() { shouldShowGroupByButton() {
return this.isProjectVulnerabilityReport && this.isVisible; if (!this.isVisible) {
return false;
}
if (
this.isGroupVulnerabilityReport &&
this.glFeatures.groupLevelVulnerabilityReportGrouping
) {
return true;
}
return this.isProjectVulnerabilityReport;
}, },
groups() { groups() {
if (!this.groupBy) { if (!this.groupBy) {
...@@ -310,8 +326,8 @@ export default { ...@@ -310,8 +326,8 @@ export default {
const queryName = `groupCounts.${value}`; const queryName = `groupCounts.${value}`;
const variables = { const variables = {
fullPath: this.fullPath, fullPath: this.fullPath,
isProject: true, isProject: this.isProjectVulnerabilityReport,
isGroup: false, isGroup: this.isGroupVulnerabilityReport,
isInstance: false, isInstance: false,
...this.graphqlFilters, ...this.graphqlFilters,
...this.formatGroupFilters(value), ...this.formatGroupFilters(value),
...@@ -329,8 +345,10 @@ export default { ...@@ -329,8 +345,10 @@ export default {
return variables; return variables;
}, },
update(data) { update(data) {
this.$set(this.groupCounts, value, data.project?.vulnerabilitySeveritiesCount); const level = this.isProjectVulnerabilityReport ? 'project' : 'group';
return data.project?.vulnerabilitySeveritiesCount;
this.$set(this.groupCounts, value, data[level]?.vulnerabilitySeveritiesCount);
return data[level]?.vulnerabilitySeveritiesCount;
}, },
error() { error() {
createAlert({ createAlert({
......
...@@ -14,6 +14,7 @@ class VulnerabilitiesController < Groups::ApplicationController ...@@ -14,6 +14,7 @@ class VulnerabilitiesController < Groups::ApplicationController
before_action do before_action do
push_frontend_feature_flag(:activity_filter_has_mr, @project) push_frontend_feature_flag(:activity_filter_has_mr, @project)
push_frontend_feature_flag(:activity_filter_has_remediations, @project) push_frontend_feature_flag(:activity_filter_has_remediations, @project)
push_frontend_feature_flag(:group_level_vulnerability_report_grouping, @project)
end end
def index def index
......
...@@ -40,7 +40,9 @@ describe('Vulnerability report component', () => { ...@@ -40,7 +40,9 @@ describe('Vulnerability report component', () => {
fullPath, fullPath,
filterFn, filterFn,
scanners, scanners,
isGroupVulnerabilityReport,
isProjectVulnerabilityReport = true, isProjectVulnerabilityReport = true,
glFeatures,
shouldShowCallout = false, shouldShowCallout = false,
} = {}) => { } = {}) => {
router = new VueRouter({ router = new VueRouter({
...@@ -54,9 +56,11 @@ describe('Vulnerability report component', () => { ...@@ -54,9 +56,11 @@ describe('Vulnerability report component', () => {
apolloProvider, apolloProvider,
router, router,
provide: { provide: {
isGroupVulnerabilityReport,
isProjectVulnerabilityReport, isProjectVulnerabilityReport,
fullPath, fullPath,
scanners, scanners,
glFeatures,
}, },
propsData: { propsData: {
query, query,
...@@ -212,13 +216,14 @@ describe('Vulnerability report component', () => { ...@@ -212,13 +216,14 @@ describe('Vulnerability report component', () => {
}); });
}); });
describe('group by', () => { describe.each`
dashboardType
${DASHBOARD_TYPES.PROJECT}
${DASHBOARD_TYPES.GROUP}
`('group by: $dashboardType', ({ dashboardType }) => {
const counts = { critical: 1, high: 2, medium: 5, low: 4, info: 3, unknown: 6 }; const counts = { critical: 1, high: 2, medium: 5, low: 4, info: 3, unknown: 6 };
const getCountsRequestHandler = ({ const getCountsRequestHandler = ({ data = counts } = {}) => {
data = counts,
dashboardType = DASHBOARD_TYPES.PROJECT,
} = {}) => {
return jest.fn().mockResolvedValue({ return jest.fn().mockResolvedValue({
data: { data: {
[dashboardType]: { [dashboardType]: {
...@@ -249,10 +254,17 @@ describe('Vulnerability report component', () => { ...@@ -249,10 +254,17 @@ describe('Vulnerability report component', () => {
} = {}) => { } = {}) => {
createWrapper({ createWrapper({
apolloProvider: createMockApollo([[countsQuery, getCountsRequestHandler()]]), apolloProvider: createMockApollo([[countsQuery, getCountsRequestHandler()]]),
isProjectVulnerabilityReport: true, isProjectVulnerabilityReport: dashboardType === DASHBOARD_TYPES.PROJECT,
isGroupVulnerabilityReport: dashboardType === DASHBOARD_TYPES.GROUP,
fullPath: 'gitlab-org/gitlab', fullPath: 'gitlab-org/gitlab',
shouldShowCallout, shouldShowCallout,
scanners, scanners,
glFeatures:
DASHBOARD_TYPES.GROUP === dashboardType
? {
groupLevelVulnerabilityReportGrouping: true,
}
: {},
}); });
// Reset filters // Reset filters
...@@ -267,26 +279,6 @@ describe('Vulnerability report component', () => { ...@@ -267,26 +279,6 @@ describe('Vulnerability report component', () => {
await waitForPromises(); await waitForPromises();
}; };
it.each`
isProjectVulnerabilityReport | isVisible | shouldDisplay
${false} | ${true} | ${false}
${true} | ${true} | ${true}
${true} | ${false} | ${false}
`(
'displays the group by button correctly ' +
'project: $isProjectVulnerabilityReport, ' +
'isVisible: $isVisible, ' +
'should display: $shouldDisplay',
({ isProjectVulnerabilityReport, isVisible, shouldDisplay }) => {
createWrapper({
isProjectVulnerabilityReport,
isVisible,
});
expect(findGroupByButton().exists()).toBe(shouldDisplay);
},
);
it('displays the group by label', () => { it('displays the group by label', () => {
createWrapperWithFilters(); createWrapperWithFilters();
expect(wrapper.findByText('Group by:').exists()).toBe(true); expect(wrapper.findByText('Group by:').exists()).toBe(true);
......
0% 加载中 .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册