Create `Group => Security => Policies` page and create `:group_security_policies` feature flag

Why are we doing this work

  • a user wants to be able to create group-wide policies

Relevant links

group-level policies list design

Non-functional requirements

  • Documentation:
  • Feature flag: :group_security_policies
  • Testing:

Implementation plan

  • backend Create groups/<GROUP_NAME>/-/security/policies path to display the list of the group's policies
    • new controller:ee/app/controllers/groups/security/policies_controller.rb, new route:#index, new view: ee/app/views/groups/security/policies/index.html.haml
  • frontend copy ee/app/assets/javascripts/pages/projects/security/policies/index/index.js to create the ee/app/assets/javascripts/pages/group/security/policies/index/index.js
  • frontend update policy project linking to work for groups by switching from projectPath to namespacePath in the existing code
  • frontend modify scan_execution_policies query to retrieve group-level policies
    • when the group-level scan execution policies query is complete on the backend , enable it on the frontend with the following patch
Patch
diff --git a/ee/app/assets/javascripts/threat_monitoring/graphql/queries/group_scan_execution_policies.query.graphql b/ee/app/assets/javascripts/threat_monitoring/graphql/queries/group_scan_execution_policies.query.graphql
index 9e14b368027..028e824720b 100644
--- a/ee/app/assets/javascripts/threat_monitoring/graphql/queries/group_scan_execution_policies.query.graphql
+++ b/ee/app/assets/javascripts/threat_monitoring/graphql/queries/group_scan_execution_policies.query.graphql
@@ -1,5 +1,5 @@
 query groupScanExecutionPolicies($fullPath: ID!) {
-  group(fullPath: $fullPath) @client {
+  group(fullPath: $fullPath) {
     id
     scanExecutionPolicies {
       nodes {
diff --git a/ee/spec/frontend/threat_monitoring/components/policies/policies_list_spec.js b/ee/spec/frontend/threat_monitoring/components/policies/policies_list_spec.js
index 500e47075cb..285f4dd19c1 100644
--- a/ee/spec/frontend/threat_monitoring/components/policies/policies_list_spec.js
+++ b/ee/spec/frontend/threat_monitoring/components/policies/policies_list_spec.js
@@ -7,6 +7,7 @@ import PoliciesList from 'ee/threat_monitoring/components/policies/policies_list
 import PolicyDrawer from 'ee/threat_monitoring/components/policy_drawer/policy_drawer.vue';
 import { NAMESPACE_TYPES, PREDEFINED_NETWORK_POLICIES } from 'ee/threat_monitoring/constants';
 import networkPoliciesQuery from 'ee/threat_monitoring/graphql/queries/network_policies.query.graphql';
+import groupScanExecutionPoliciesQuery from 'ee/threat_monitoring/graphql/queries/group_scan_execution_policies.query.graphql';
 import projectScanExecutionPoliciesQuery from 'ee/threat_monitoring/graphql/queries/project_scan_execution_policies.query.graphql';
 import scanResultPoliciesQuery from 'ee/threat_monitoring/graphql/queries/scan_result_policies.query.graphql';
 import createStore from 'ee/threat_monitoring/store';
@@ -17,6 +18,7 @@ import waitForPromises from 'helpers/wait_for_promises';
 import {
   networkPolicies,
   projectScanExecutionPolicies,
+  groupScanExecutionPolicies,
   scanResultPolicies,
 } from '../../mocks/mock_apollo';
 import {
@@ -43,10 +45,12 @@ const networkPoliciesSpy = networkPolicies(mockNetworkPoliciesResponse);
 const projectScanExecutionPoliciesSpy = projectScanExecutionPolicies(
   mockScanExecutionPoliciesResponse,
 );
+const groupScanExecutionPoliciesSpy = groupScanExecutionPolicies(mockScanExecutionPoliciesResponse);
 const scanResultPoliciesSpy = scanResultPolicies(mockScanResultPoliciesResponse);
 const defaultRequestHandlers = {
   networkPolicies: networkPoliciesSpy,
   projectScanExecutionPolicies: projectScanExecutionPoliciesSpy,
+  groupScanExecutionPolicies: groupScanExecutionPoliciesSpy,
   scanResultPolicies: scanResultPoliciesSpy,
 };
 const pendingHandler = jest.fn(() => new Promise(() => {}));
@@ -98,6 +102,7 @@ describe('PoliciesList component', () => {
           apolloProvider: createMockApollo([
             [networkPoliciesQuery, requestHandlers.networkPolicies],
             [projectScanExecutionPoliciesQuery, requestHandlers.projectScanExecutionPolicies],
+            [groupScanExecutionPoliciesQuery, requestHandlers.groupScanExecutionPolicies],
             [scanResultPoliciesQuery, requestHandlers.scanResultPolicies],
           ]),
           stubs: {
@@ -275,6 +280,9 @@ describe('PoliciesList component', () => {
     it('fetches policies', () => {
       expect(requestHandlers.networkPolicies).not.toHaveBeenCalled();
       expect(requestHandlers.projectScanExecutionPolicies).not.toHaveBeenCalled();
+      expect(requestHandlers.groupScanExecutionPolicies).toHaveBeenCalledWith({
+        fullPath: groupFullPath,
+      });
       expect(requestHandlers.scanResultPolicies).not.toHaveBeenCalled();
     });
   });
diff --git a/ee/spec/frontend/threat_monitoring/mocks/mock_apollo.js b/ee/spec/frontend/threat_monitoring/mocks/mock_apollo.js
index c66cff449f2..32a4b7bf0c9 100644
--- a/ee/spec/frontend/threat_monitoring/mocks/mock_apollo.js
+++ b/ee/spec/frontend/threat_monitoring/mocks/mock_apollo.js
@@ -59,6 +59,19 @@ export const projectScanExecutionPolicies = (nodes) =>
     },
   });
 
+export const groupScanExecutionPolicies = (nodes) =>
+  jest.fn().mockResolvedValue({
+    data: {
+      group: {
+        id: '3',
+        __typename: 'Group',
+        scanExecutionPolicies: {
+          nodes,
+        },
+      },
+    },
+  });
+
 export const scanResultPolicies = (nodes) =>
   jest.fn().mockResolvedValue({
     data: {
Edited by Alexander Turinske