diff --git a/ee/app/assets/javascripts/security_orchestration/components/loader_with_message.vue b/ee/app/assets/javascripts/security_orchestration/components/loader_with_message.vue new file mode 100644 index 0000000000000000000000000000000000000000..a7423e27120bdd593bc1150d4036ea5dc2ba248e --- /dev/null +++ b/ee/app/assets/javascripts/security_orchestration/components/loader_with_message.vue @@ -0,0 +1,33 @@ +<script> +import { GlLoadingIcon } from '@gitlab/ui'; +import { s__ } from '~/locale'; + +export default { + i18n: { + defaultText: s__('SecurityOrchestration|Fetching the scope information.'), + }, + name: 'LoaderWithMessage', + components: { + GlLoadingIcon, + }, + props: { + message: { + type: String, + required: false, + default: '', + }, + }, + computed: { + loadingMessage() { + return this.message || this.$options.i18n.defaultText; + }, + }, +}; +</script> + +<template> + <div class="gl-display-flex gl-gap-3 gl-align-items-baseline"> + <gl-loading-icon inline /> + <span data-testid="loading-text">{{ loadingMessage }}</span> + </div> +</template> diff --git a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scope/scope_section.vue b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scope/scope_section.vue index 4a52c240cc7b6b2977d739bd17409b3168abea16..f137ee481ffc90ffc9ecf960ade9f2afa794ce14 100644 --- a/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scope/scope_section.vue +++ b/ee/app/assets/javascripts/security_orchestration/components/policy_editor/scope/scope_section.vue @@ -1,6 +1,6 @@ <script> import { isEmpty } from 'lodash'; -import { GlAlert, GlCollapsibleListbox, GlIcon, GlSprintf, GlLoadingIcon } from '@gitlab/ui'; +import { GlAlert, GlCollapsibleListbox, GlIcon, GlSprintf } from '@gitlab/ui'; import { s__, __ } from '~/locale'; import { helpPagePath } from '~/helpers/help_page_helper'; import { convertToGraphQLId, getIdFromGraphQLId } from '~/graphql_shared/utils'; @@ -9,6 +9,7 @@ import glFeatureFlagsMixin from '~/vue_shared/mixins/gl_feature_flags_mixin'; import { NAMESPACE_TYPES } from 'ee/security_orchestration/constants'; import PolicyPopover from 'ee/security_orchestration/components/policy_popover.vue'; import getSppLinkedProjectsNamespaces from 'ee/security_orchestration/graphql/queries/get_spp_linked_projects_namespaces.graphql'; +import LoaderWithMessage from '../../loader_with_message.vue'; import GroupProjectsDropdown from '../../group_projects_dropdown.vue'; import ComplianceFrameworkDropdown from './compliance_framework_dropdown.vue'; import ScopeSectionAlert from './scope_section_alert.vue'; @@ -61,9 +62,9 @@ export default { GlAlert, GlIcon, GlCollapsibleListbox, - GlLoadingIcon, GlSprintf, GroupProjectsDropdown, + LoaderWithMessage, PolicyPopover, ScopeSectionAlert, }, @@ -278,10 +279,7 @@ export default { {{ errorDescription }} </gl-alert> - <div v-if="showLoader" class="gl-display-flex gl-gap-3 gl-align-items-baseline gl-mb-4"> - <gl-loading-icon inline /> - <span data-testid="loading-text">{{ $options.i18n.policyScopeLoadingText }}</span> - </div> + <loader-with-message v-if="showLoader" class="gl-mb-4" /> <div v-else class="gl-display-flex gl-gap-3 gl-align-items-center gl-flex-wrap gl-mt-2 gl-mb-6"> <template v-if="showLinkedSppItemsError"> diff --git a/ee/app/assets/javascripts/security_orchestration/graphql/fragments/policy_scope.fragment.graphql b/ee/app/assets/javascripts/security_orchestration/graphql/fragments/policy_scope.fragment.graphql new file mode 100644 index 0000000000000000000000000000000000000000..043f0ed0e85b9aa2316aa32f2bf04d9c55ec2b2e --- /dev/null +++ b/ee/app/assets/javascripts/security_orchestration/graphql/fragments/policy_scope.fragment.graphql @@ -0,0 +1,33 @@ +#import "~/graphql_shared/fragments/page_info.fragment.graphql" + +fragment PolicyScope on PolicyScope { + complianceFrameworks { + nodes { + id + name + color + description + } + pageInfo { + ...PageInfo + } + } + excludingProjects { + nodes { + id + name + } + pageInfo { + ...PageInfo + } + } + includingProjects { + nodes { + id + name + } + pageInfo { + ...PageInfo + } + } +} diff --git a/ee/app/assets/javascripts/security_orchestration/graphql/queries/group_scan_execution_policies.query.graphql b/ee/app/assets/javascripts/security_orchestration/graphql/queries/group_scan_execution_policies.query.graphql index c322601173fff08b8ee7f747311a74d8c52ebb34..000a9abcfa8d98142dc3cd5c8c969549e04d4b1e 100644 --- a/ee/app/assets/javascripts/security_orchestration/graphql/queries/group_scan_execution_policies.query.graphql +++ b/ee/app/assets/javascripts/security_orchestration/graphql/queries/group_scan_execution_policies.query.graphql @@ -1,4 +1,5 @@ #import "../fragments/scan_policy_source.fragment.graphql" +#import "../fragments/policy_scope.fragment.graphql" query groupScanExecutionPolicies( $fullPath: ID! @@ -12,6 +13,9 @@ query groupScanExecutionPolicies( yaml editPath enabled + policyScope { + ...PolicyScope + } source { ...SecurityPolicySource } diff --git a/ee/app/assets/javascripts/security_orchestration/graphql/queries/group_scan_result_policies.query.graphql b/ee/app/assets/javascripts/security_orchestration/graphql/queries/group_scan_result_policies.query.graphql index 63c95c69ca6063d8f491150cb828aefa728b8cd0..411414280d79e01e10512bd558279330d7851917 100644 --- a/ee/app/assets/javascripts/security_orchestration/graphql/queries/group_scan_result_policies.query.graphql +++ b/ee/app/assets/javascripts/security_orchestration/graphql/queries/group_scan_result_policies.query.graphql @@ -1,4 +1,5 @@ #import "../fragments/scan_policy_source.fragment.graphql" +#import "../fragments/policy_scope.fragment.graphql" query groupScanResultPolicies( $fullPath: ID! @@ -24,6 +25,9 @@ query groupScanResultPolicies( webUrl } roleApprovers + policyScope { + ...PolicyScope + } source { ...SecurityPolicySource } diff --git a/ee/app/assets/javascripts/security_orchestration/graphql/queries/project_scan_execution_policies.query.graphql b/ee/app/assets/javascripts/security_orchestration/graphql/queries/project_scan_execution_policies.query.graphql index 8dc8c363f94d279fcfd4a61177acd5c30d48e1ec..ae4f5ed113b4119478b9e9c3eb43f5a7f821d091 100644 --- a/ee/app/assets/javascripts/security_orchestration/graphql/queries/project_scan_execution_policies.query.graphql +++ b/ee/app/assets/javascripts/security_orchestration/graphql/queries/project_scan_execution_policies.query.graphql @@ -1,4 +1,5 @@ #import "../fragments/scan_policy_source.fragment.graphql" +#import "../fragments/policy_scope.fragment.graphql" query projectScanExecutionPolicies( $fullPath: ID! @@ -12,6 +13,9 @@ query projectScanExecutionPolicies( yaml editPath enabled + policyScope { + ...PolicyScope + } source { ...SecurityPolicySource } diff --git a/ee/app/assets/javascripts/security_orchestration/graphql/queries/project_scan_result_policies.query.graphql b/ee/app/assets/javascripts/security_orchestration/graphql/queries/project_scan_result_policies.query.graphql index 983ff0d88393f624e925922d246529c7298fc213..778940de75a0afd300bb0e1cb521d502b7006b62 100644 --- a/ee/app/assets/javascripts/security_orchestration/graphql/queries/project_scan_result_policies.query.graphql +++ b/ee/app/assets/javascripts/security_orchestration/graphql/queries/project_scan_result_policies.query.graphql @@ -1,4 +1,5 @@ #import "../fragments/scan_policy_source.fragment.graphql" +#import "../fragments/policy_scope.fragment.graphql" query projectScanResultPolicies( $fullPath: ID! @@ -13,6 +14,9 @@ query projectScanResultPolicies( editPath enabled updatedAt + policyScope { + ...PolicyScope + } userApprovers { id name diff --git a/ee/spec/frontend/security_orchestration/components/policy_editor/scope/scope_section_spec.js b/ee/spec/frontend/security_orchestration/components/policy_editor/scope/scope_section_spec.js index da214ffd27e2b1d4120703761bca55978d66dd75..1b65dc6bee35fbb651fc8f8b01486a1e174b4332 100644 --- a/ee/spec/frontend/security_orchestration/components/policy_editor/scope/scope_section_spec.js +++ b/ee/spec/frontend/security_orchestration/components/policy_editor/scope/scope_section_spec.js @@ -1,6 +1,6 @@ import Vue from 'vue'; import VueApollo from 'vue-apollo'; -import { GlAlert, GlSprintf, GlLoadingIcon, GlIcon } from '@gitlab/ui'; +import { GlAlert, GlSprintf, GlIcon } from '@gitlab/ui'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { convertToGraphQLId } from '~/graphql_shared/utils'; import { TYPENAME_PROJECT } from '~/graphql_shared/constants'; @@ -9,6 +9,7 @@ import waitForPromises from 'helpers/wait_for_promises'; import ScopeSection from 'ee/security_orchestration/components/policy_editor/scope/scope_section.vue'; import ComplianceFrameworkDropdown from 'ee/security_orchestration/components/policy_editor/scope/compliance_framework_dropdown.vue'; import GroupProjectsDropdown from 'ee/security_orchestration/components/group_projects_dropdown.vue'; +import LoaderWithMessage from 'ee/security_orchestration/components/loader_with_message.vue'; import ScopeSectionAlert from 'ee/security_orchestration/components/policy_editor/scope/scope_section_alert.vue'; import getSppLinkedProjectsNamespaces from 'ee/security_orchestration/graphql/queries/get_spp_linked_projects_namespaces.graphql'; import createMockApollo from 'helpers/mock_apollo_helper'; @@ -62,6 +63,7 @@ describe('PolicyScope', () => { stubs: { GlSprintf, ScopeSectionAlert, + LoaderWithMessage, }, }); }; @@ -72,7 +74,7 @@ describe('PolicyScope', () => { const findProjectScopeTypeDropdown = () => wrapper.findByTestId('project-scope-type'); const findExceptionTypeDropdown = () => wrapper.findByTestId('exception-type'); const findPolicyScopeProjectText = () => wrapper.findByTestId('policy-scope-project-text'); - const findLoadingIcon = () => wrapper.findComponent(GlLoadingIcon); + const findLoader = () => wrapper.findComponent(LoaderWithMessage); const findScopeSectionAlert = () => wrapper.findComponent(ScopeSectionAlert); const findLoadingText = () => wrapper.findByTestId('loading-text'); const findErrorMessage = () => wrapper.findByTestId('policy-scope-project-error'); @@ -302,7 +304,7 @@ describe('PolicyScope', () => { await waitForPromises(); - expect(findLoadingIcon().exists()).toBe(false); + expect(findLoader().exists()).toBe(false); expect(findProjectScopeTypeDropdown().exists()).toBe(true); expect(requestHandler).toHaveBeenCalledTimes(0); expect(findPolicyScopeProjectText().exists()).toBe(false); @@ -357,7 +359,7 @@ describe('PolicyScope', () => { }, }); - expect(findLoadingIcon().exists()).toBe(true); + expect(findLoader().exists()).toBe(true); expect(findLoadingText().text()).toBe('Fetching the scope information.'); }); @@ -431,7 +433,7 @@ describe('PolicyScope', () => { await waitForPromises(); expect(requestHandler).toHaveBeenCalledTimes(0); - expect(findLoadingIcon().exists()).toBe(false); + expect(findLoader().exists()).toBe(false); }); }); diff --git a/ee/spec/frontend/security_orchestration/mocks/mock_apollo.js b/ee/spec/frontend/security_orchestration/mocks/mock_apollo.js index 6417f29c766ece0c3dcfa43f822550b6306f84b4..da8042c5ada776621eb23d4d0c078f83ce4c9d6e 100644 --- a/ee/spec/frontend/security_orchestration/mocks/mock_apollo.js +++ b/ee/spec/frontend/security_orchestration/mocks/mock_apollo.js @@ -138,3 +138,36 @@ export const complianceFrameworksResponse = [ }, }, ]; + +export const mockLinkedSppItemsResponse = ({ projects = [], namespaces = [] } = {}) => + jest.fn().mockResolvedValue({ + data: { + project: { + id: '1', + securityPolicyProjectLinkedProjects: { + nodes: projects, + }, + securityPolicyProjectLinkedNamespaces: { + nodes: namespaces, + }, + }, + }, + }); + +export const POLICY_SCOPE_MOCK = { + policyScope: { + __typename: 'PolicyScope', + complianceFrameworks: { + nodes: [], + pageInfo: {}, + }, + excludingProjects: { + nodes: [], + pageInfo: {}, + }, + includingProjects: { + nodes: [], + pageInfo: {}, + }, + }, +}; diff --git a/ee/spec/frontend/security_orchestration/mocks/mock_scan_execution_policy_data.js b/ee/spec/frontend/security_orchestration/mocks/mock_scan_execution_policy_data.js index bc3fb13c65a4b35018830bc95ac34373155be664..45649fa95571adefd683049811c32baa44cf61cb 100644 --- a/ee/spec/frontend/security_orchestration/mocks/mock_scan_execution_policy_data.js +++ b/ee/spec/frontend/security_orchestration/mocks/mock_scan_execution_policy_data.js @@ -1,3 +1,4 @@ +import { POLICY_SCOPE_MOCK } from 'ee_jest/security_orchestration/mocks/mock_apollo'; import { actionId, ruleId, unsupportedManifest, unsupportedManifestObject } from './mock_data'; export const customYaml = `variable: true @@ -88,6 +89,7 @@ export const mockProjectScanExecutionPolicy = { yaml: mockDastScanExecutionManifest, editPath: '/policies/policy-name/edit?type="scan_execution_policy"', enabled: true, + ...POLICY_SCOPE_MOCK, source: { __typename: 'ProjectSecurityPolicySource', project: { @@ -103,6 +105,7 @@ export const mockGroupScanExecutionPolicy = { yaml: mockDastScanExecutionManifest, editPath: '/policies/policy-name/edit?type="scan_execution_policy"', enabled: false, + ...POLICY_SCOPE_MOCK, source: { __typename: 'GroupSecurityPolicySource', inherited: true, diff --git a/ee/spec/frontend/security_orchestration/mocks/mock_scan_result_policy_data.js b/ee/spec/frontend/security_orchestration/mocks/mock_scan_result_policy_data.js index a9e7adf15aa77924f52a9a060f66cd2d29282008..6f170dd39ffe8cc9c50a7da72323d18bc52b84c6 100644 --- a/ee/spec/frontend/security_orchestration/mocks/mock_scan_result_policy_data.js +++ b/ee/spec/frontend/security_orchestration/mocks/mock_scan_result_policy_data.js @@ -8,6 +8,7 @@ * match), please name them similarly (e.g. fooBarScanResultManifest and fooBarScanResultObject) * and keep them near each other. */ +import { POLICY_SCOPE_MOCK } from 'ee_jest/security_orchestration/mocks/mock_apollo'; import { actionId, ruleId } from './mock_data'; export const mockForcePushSettingsManifest = `type: approval_policy @@ -141,6 +142,7 @@ export const mockProjectScanResultPolicy = { userApprovers: [], allGroupApprovers: [], roleApprovers: [], + ...POLICY_SCOPE_MOCK, source: { __typename: 'ProjectSecurityPolicySource', project: { @@ -159,6 +161,7 @@ export const mockGroupScanResultPolicy = { userApprovers: [], allGroupApprovers: [], roleApprovers: [], + ...POLICY_SCOPE_MOCK, source: { __typename: 'GroupSecurityPolicySource', inherited: true,