Commit e6c03ec6 authored by Alexander Turinske's avatar Alexander Turinske

Expand policy editor form actions

- expand it to encompass all of the policy editor page
- update network policy editor to use new layout component
- update scan execution editor to use new layout component
parent 807d8f53
......@@ -165,16 +165,6 @@ const hasUnsupportedAttribute = (manifest) => {
return isUnsupported;
};
/**
* Removes inital line dashes from a policy YAML that is received from the API, which
* is not required for the user.
* @param {String} manifest the policy from the API request
* @returns {String} the policy without the initial dashes or the initial string
*/
export const removeUnnecessaryDashes = (manifest) => {
return manifest.replace('---\n', '');
};
/*
Construct a policy object expected by the policy editor from a yaml manifest.
Expected yaml structure is defined in the official documentation:
......
import { EndpointMatchModeAny } from './constants';
export * from './constants';
// TODO removeUnnecessaryDashes should be generalized
export { default as fromYaml, removeUnnecessaryDashes } from './from_yaml';
export { default as fromYaml } from './from_yaml';
export { default as humanizeNetworkPolicy } from './humanize';
export { buildRule } from './rules';
export { default as toYaml } from './to_yaml';
......
<script>
import {
GlFormGroup,
GlFormInput,
GlFormTextarea,
GlToggle,
GlSegmentedControl,
GlButton,
GlAlert,
} from '@gitlab/ui';
import { GlFormGroup, GlFormInput, GlFormTextarea, GlToggle, GlButton, GlAlert } from '@gitlab/ui';
import { mapState, mapActions } from 'vuex';
import { redirectTo } from '~/lib/utils/url_utility';
import { s__ } from '~/locale';
import { EDITOR_MODES, EditorModeRule, EditorModeYAML, PARSING_ERROR_MESSAGE } from '../constants';
import { EDITOR_MODES, EditorModeYAML, PARSING_ERROR_MESSAGE } from '../constants';
import DimDisableContainer from '../dim_disable_container.vue';
import PolicyActionPicker from '../policy_action_picker.vue';
import PolicyAlertPicker from '../policy_alert_picker.vue';
import PolicyEditorFormActions from '../policy_editor_form_actions.vue';
import PolicyEditorLayout from '../policy_editor_layout.vue';
import PolicyPreview from '../policy_preview.vue';
import { removeUnnecessaryDashes } from '../utils';
import {
DEFAULT_NETWORK_POLICY,
RuleTypeEndpoint,
ProjectIdLabel,
fromYaml,
removeUnnecessaryDashes,
humanizeNetworkPolicy,
buildRule,
toYaml,
......@@ -40,28 +32,16 @@ export default {
GlFormInput,
GlFormTextarea,
GlToggle,
GlSegmentedControl,
GlButton,
GlAlert,
PolicyYamlEditor: () =>
import(/* webpackChunkName: 'policy_yaml_editor' */ '../../policy_yaml_editor.vue'),
PolicyRuleBuilder,
PolicyPreview,
PolicyActionPicker,
PolicyAlertPicker,
PolicyEditorFormActions,
PolicyEditorLayout,
DimDisableContainer,
},
inject: {
threatMonitoringPath: {
type: String,
default: '',
},
projectId: {
type: String,
default: '',
},
},
inject: ['threatMonitoringPath', 'projectId'],
props: {
existingPolicy: {
type: Object,
......@@ -80,7 +60,6 @@ export default {
: '';
return {
editorMode: EditorModeRule,
yamlEditorValue,
yamlEditorError: policy.error ? true : null,
policy,
......@@ -103,12 +82,6 @@ export default {
'errorUpdatingPolicy',
'errorRemovingPolicy',
]),
shouldShowRuleEditor() {
return this.editorMode === EditorModeRule;
},
shouldShowYamlEditor() {
return this.editorMode === EditorModeYAML;
},
hasParsingError() {
return Boolean(this.yamlEditorError);
},
......@@ -140,7 +113,7 @@ export default {
removeRule(ruleIndex) {
this.policy.rules.splice(ruleIndex, 1);
},
loadYaml(manifest) {
updateYaml(manifest) {
this.yamlEditorValue = manifest;
this.yamlEditorError = null;
......@@ -158,13 +131,11 @@ export default {
if (mode === EditorModeYAML && !this.hasParsingError) {
this.yamlEditorValue = toYaml(this.policy);
}
this.editorMode = mode;
},
savePolicy() {
savePolicy(mode) {
const saveFn = this.isEditing ? this.updatePolicy : this.createPolicy;
const policy = {
manifest: this.editorMode === EditorModeYAML ? this.yamlEditorValue : toYaml(this.policy),
manifest: mode === EditorModeYAML ? this.yamlEditorValue : toYaml(this.policy),
};
if (this.isEditing) {
policy.name = this.existingPolicy.name;
......@@ -186,21 +157,17 @@ export default {
</script>
<template>
<section>
<div class="gl-mb-5 gl-border-1 gl-border-solid gl-border-gray-100 gl-rounded-base">
<gl-form-group
class="gl-px-5 gl-py-3 gl-mb-0 gl-bg-gray-10 gl-border-b-solid gl-border-b-gray-100 gl-border-b-1"
<policy-editor-layout
:is-editing="isEditing"
:is-updating-policy="isUpdatingPolicy"
:policy-name="policy.name"
:yaml-editor-value="yamlEditorValue"
@remove-policy="removePolicy"
@save-policy="savePolicy"
@update-editor-mode="changeEditorMode"
@update-yaml="updateYaml"
>
<gl-segmented-control
data-testid="editor-mode"
:options="$options.EDITOR_MODES"
:checked="editorMode"
@input="changeEditorMode"
/>
</gl-form-group>
<div class="gl-display-flex gl-sm-flex-direction-column">
<section class="gl-w-full gl-p-5 gl-flex-fill-4 policy-table-left">
<div v-if="shouldShowRuleEditor" data-testid="rule-editor">
<template #rule-editor>
<gl-alert v-if="hasParsingError" data-testid="parsing-alert" :dismissible="false">
{{ $options.i18n.PARSING_ERROR_MESSAGE }}
</gl-alert>
......@@ -209,10 +176,7 @@ export default {
<gl-form-input id="policyName" v-model="policy.name" :disabled="hasParsingError" />
</gl-form-group>
<gl-form-group
:label="s__('NetworkPolicies|Description')"
label-for="policyDescription"
>
<gl-form-group :label="s__('NetworkPolicies|Description')" label-for="policyDescription">
<gl-form-textarea
id="policyDescription"
v-model="policy.description"
......@@ -249,19 +213,14 @@ export default {
@remove="removeRule(index)"
/>
<div
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-mb-5"
>
<div class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-mb-5">
<gl-button variant="link" data-testid="add-rule" @click="addRule">{{
s__('Network Policy|New rule')
}}</gl-button>
</div>
</dim-disable-container>
<dim-disable-container
data-testid="policy-action-container"
:disabled="hasParsingError"
>
<dim-disable-container data-testid="policy-action-container" :disabled="hasParsingError">
<template #title>
<h4>{{ s__('NetworkPolicies|Actions') }}</h4>
<p>
......@@ -278,20 +237,8 @@ export default {
<policy-action-picker />
<policy-alert-picker :policy-alert="policyAlert" @update-alert="handleAlertUpdate" />
</dim-disable-container>
</div>
<policy-yaml-editor
v-if="shouldShowYamlEditor"
data-testid="policy-yaml-editor"
:value="yamlEditorValue"
:read-only="false"
@input="loadYaml"
/>
</section>
<section
v-if="shouldShowRuleEditor"
class="gl-w-30p gl-p-5 gl-border-l-gray-100 gl-border-l-1 gl-border-l-solid gl-flex-fill-2"
>
</template>
<template #rule-editor-preview>
<dim-disable-container data-testid="policy-preview-container" :disabled="hasParsingError">
<template #title>
<h5>{{ s__('NetworkPolicies|Policy preview') }}</h5>
......@@ -303,9 +250,6 @@ export default {
<policy-preview :policy-yaml="policyYaml" :policy-description="humanizedPolicy" />
</dim-disable-container>
</section>
</div>
</div>
<policy-editor-form-actions />
</section>
</template>
</policy-editor-layout>
</template>
<script>
import { GlButton, GlModal, GlModalDirective } from '@gitlab/ui';
import { s__, __, sprintf } from '~/locale';
import { DELETE_MODAL_CONFIG } from './constants';
import { GlButton, GlFormGroup, GlModal, GlModalDirective, GlSegmentedControl } from '@gitlab/ui';
import { s__, sprintf } from '~/locale';
import { DELETE_MODAL_CONFIG, EDITOR_MODES, EditorModeRule, EditorModeYAML } from './constants';
export default {
i18n: {
createMergeRequest: __('Create merge request'),
DELETE_MODAL_CONFIG,
},
components: {
GlButton,
GlFormGroup,
GlModal,
GlSegmentedControl,
PolicyYamlEditor: () =>
import(/* webpackChunkName: 'policy_yaml_editor' */ '../policy_yaml_editor.vue'),
},
directives: { GlModal: GlModalDirective },
inject: {
threatMonitoringPath: {
inject: ['threatMonitoringPath'],
props: {
customSaveButtonText: {
type: String,
required: false,
default: '',
},
defaultEditorMode: {
type: String,
required: false,
default: EditorModeRule,
},
props: {
isCreatingMergeRequest: {
type: Boolean,
editorModes: {
type: Array,
required: false,
default: false,
default: () => EDITOR_MODES,
},
isEditing: {
type: Boolean,
......@@ -40,50 +48,87 @@ export default {
required: false,
default: '',
},
shouldShowMergeRequestButton: {
type: Boolean,
yamlEditorValue: {
type: String,
required: false,
default: false,
default: '',
},
},
data() {
return {
selectedEditorMode: this.defaultEditorMode,
};
},
computed: {
deleteModalTitle() {
return sprintf(s__('NetworkPolicies|Delete policy: %{policy}'), { policy: this.policyName });
},
saveButtonText() {
// TODO verify
if (this.customSaveButtonText) {
return this.customSaveButtonText;
}
return this.isEditing
? s__('NetworkPolicies|Save changes')
: s__('NetworkPolicies|Create policy');
},
shouldShowRuleEditor() {
return this.selectedEditorMode === EditorModeRule;
},
shouldShowYamlEditor() {
return this.selectedEditorMode === EditorModeYAML;
},
methods: {
createMergeRequest() {
// TODO emit here
},
methods: {
removePolicy() {
// TODO emit here
this.$emit('remove-policy');
},
savePolicy() {
// TODO emit here
this.$emit('save-policy', this.selectedEditorMode);
},
updateEditorMode(mode) {
this.selectedEditorMode = mode;
this.$emit('update-editor-mode', mode);
},
updateYaml() {
this.$emit('load-yaml');
},
},
};
</script>
<template>
<div>
<gl-button
v-if="shouldShowMergeRequestButton"
type="submit"
variant="success"
data-testid="create-merge-request"
:loading="isCreatingMergeRequest"
@click="createMergeRequest"
>{{ $options.i18n.createMergeRequest }}</gl-button
<section>
<div class="gl-mb-5 gl-border-1 gl-border-solid gl-border-gray-100 gl-rounded-base">
<gl-form-group
class="gl-px-5 gl-py-3 gl-mb-0 gl-bg-gray-10 gl-border-b-solid gl-border-b-gray-100 gl-border-b-1"
>
<gl-segmented-control
data-testid="editor-mode"
:options="editorModes"
:checked="selectedEditorMode"
@input="updateEditorMode"
/>
</gl-form-group>
<div class="gl-display-flex gl-sm-flex-direction-column">
<section class="gl-w-full gl-p-5 gl-flex-fill-4 policy-table-left">
<slot v-if="shouldShowRuleEditor" name="rule-editor" data-testid="rule-editor"></slot>
<policy-yaml-editor
v-if="shouldShowYamlEditor"
data-testid="policy-yaml-editor"
:value="yamlEditorValue"
:read-only="false"
@input="updateYaml"
/>
</section>
<section
v-if="shouldShowRuleEditor"
class="gl-w-30p gl-p-5 gl-border-l-gray-100 gl-border-l-1 gl-border-l-solid gl-flex-fill-2"
>
<slot name="rule-editor-preview"></slot>
</section>
</div>
</div>
<gl-button
v-else
type="submit"
variant="success"
data-testid="save-policy"
......@@ -114,5 +159,5 @@ export default {
)
}}
</gl-modal>
</div>
</section>
</template>
import { safeLoad } from 'js-yaml';
/*
Construct a policy object expected by the policy editor from a yaml manifest.
*/
export const fromYaml = (manifest) => {
return safeLoad(manifest, { json: true });
};
export { fromYaml } from './from_yaml';
export const DEFAULT_SCAN_EXECUTION_POLICY = `type: scan_execution_policy
name: ''
description: ''
......
<script>
import { GlFormGroup, GlSegmentedControl } from '@gitlab/ui';
import { __ } from '~/locale';
import { EDITOR_MODES, EditorModeYAML } from '../constants';
import PolicyEditorFormActions from '../policy_editor_form_actions.vue';
import { DEFAULT_SCAN_EXECUTION_POLICY } from './lib';
import PolicyEditorLayout from '../policy_editor_layout.vue';
import { DEFAULT_SCAN_EXECUTION_POLICY, fromYaml } from './lib';
export default {
DEFAULT_EDITOR_MODE: EditorModeYAML,
EDITOR_MODES: [EDITOR_MODES[1]],
components: {
GlFormGroup,
GlSegmentedControl,
PolicyYamlEditor: () =>
import(/* webpackChunkName: 'policy_yaml_editor' */ '../../policy_yaml_editor.vue'),
PolicyEditorFormActions,
},
inject: {
threatMonitoringPath: {
type: String,
default: '',
},
projectId: {
type: String,
default: '',
i18n: {
createMergeRequest: __('Create merge request'),
},
components: {
PolicyEditorLayout,
},
inject: ['threatMonitoringPath', 'projectId'],
props: {
existingPolicy: {
type: Object,
......@@ -32,8 +23,8 @@ export default {
},
data() {
const policy = this.existingPolicy
? this.existingPolicy.manifest
: DEFAULT_SCAN_EXECUTION_POLICY;
? fromYaml(this.existingPolicy.manifest)
: fromYaml(DEFAULT_SCAN_EXECUTION_POLICY);
const yamlEditorValue = this.existingPolicy
? this.existingPolicy.manifest
......@@ -46,32 +37,32 @@ export default {
policy,
};
},
computed: {
isCreatingMergeRequest() {
// TODO track the graphql mutation status after #333163 is closed
return false;
},
isEditing() {
return Boolean(this.existingPolicy);
},
},
methods: {
createMergeRequest() {
// TODO call graphql mutation and redirect to merge request after #333163 is closed
},
},
};
</script>
<template>
<section>
<div class="gl-mb-5 gl-border-1 gl-border-solid gl-border-gray-100 gl-rounded-base">
<gl-form-group
class="gl-px-5 gl-py-3 gl-mb-0 gl-bg-gray-10 gl-border-b-solid gl-border-b-gray-100 gl-border-b-1"
>
<gl-segmented-control
data-testid="editor-mode"
:options="$options.EDITOR_MODES"
:checked="editorMode"
/>
</gl-form-group>
<div class="gl-display-flex gl-sm-flex-direction-column">
<section class="gl-w-full gl-p-5 gl-flex-fill-4 policy-table-left">
<policy-yaml-editor
data-testid="policy-yaml-editor"
:value="yamlEditorValue"
:read-only="false"
@input="loadYaml"
<policy-editor-layout
:custom-save-button-text="$options.i18n.createMergeRequest"
:default-editor-mode="$options.DEFAULT_EDITOR_MODE"
:editor-modes="$options.EDITOR_MODES"
:is-editing="isEditing"
:is-updating-policy="isCreatingMergeRequest"
:policy-name="policy.name"
:yaml-editor-value="yamlEditorValue"
@save-policy="createMergeRequest"
/>
</section>
</div>
</div>
<policy-editor-form-actions :should-show-merge-request-button="true" />
</section>
</template>
/**
* Removes inital line dashes from a policy YAML that is received from the API, which
* is not required for the user.
* @param {String} manifest the policy from the API request
* @returns {String} the policy without the initial dashes or the initial string
*/
export const removeUnnecessaryDashes = (manifest) => {
return manifest.replace('---\n', '');
};
---
name: scan_execution_policy_ui
introduced_by_url:
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/63585
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/273791
milestone: '14.0'
type: development
......
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