Commit cd896116 authored by Enrique Alcántara's avatar Enrique Alcántara

Merge branch '321884-policies-edit-page' into 'master'

Update edit page to account for type parameter

See merge request gitlab-org/gitlab!68416
parents 5fc75197 0ad7ca52
......@@ -48,6 +48,11 @@ export default {
required: false,
default: null,
},
isEditing: {
type: Boolean,
required: false,
default: false,
},
},
data() {
const policy = this.existingPolicy
......@@ -85,9 +90,6 @@ export default {
hasParsingError() {
return Boolean(this.yamlEditorError);
},
isEditing() {
return Boolean(this.existingPolicy);
},
},
methods: {
...mapActions('networkPolicies', ['createPolicy', 'updatePolicy', 'deletePolicy']),
......
......@@ -17,6 +17,7 @@ export default {
ScanExecutionPolicyEditor,
},
mixins: [glFeatureFlagMixin()],
inject: ['policyType'],
props: {
assignedPolicyProject: {
type: Object,
......@@ -31,19 +32,31 @@ export default {
data() {
return {
error: '',
policyType: POLICY_TYPE_COMPONENT_OPTIONS.container.value,
newPolicyType: POLICY_TYPE_COMPONENT_OPTIONS.container.value,
};
},
computed: {
policyComponent() {
return POLICY_TYPE_COMPONENT_OPTIONS[this.policyType].component;
currentPolicyType() {
return this.isEditing ? this.policyType : this.newPolicyType;
},
isEditing() {
return Boolean(this.existingPolicy);
},
policyTypes() {
return Object.values(POLICY_TYPE_COMPONENT_OPTIONS);
},
policyOptions() {
return (
this.policyTypes.find((option) => {
return this.isEditing
? option.urlParameter === this.currentPolicyType
: option.value === this.currentPolicyType;
}) || POLICY_TYPE_COMPONENT_OPTIONS.container
);
},
shouldAllowPolicyTypeSelection() {
return !this.existingPolicy && this.glFeatures.securityOrchestrationPoliciesConfiguration;
},
shouldShowEnvironmentPicker() {
return POLICY_TYPE_COMPONENT_OPTIONS[this.policyType].shouldShowEnvironmentPicker;
},
},
created() {
this.fetchEnvironments();
......@@ -53,11 +66,10 @@ export default {
setError(error) {
this.error = error;
},
updatePolicyType(type) {
this.policyType = type;
handleNewPolicyType(type) {
this.newPolicyType = type;
},
},
policyTypes: Object.values(POLICY_TYPE_COMPONENT_OPTIONS),
};
</script>
......@@ -73,18 +85,19 @@ export default {
<gl-form-group :label="s__('NetworkPolicies|Policy type')" label-for="policyType">
<gl-form-select
id="policyType"
:value="policyType"
:options="$options.policyTypes"
:value="policyOptions.value"
:options="policyTypes"
:disabled="!shouldAllowPolicyTypeSelection"
@change="updatePolicyType"
@change="handleNewPolicyType"
/>
</gl-form-group>
<environment-picker v-if="shouldShowEnvironmentPicker" class="gl-ml-5" />
<environment-picker v-if="policyOptions.shouldShowEnvironmentPicker" class="gl-ml-5" />
</div>
<component
:is="policyComponent"
:is="policyOptions.component"
:existing-policy="existingPolicy"
:assigned-policy-project="assignedPolicyProject"
:is-editing="isEditing"
@error="setError($event)"
/>
</section>
......
export { fromYaml } from './from_yaml';
export { toYaml } from './to_yaml';
export * from './constants';
export * from './utils';
......
import { safeDump } from 'js-yaml';
/*
Return yaml representation of a policy.
*/
export const toYaml = (policy) => {
return safeDump(policy);
};
<script>
import { removeUnnecessaryDashes } from 'ee/threat_monitoring/utils';
import { joinPaths, visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
import { EDITOR_MODES, EDITOR_MODE_YAML } from '../constants';
......@@ -9,8 +8,9 @@ import {
fromYaml,
GRAPHQL_ERROR_MESSAGE,
modifyPolicy,
SECURITY_POLICY_ACTIONS,
toYaml,
} from './lib';
import { SECURITY_POLICY_ACTIONS } from './lib/constants';
export default {
SECURITY_POLICY_ACTIONS,
......@@ -33,10 +33,15 @@ export default {
required: false,
default: null,
},
isEditing: {
type: Boolean,
required: false,
default: false,
},
},
data() {
const yamlEditorValue = this.existingPolicy
? removeUnnecessaryDashes(this.existingPolicy.manifest)
? toYaml(this.existingPolicy)
: DEFAULT_SCAN_EXECUTION_POLICY;
return {
......@@ -47,11 +52,6 @@ export default {
yamlEditorValue,
};
},
computed: {
isEditing() {
return Boolean(this.existingPolicy);
},
},
methods: {
handleError(error) {
if (error.message.toLowerCase().includes('graphql')) {
......
......@@ -23,6 +23,7 @@ export default () => {
networkPoliciesEndpoint,
threatMonitoringPath,
policy,
policyType,
projectPath,
projectId,
environmentId,
......@@ -46,7 +47,7 @@ export default () => {
};
if (policy) {
props.existingPolicy = JSON.parse(policy);
props.existingPolicy = { type: policyType, ...JSON.parse(policy) };
}
return new Vue({
......@@ -56,6 +57,7 @@ export default () => {
configureAgentHelpPath,
createAgentHelpPath,
disableScanExecutionUpdate: parseBoolean(disableScanExecutionUpdate),
policyType,
projectId,
projectPath,
threatMonitoringPath,
......
......@@ -265,7 +265,7 @@ describe('NetworkPolicyEditor component', () => {
});
});
describe('given existingPolicy property was provided', () => {
describe('editing a policy', () => {
const manifest = toYaml({
name: 'policy',
endpointLabels: '',
......@@ -276,6 +276,7 @@ describe('NetworkPolicyEditor component', () => {
factory({
propsData: {
existingPolicy: { name: 'policy', manifest },
isEditing: true,
},
});
});
......
......@@ -4,9 +4,10 @@ import { POLICY_TYPE_COMPONENT_OPTIONS } from 'ee/threat_monitoring/components/c
import EnvironmentPicker from 'ee/threat_monitoring/components/environment_picker.vue';
import NetworkPolicyEditor from 'ee/threat_monitoring/components/policy_editor/network_policy/network_policy_editor.vue';
import PolicyEditor from 'ee/threat_monitoring/components/policy_editor/policy_editor.vue';
import ScanExecutionPolicyEditor from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/scan_execution_policy_editor.vue';
import { DEFAULT_ASSIGNED_POLICY_PROJECT } from 'ee/threat_monitoring/constants';
import createStore from 'ee/threat_monitoring/store';
import { mockL3Manifest } from '../../mocks/mock_data';
import { mockDastScanExecutionObject, mockL3Manifest } from '../../mocks/mock_data';
describe('PolicyEditor component', () => {
let store;
......@@ -16,6 +17,7 @@ describe('PolicyEditor component', () => {
const findEnvironmentPicker = () => wrapper.findComponent(EnvironmentPicker);
const findFormSelect = () => wrapper.findComponent(GlFormSelect);
const findNeworkPolicyEditor = () => wrapper.findComponent(NetworkPolicyEditor);
const findScanExecutionPolicyEditor = () => wrapper.findComponent(ScanExecutionPolicyEditor);
const factory = ({ propsData = {}, provide = {} } = {}) => {
store = createStore();
......@@ -27,7 +29,10 @@ describe('PolicyEditor component', () => {
assignedPolicyProject: DEFAULT_ASSIGNED_POLICY_PROJECT,
...propsData,
},
provide,
provide: {
policyType: undefined,
...provide,
},
store,
stubs: { GlFormSelect },
});
......@@ -64,19 +69,44 @@ describe('PolicyEditor component', () => {
expect(alert.exists()).toBe(true);
expect(alert.text()).toBe(errorMessage);
});
it.each`
policyType | option | findComponent
${'container'} | ${POLICY_TYPE_COMPONENT_OPTIONS.container} | ${findNeworkPolicyEditor}
${'scanExecution'} | ${POLICY_TYPE_COMPONENT_OPTIONS.scanExecution} | ${findScanExecutionPolicyEditor}
`(
'renders the policy editor of type $policyType when selected',
async ({ findComponent, option, policyType }) => {
const formSelect = findFormSelect();
formSelect.vm.$emit('change', policyType);
await wrapper.vm.$nextTick();
const component = findComponent();
expect(formSelect.attributes('value')).toBe(option.value);
expect(component.exists()).toBe(true);
expect(component.props('isEditing')).toBe(false);
},
);
});
describe('when an existing policy is present', () => {
beforeEach(() => {
factory({ propsData: { existingPolicy: { manifest: mockL3Manifest } } });
});
it('renders the disabled form select', () => {
const formSelect = findFormSelect();
expect(formSelect.exists()).toBe(true);
expect(formSelect.attributes('value')).toBe(POLICY_TYPE_COMPONENT_OPTIONS.container.value);
expect(formSelect.attributes('disabled')).toBe('true');
});
it.each`
policyType | option | existingPolicy | findComponent
${'container_policy'} | ${POLICY_TYPE_COMPONENT_OPTIONS.container} | ${{ manifest: mockL3Manifest }} | ${findNeworkPolicyEditor}
${'scan_execution_policy'} | ${POLICY_TYPE_COMPONENT_OPTIONS.scanExecution} | ${mockDastScanExecutionObject} | ${findScanExecutionPolicyEditor}
`(
'renders the disabled form select for existing policy of type $policyType',
async ({ existingPolicy, findComponent, option, policyType }) => {
factory({ propsData: { existingPolicy }, provide: { policyType } });
await wrapper.vm.$nextTick();
const formSelect = findFormSelect();
expect(formSelect.exists()).toBe(true);
expect(formSelect.attributes('value')).toBe(option.value);
expect(formSelect.attributes('disabled')).toBe('true');
const component = findComponent();
expect(component.exists()).toBe(true);
expect(component.props('isEditing')).toBe(true);
},
);
});
describe('with "securityOrchestrationPoliciesConfiguration" feature flag enabled', () => {
......
import { fromYaml } from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib';
import {
DEFAULT_SCAN_EXECUTION_POLICY,
fromYaml,
} from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib';
mockDastScanExecutionManifest,
mockDastScanExecutionObject,
} from 'ee_jest/threat_monitoring/mocks/mock_data';
describe('fromYaml', () => {
it('returns policy object', () => {
expect(fromYaml(DEFAULT_SCAN_EXECUTION_POLICY)).toMatchObject({
name: '',
description: '',
enabled: false,
actions: [{ scan: 'dast', site_profile: '', scanner_profile: '' }],
rules: [{ branches: ['main'], type: 'pipeline' }],
});
expect(fromYaml(mockDastScanExecutionManifest)).toStrictEqual(mockDastScanExecutionObject);
});
});
import { toYaml } from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib';
import {
mockDastScanExecutionManifest,
mockDastScanExecutionObject,
} from 'ee_jest/threat_monitoring/mocks/mock_data';
describe('toYaml', () => {
it('returns policy object as yaml', () => {
expect(toYaml(mockDastScanExecutionObject)).toBe(mockDastScanExecutionManifest);
});
});
......@@ -3,10 +3,14 @@ import PolicyEditorLayout from 'ee/threat_monitoring/components/policy_editor/po
import {
DEFAULT_SCAN_EXECUTION_POLICY,
modifyPolicy,
SECURITY_POLICY_ACTIONS,
} from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib';
import { SECURITY_POLICY_ACTIONS } from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib/constants';
import ScanExecutionPolicyEditor from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/scan_execution_policy_editor.vue';
import { DEFAULT_ASSIGNED_POLICY_PROJECT } from 'ee/threat_monitoring/constants';
import {
mockDastScanExecutionManifest,
mockDastScanExecutionObject,
} from 'ee_jest/threat_monitoring/mocks/mock_data';
import { visitUrl } from '~/lib/utils/url_utility';
jest.mock('~/lib/utils/url_utility', () => ({
......@@ -21,6 +25,12 @@ jest.mock('ee/threat_monitoring/components/policy_editor/scan_execution_policy/l
fromYaml: jest.requireActual(
'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib',
).fromYaml,
toYaml: jest.requireActual(
'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib',
).toYaml,
SECURITY_POLICY_ACTIONS: jest.requireActual(
'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib',
).SECURITY_POLICY_ACTIONS,
modifyPolicy: jest.fn().mockResolvedValue({
mergeRequest: { id: '2' },
policyProject: { fullPath: 'tests' },
......@@ -45,6 +55,10 @@ describe('ScanExecutionPolicyEditor', () => {
});
};
const factoryWithExistingPolicy = () => {
return factory({ propsData: { existingPolicy: mockDastScanExecutionObject, isEditing: true } });
};
const findPolicyEditorLayout = () => wrapper.findComponent(PolicyEditorLayout);
afterEach(() => {
......@@ -63,13 +77,13 @@ describe('ScanExecutionPolicyEditor', () => {
});
it.each`
status | action | event | factoryFn
${'to save a new policy'} | ${SECURITY_POLICY_ACTIONS.APPEND} | ${'save-policy'} | ${factory}
${'to update an existing policy'} | ${SECURITY_POLICY_ACTIONS.REPLACE} | ${'save-policy'} | ${() => factory({ propsData: { existingPolicy: { manifest: DEFAULT_SCAN_EXECUTION_POLICY } } })}
${'to delete an existing policy'} | ${SECURITY_POLICY_ACTIONS.REMOVE} | ${'remove-policy'} | ${() => factory({ propsData: { existingPolicy: { manifest: DEFAULT_SCAN_EXECUTION_POLICY } } })}
status | action | event | factoryFn | yamlEditorValue
${'to save a new policy'} | ${SECURITY_POLICY_ACTIONS.APPEND} | ${'save-policy'} | ${factory} | ${DEFAULT_SCAN_EXECUTION_POLICY}
${'to update an existing policy'} | ${SECURITY_POLICY_ACTIONS.REPLACE} | ${'save-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest}
${'to delete an existing policy'} | ${SECURITY_POLICY_ACTIONS.REMOVE} | ${'remove-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest}
`(
'navigates to the new merge request when "modifyPolicy" is emitted $status',
async ({ action, event, factoryFn }) => {
async ({ action, event, factoryFn, yamlEditorValue }) => {
factoryFn();
await wrapper.vm.$nextTick();
findPolicyEditorLayout().vm.$emit(event);
......@@ -79,7 +93,7 @@ describe('ScanExecutionPolicyEditor', () => {
action,
assignedPolicyProject: DEFAULT_ASSIGNED_POLICY_PROJECT,
projectPath: defaultProjectPath,
yamlEditorValue: DEFAULT_SCAN_EXECUTION_POLICY,
yamlEditorValue,
});
await wrapper.vm.$nextTick();
expect(visitUrl).toHaveBeenCalled();
......
......@@ -221,8 +221,8 @@ export const mockAlertDetails = {
};
export const mockDastScanExecutionManifest = `type: scan_execution_policy
name: 'Test Dast'
description: 'This is a good test'
name: Test Dast
description: This is a good test
enabled: false
rules:
- type: pipeline
......@@ -230,10 +230,25 @@ rules:
- main
actions:
- scan: dast
site_profile: 'required_site_profile'
scanner_profile: 'required_scanner_profile'
site_profile: required_site_profile
scanner_profile: required_scanner_profile
`;
export const mockDastScanExecutionObject = {
type: 'scan_execution_policy',
name: 'Test Dast',
description: 'This is a good test',
enabled: false,
rules: [{ type: 'pipeline', branches: ['main'] }],
actions: [
{
scan: 'dast',
site_profile: 'required_site_profile',
scanner_profile: 'required_scanner_profile',
},
],
};
export const mockL7Manifest = `apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
......
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