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