Commit e3b9e8db authored by ap4y's avatar ap4y

Implement creation flow in policy editor

This commit implements policy editor integration with an API endpoint
through the existing vuex action.
parent 4a0c6ab9
......@@ -3,6 +3,8 @@ export const EditorModeYAML = 'yaml';
export const RuleTypeNetwork = 'network';
export const RuleActionTypeAllow = 'allow';
export const RuleDirectionInbound = 'ingress';
export const RuleDirectionOutbound = 'egress';
......
<script>
export default {};
import { GlForm, GlFormSelect, GlSprintf } from '@gitlab/ui';
import { s__ } from '~/locale';
import { RuleActionTypeAllow } from './constants';
export default {
components: {
GlForm,
GlFormSelect,
GlSprintf,
},
data() {
return { actionType: RuleActionTypeAllow };
},
actionTypes: [{ value: RuleActionTypeAllow, text: s__('NetworkPolicies|Allow') }],
};
</script>
<template>
<div class="gl-bg-gray-100 p-2"></div>
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-px-5 gl-pt-5"
>
<gl-form inline>
<gl-sprintf
:message="
s__(
'NetworkPolicies|%{labelStart}Then%{labelEnd} %{action} %{spanStart}the network traffic.%{spanEnd}',
)
"
>
<template #label="{ content }">
<label for="actionType" class="text-uppercase gl-font-lg gl-mr-4 gl-mb-5!">{{
content
}}</label>
</template>
<template #action>
<gl-form-select
id="actionType"
class="gl-mr-4 gl-mb-5!"
:value="actionType"
:options="$options.actionTypes"
/>
</template>
<template #span="{ content }">
<span class="gl-mb-5">{{ content }}</span>
</template>
</gl-sprintf>
</gl-form>
</div>
</template>
<script>
import { mapActions } from 'vuex';
import { mapState, mapActions } from 'vuex';
import {
GlFormGroup,
GlFormSelect,
......@@ -11,6 +11,7 @@ import {
GlAlert,
} from '@gitlab/ui';
import { s__ } from '~/locale';
import { redirectTo } from '~/lib/utils/url_utility';
import EnvironmentPicker from '../environment_picker.vue';
import NetworkPolicyEditor from '../network_policy_editor.vue';
import PolicyRuleBuilder from './policy_rule_builder.vue';
......@@ -43,6 +44,12 @@ export default {
PolicyPreview,
PolicyActionPicker,
},
props: {
threatMonitoringPath: {
type: String,
required: true,
},
},
data() {
return {
editorMode: EditorModeRule,
......@@ -65,6 +72,8 @@ export default {
policyYaml() {
return toYaml(this.policy);
},
...mapState('threatMonitoring', ['currentEnvironmentId']),
...mapState('networkPolicies', ['errorUpdatingPolicy']),
shouldShowRuleEditor() {
return this.editorMode === EditorModeRule;
},
......@@ -80,6 +89,7 @@ export default {
},
methods: {
...mapActions('threatMonitoring', ['fetchEnvironments']),
...mapActions('networkPolicies', ['createPolicy']),
addRule() {
this.policy.rules.push(buildRule(RuleTypeEndpoint));
},
......@@ -110,6 +120,15 @@ export default {
this.editorMode = mode;
},
savePolicy() {
const policy = { manifest: toYaml(this.policy) };
return this.createPolicy({
environmentId: this.currentEnvironmentId,
policy,
}).then(() => {
if (!this.errorUpdatingPolicy) redirectTo(this.threatMonitoringPath);
});
},
},
policyTypes: [{ value: 'networkPolicy', text: s__('NetworkPolicies|Network Policy') }],
editorModes: [
......@@ -197,7 +216,7 @@ export default {
@endpoint-labels-change="updateEndpointLabels"
/>
<div class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100">
<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"
category="primary"
......@@ -209,6 +228,7 @@ export default {
</div>
<h4>{{ s__('NetworkPolicies|Actions') }}</h4>
<p>{{ s__('NetworkPolicies|Traffic that does not match any rule will be blocked.') }}</p>
<policy-action-picker />
</div>
<div class="col-sm-12 col-md-6 col-lg-5 col-xl-4">
......@@ -238,10 +258,17 @@ export default {
<hr />
<div class="row">
<div class="col-md-auto">
<gl-button type="submit" category="primary" variant="success">{{
s__('NetworkPolicies|Create policy')
<gl-button
type="submit"
category="primary"
variant="success"
data-testid="create-policy"
@click="savePolicy"
>{{ s__('NetworkPolicies|Create policy') }}</gl-button
>
<gl-button category="secondary" variant="default" :href="threatMonitoringPath">{{
__('Cancel')
}}</gl-button>
<gl-button category="secondary" variant="default">{{ __('Cancel') }}</gl-button>
</div>
</div>
</section>
......
......@@ -133,7 +133,7 @@ export default {
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base px-3 pt-3"
>
<gl-form inline>
<gl-form inline @submit.prevent>
<gl-sprintf :message="sprintfTemplate">
<template #ifLabel="{ content }">
<label for="ruleType" class="text-uppercase gl-font-lg gl-mr-4 gl-mb-5!">{{
......
......@@ -4,7 +4,7 @@ import createStore from './store';
export default () => {
const el = document.querySelector('#js-policy-builder-app');
const { environmentsEndpoint, networkPoliciesEndpoint } = el.dataset;
const { environmentsEndpoint, networkPoliciesEndpoint, threatMonitoringPath } = el.dataset;
const store = createStore();
store.dispatch('threatMonitoring/setEndpoints', {
......@@ -18,7 +18,9 @@ export default () => {
el,
store,
render(createElement) {
return createElement(PolicyEditorApp, {});
return createElement(PolicyEditorApp, {
props: { threatMonitoringPath },
});
},
});
};
......@@ -12,6 +12,7 @@ export default {
state.environments = payload;
state.isLoadingEnvironments = false;
state.errorLoadingEnvironments = false;
if (payload.length > 0) state.currentEnvironmentId = payload[0].id;
},
[types.RECEIVE_ENVIRONMENTS_ERROR](state) {
state.isLoadingEnvironments = false;
......
......@@ -4,4 +4,5 @@
#js-policy-builder-app{ data: { network_policies_endpoint: project_security_network_policies_path(@project),
environments_endpoint: project_environments_path(@project),
threat_monitoring_path: project_threat_monitoring_path(@project),
} }
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PolicyActionPicker component renders policy action picker 1`] = `
<div
class="gl-bg-gray-10 gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-px-5 gl-pt-5"
>
<gl-form-stub
inline=""
>
<gl-sprintf-stub
message="%{labelStart}Then%{labelEnd} %{action} %{spanStart}the network traffic.%{spanEnd}"
/>
</gl-form-stub>
</div>
`;
......@@ -158,7 +158,7 @@ exports[`PolicyEditorApp component renders the policy editor layout 1`] = `
</h4>
<div
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100"
class="gl-p-3 gl-rounded-base gl-border-1 gl-border-solid gl-border-gray-100 gl-mb-5"
>
<gl-button-stub
category="primary"
......@@ -175,6 +175,10 @@ exports[`PolicyEditorApp component renders the policy editor layout 1`] = `
Actions
</h4>
<p>
Traffic that does not match any rule will be blocked.
</p>
<policy-action-picker-stub />
</div>
......@@ -212,6 +216,7 @@ spec:
>
<gl-button-stub
category="primary"
data-testid="create-policy"
icon=""
size="medium"
type="submit"
......@@ -222,6 +227,7 @@ spec:
<gl-button-stub
category="secondary"
href="/threat-monitoring"
icon=""
size="medium"
variant="default"
......
import { shallowMount } from '@vue/test-utils';
import PolicyActionPicker from 'ee/threat_monitoring/components/policy_editor/policy_action_picker.vue';
describe('PolicyActionPicker component', () => {
let wrapper;
const factory = ({ propsData } = {}) => {
wrapper = shallowMount(PolicyActionPicker, {
propsData: {
...propsData,
},
});
};
beforeEach(() => {
factory();
});
afterEach(() => {
wrapper.destroy();
});
it('renders policy action picker', () => {
expect(wrapper.element).toMatchSnapshot();
});
});
......@@ -12,6 +12,10 @@ import {
EndpointMatchModeLabel,
} from 'ee/threat_monitoring/components/policy_editor/constants';
import fromYaml from 'ee/threat_monitoring/components/policy_editor/lib/from_yaml';
import toYaml from 'ee/threat_monitoring/components/policy_editor/lib/to_yaml';
import { redirectTo } from '~/lib/utils/url_utility';
jest.mock('~/lib/utils/url_utility');
describe('PolicyEditorApp component', () => {
let store;
......@@ -22,9 +26,15 @@ describe('PolicyEditorApp component', () => {
Object.assign(store.state.threatMonitoring, {
...state,
});
Object.assign(store.state.networkPolicies, {
...state,
});
jest.spyOn(store, 'dispatch').mockImplementation(() => Promise.resolve());
wrapper = shallowMount(PolicyEditorApp, {
propsData: {
threatMonitoringPath: '/threat-monitoring',
...propsData,
},
store,
......@@ -45,7 +55,6 @@ describe('PolicyEditorApp component', () => {
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
it('renders the policy editor layout', () => {
......@@ -188,4 +197,32 @@ spec:
expect(findAddRuleButton().props('disabled')).toBe(true);
});
});
it('creates policy and redirects to a threat monitoring path', async () => {
wrapper.find("[data-testid='create-policy']").vm.$emit('click');
await wrapper.vm.$nextTick();
expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/createPolicy', {
environmentId: -1,
policy: { manifest: toYaml(wrapper.vm.policy) },
});
expect(redirectTo).toHaveBeenCalledWith('/threat-monitoring');
});
describe('given there is a createPolicy error', () => {
beforeEach(() => {
factory({
state: {
errorUpdatingPolicy: true,
},
});
});
it('it does not redirect', async () => {
wrapper.find("[data-testid='create-policy']").vm.$emit('click');
await wrapper.vm.$nextTick();
expect(redirectTo).not.toHaveBeenCalledWith('/threat-monitoring');
});
});
});
......@@ -48,6 +48,10 @@ describe('Threat Monitoring mutations', () => {
it('sets errorLoadingEnvironments to false', () => {
expect(state.errorLoadingEnvironments).toBe(false);
});
it('sets currentEnvironmentId to 1', () => {
expect(state.currentEnvironmentId).toEqual(1);
});
});
describe(types.RECEIVE_ENVIRONMENTS_ERROR, () => {
......
......@@ -16167,6 +16167,9 @@ msgstr ""
msgid "NetworkPolicies|%{ifLabelStart}if%{ifLabelEnd} %{ruleType} %{isLabelStart}is%{isLabelEnd} %{ruleDirection} %{ruleSelector} %{directionLabelStart}and is outbound to a%{directionLabelEnd} %{rule} %{portsLabelStart}on%{portsLabelEnd} %{ports}"
msgstr ""
msgid "NetworkPolicies|%{labelStart}Then%{labelEnd} %{action} %{spanStart}the network traffic.%{spanEnd}"
msgstr ""
msgid "NetworkPolicies|%{number} selected"
msgstr ""
......@@ -16188,6 +16191,9 @@ msgstr ""
msgid "NetworkPolicies|All selected"
msgstr ""
msgid "NetworkPolicies|Allow"
msgstr ""
msgid "NetworkPolicies|Allow all inbound traffic to %{selector} from %{ruleSelector} on %{ports}"
msgstr ""
......@@ -16296,6 +16302,9 @@ msgstr ""
msgid "NetworkPolicies|Status"
msgstr ""
msgid "NetworkPolicies|Traffic that does not match any rule will be blocked."
msgstr ""
msgid "NetworkPolicies|YAML editor"
msgstr ""
......
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