Commit 60a07d1a authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Merge branch '273788-reusable-network-policy-drawer' into 'master'

Move network policy drawer to standalone component

See merge request gitlab-org/gitlab!62772
parents 56455512 cbd75278
<script> <script>
import { GlTable, GlEmptyState, GlDrawer, GlButton, GlAlert, GlSprintf, GlLink } from '@gitlab/ui'; import { GlTable, GlEmptyState, GlButton, GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { mapState, mapActions, mapGetters } from 'vuex'; import { mapState, mapActions, mapGetters } from 'vuex';
import { getTimeago } from '~/lib/utils/datetime_utility'; import { getTimeago } from '~/lib/utils/datetime_utility';
import { setUrlFragment, mergeUrlParams } from '~/lib/utils/url_utility'; import { setUrlFragment, mergeUrlParams } from '~/lib/utils/url_utility';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { getContentWrapperHeight } from '../utils';
import EnvironmentPicker from './environment_picker.vue'; import EnvironmentPicker from './environment_picker.vue';
import { CiliumNetworkPolicyKind } from './policy_editor/constants'; import NetworkPolicyDrawer from './policy_drawer/network_policy_drawer.vue';
import PolicyDrawer from './policy_editor/policy_drawer.vue';
export default { export default {
components: { components: {
GlTable, GlTable,
GlEmptyState, GlEmptyState,
GlDrawer,
GlButton, GlButton,
GlAlert, GlAlert,
GlSprintf, GlSprintf,
GlLink, GlLink,
EnvironmentPicker, EnvironmentPicker,
NetworkPolicyEditor: () => NetworkPolicyDrawer,
import(/* webpackChunkName: 'network_policy_editor' */ './network_policy_editor.vue'),
PolicyDrawer,
}, },
props: { props: {
documentationPath: { documentationPath: {
...@@ -54,11 +49,6 @@ export default { ...@@ -54,11 +49,6 @@ export default {
hasAutoDevopsPolicy() { hasAutoDevopsPolicy() {
return this.policiesWithDefaults.some((policy) => policy.isAutodevops); return this.policiesWithDefaults.some((policy) => policy.isAutodevops);
}, },
hasCiliumSelectedPolicy() {
return this.hasSelectedPolicy
? this.selectedPolicy.manifest.includes(CiliumNetworkPolicyKind)
: false;
},
editPolicyPath() { editPolicyPath() {
return this.hasSelectedPolicy return this.hasSelectedPolicy
? mergeUrlParams( ? mergeUrlParams(
...@@ -106,9 +96,6 @@ export default { ...@@ -106,9 +96,6 @@ export default {
}, },
methods: { methods: {
...mapActions('networkPolicies', ['fetchPolicies', 'createPolicy', 'updatePolicy']), ...mapActions('networkPolicies', ['fetchPolicies', 'createPolicy', 'updatePolicy']),
getDrawerHeaderHeight() {
return getContentWrapperHeight('.js-threat-monitoring-container-wrapper');
},
getTimeAgoString(creationTimestamp) { getTimeAgoString(creationTimestamp) {
if (!creationTimestamp) return ''; if (!creationTimestamp) return '';
return getTimeago().format(creationTimestamp); return getTimeago().format(creationTimestamp);
...@@ -208,44 +195,12 @@ export default { ...@@ -208,44 +195,12 @@ export default {
</template> </template>
</gl-table> </gl-table>
<gl-drawer <network-policy-drawer
ref="editorDrawer"
:z-index="252"
:open="hasSelectedPolicy" :open="hasSelectedPolicy"
:header-height="getDrawerHeaderHeight()" :policy="selectedPolicy"
:edit-policy-path="editPolicyPath"
data-testid="policyDrawer"
@close="deselectPolicy" @close="deselectPolicy"
> />
<template #header>
<div>
<h3 class="gl-mb-5 gl-mt-0">{{ selectedPolicy.name }}</h3>
<div>
<gl-button
data-testid="edit-button"
category="primary"
variant="info"
:href="editPolicyPath"
>{{ s__('NetworkPolicies|Edit policy') }}</gl-button
>
</div>
</div>
</template>
<div v-if="hasSelectedPolicy">
<policy-drawer v-if="hasCiliumSelectedPolicy" v-model="selectedPolicy.manifest" />
<div v-else>
<h5>{{ s__('NetworkPolicies|Policy definition') }}</h5>
<p>
{{ s__("NetworkPolicies|Define this policy's location, conditions and actions.") }}
</p>
<div class="gl-p-3 gl-bg-gray-50">
<network-policy-editor
ref="policyEditor"
v-model="selectedPolicy.manifest"
class="network-policy-editor"
/>
</div>
</div>
</div>
</gl-drawer>
</div> </div>
</template> </template>
<script> <script>
import { __ } from '~/locale'; import { __ } from '~/locale';
import fromYaml, { removeUnnecessaryDashes } from './lib/from_yaml'; import fromYaml, { removeUnnecessaryDashes } from '../policy_editor/lib/from_yaml';
import humanizeNetworkPolicy from './lib/humanize'; import humanizeNetworkPolicy from '../policy_editor/lib/humanize';
import PolicyPreview from './policy_preview.vue'; import PolicyPreview from '../policy_editor/policy_preview.vue';
export default { export default {
components: { components: {
...@@ -41,7 +41,7 @@ export default { ...@@ -41,7 +41,7 @@ export default {
<template> <template>
<div> <div>
<h5 class="gl-mt-3">{{ __('Type') }}</h5> <h5 class="gl-mt-3">{{ __('Type') }}</h5>
<p>{{ s__('NetworkPolicies|Container runtime') }}</p> <p>{{ s__('NetworkPolicies|Network policy') }}</p>
<div v-if="policy"> <div v-if="policy">
<template v-if="policy.description"> <template v-if="policy.description">
......
<script>
import { GlButton, GlDrawer } from '@gitlab/ui';
import { getContentWrapperHeight } from '../../utils';
import { CiliumNetworkPolicyKind } from '../policy_editor/constants';
import CiliumNetworkPolicy from './cilium_network_policy.vue';
export default {
components: {
GlButton,
GlDrawer,
NetworkPolicyEditor: () =>
import(/* webpackChunkName: 'network_policy_editor' */ '../network_policy_editor.vue'),
CiliumNetworkPolicy,
},
props: {
policy: {
type: Object,
required: false,
default: null,
},
editPolicyPath: {
type: String,
required: false,
default: '',
},
},
computed: {
isCiliumNetworkPolicy() {
return this.policy ? this.policy.manifest.includes(CiliumNetworkPolicyKind) : false;
},
},
methods: {
getDrawerHeaderHeight() {
return getContentWrapperHeight('.js-threat-monitoring-container-wrapper');
},
},
// We set the drawer's z-index to 252 to clear flash messages that might be displayed in the page
// and that have a z-index of 251.
DRAWER_Z_INDEX: 252,
};
</script>
<template>
<gl-drawer
:z-index="$options.DRAWER_Z_INDEX"
:header-height="getDrawerHeaderHeight()"
v-bind="$attrs"
v-on="$listeners"
>
<template v-if="policy" #header>
<div>
<h3 class="gl-mb-5 gl-mt-0">{{ policy.name }}</h3>
<div>
<gl-button
data-testid="edit-button"
category="primary"
variant="info"
:href="editPolicyPath"
>{{ s__('NetworkPolicies|Edit policy') }}</gl-button
>
</div>
</div>
</template>
<div v-if="policy">
<cilium-network-policy v-if="isCiliumNetworkPolicy" :value="policy.manifest" />
<div v-else>
<h5>{{ s__('NetworkPolicies|Policy definition') }}</h5>
<p>
{{ s__("NetworkPolicies|Define this policy's location, conditions and actions.") }}
</p>
<div class="gl-p-3 gl-bg-gray-50">
<network-policy-editor
:value="policy.manifest"
data-testid="policyEditor"
class="network-policy-editor"
/>
</div>
</div>
</div>
</gl-drawer>
</template>
...@@ -6,10 +6,10 @@ exports[`NetworkPolicyList component renders policies table 1`] = ` ...@@ -6,10 +6,10 @@ exports[`NetworkPolicyList component renders policies table 1`] = `
<table <table
aria-busy="false" aria-busy="false"
aria-colcount="3" aria-colcount="3"
aria-describedby="__BVID__323__caption_" aria-describedby="__BVID__181__caption_"
aria-multiselectable="false" aria-multiselectable="false"
class="table b-table gl-table table-hover b-table-stacked-md b-table-selectable b-table-select-single" class="table b-table gl-table table-hover b-table-stacked-md b-table-selectable b-table-select-single"
id="__BVID__323" id="__BVID__181"
role="table" role="table"
> >
<!----> <!---->
......
import { GlTable } from '@gitlab/ui'; import { GlTable, GlDrawer } from '@gitlab/ui';
import { mount } from '@vue/test-utils';
import NetworkPolicyList from 'ee/threat_monitoring/components/network_policy_list.vue'; import NetworkPolicyList from 'ee/threat_monitoring/components/network_policy_list.vue';
import PolicyDrawer from 'ee/threat_monitoring/components/policy_editor/policy_drawer.vue';
import createStore from 'ee/threat_monitoring/store'; import createStore from 'ee/threat_monitoring/store';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import { mockPoliciesResponse } from '../mocks/mock_data'; import { mockPoliciesResponse, mockCiliumPolicy } from '../mocks/mock_data';
const mockData = mockPoliciesResponse.map((policy) => convertObjectPropsToCamelCase(policy)); const mockData = mockPoliciesResponse.map((policy) => convertObjectPropsToCamelCase(policy));
...@@ -22,7 +21,7 @@ describe('NetworkPolicyList component', () => { ...@@ -22,7 +21,7 @@ describe('NetworkPolicyList component', () => {
jest.spyOn(store, 'dispatch').mockImplementation(() => Promise.resolve()); jest.spyOn(store, 'dispatch').mockImplementation(() => Promise.resolve());
wrapper = mount(NetworkPolicyList, { wrapper = mountExtended(NetworkPolicyList, {
propsData: { propsData: {
documentationPath: 'documentation_path', documentationPath: 'documentation_path',
newPolicyPath: '/policies/new', newPolicyPath: '/policies/new',
...@@ -31,16 +30,15 @@ describe('NetworkPolicyList component', () => { ...@@ -31,16 +30,15 @@ describe('NetworkPolicyList component', () => {
data, data,
store, store,
provide, provide,
stubs: { NetworkPolicyEditor: true }, stubs: { NetworkPolicyDrawer: GlDrawer },
}); });
}; };
const findEnvironmentsPicker = () => wrapper.find({ ref: 'environmentsPicker' }); const findEnvironmentsPicker = () => wrapper.find({ ref: 'environmentsPicker' });
const findPoliciesTable = () => wrapper.find(GlTable); const findPoliciesTable = () => wrapper.find(GlTable);
const findTableEmptyState = () => wrapper.find({ ref: 'tableEmptyState' }); const findTableEmptyState = () => wrapper.find({ ref: 'tableEmptyState' });
const findEditorDrawer = () => wrapper.find({ ref: 'editorDrawer' }); const findPolicyDrawer = () => wrapper.findByTestId('policyDrawer');
const findPolicyEditor = () => wrapper.find({ ref: 'policyEditor' }); const findAutodevopsAlert = () => wrapper.findByTestId('autodevopsAlert');
const findAutodevopsAlert = () => wrapper.find('[data-testid="autodevopsAlert"]');
beforeEach(() => { beforeEach(() => {
factory({}); factory({});
...@@ -60,10 +58,6 @@ describe('NetworkPolicyList component', () => { ...@@ -60,10 +58,6 @@ describe('NetworkPolicyList component', () => {
expect(button.exists()).toBe(true); expect(button.exists()).toBe(true);
}); });
it('does not render the new policy drawer', () => {
expect(wrapper.find(PolicyDrawer).exists()).toBe(false);
});
it('fetches policies', () => { it('fetches policies', () => {
expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/fetchPolicies', -1); expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/fetchPolicies', -1);
}); });
...@@ -75,41 +69,18 @@ describe('NetworkPolicyList component', () => { ...@@ -75,41 +69,18 @@ describe('NetworkPolicyList component', () => {
expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/fetchPolicies', 2); expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/fetchPolicies', 2);
}); });
it('does not render edit button', () => {
expect(wrapper.find('[data-testid="edit-button"]').exists()).toBe(false);
});
describe('given selected policy is a cilium policy', () => { describe('given selected policy is a cilium policy', () => {
const manifest = `apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: policy
spec:
endpointSelector: {}`;
beforeEach(() => { beforeEach(() => {
factory({ factory({
data: () => ({ selectedPolicyName: 'policy' }), data: () => ({ selectedPolicyName: 'policy' }),
state: { state: {
policies: [ policies: [mockCiliumPolicy],
{
name: 'policy',
creationTimestamp: new Date(),
manifest,
},
],
}, },
}); });
}); });
it('renders the new policy drawer', () => { it('renders the new policy drawer', () => {
expect(wrapper.find(PolicyDrawer).exists()).toBe(true); expect(findPolicyDrawer().exists()).toBe(true);
});
it('renders edit button', () => {
const button = wrapper.find('[data-testid="edit-button"]');
expect(button.exists()).toBe(true);
expect(button.attributes().href).toBe('/policies/policy/edit?environment_id=-1');
}); });
}); });
...@@ -129,7 +100,7 @@ spec: ...@@ -129,7 +100,7 @@ spec:
}); });
it('renders closed editor drawer', () => { it('renders closed editor drawer', () => {
const editorDrawer = findEditorDrawer(); const editorDrawer = findPolicyDrawer();
expect(editorDrawer.exists()).toBe(true); expect(editorDrawer.exists()).toBe(true);
expect(editorDrawer.props('open')).toBe(false); expect(editorDrawer.props('open')).toBe(false);
}); });
...@@ -138,7 +109,7 @@ spec: ...@@ -138,7 +109,7 @@ spec:
findPoliciesTable().find('td').trigger('click'); findPoliciesTable().find('td').trigger('click');
return wrapper.vm.$nextTick().then(() => { return wrapper.vm.$nextTick().then(() => {
const editorDrawer = findEditorDrawer(); const editorDrawer = findPolicyDrawer();
expect(editorDrawer.exists()).toBe(true); expect(editorDrawer.exists()).toBe(true);
expect(editorDrawer.props('open')).toBe(true); expect(editorDrawer.props('open')).toBe(true);
}); });
...@@ -160,16 +131,10 @@ spec: ...@@ -160,16 +131,10 @@ spec:
}); });
it('renders opened editor drawer', () => { it('renders opened editor drawer', () => {
const editorDrawer = findEditorDrawer(); const editorDrawer = findPolicyDrawer();
expect(editorDrawer.exists()).toBe(true); expect(editorDrawer.exists()).toBe(true);
expect(editorDrawer.props('open')).toBe(true); expect(editorDrawer.props('open')).toBe(true);
}); });
it('renders network policy editor with manifest', () => {
const policyEditor = findPolicyEditor();
expect(policyEditor.exists()).toBe(true);
expect(policyEditor.attributes('value')).toBe(mockData[0].manifest);
});
}); });
describe('given there is a default environment with no data to display', () => { describe('given there is a default environment with no data to display', () => {
......
// Jest Snapshot v1, https://goo.gl/fbAQLP // Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`PolicyDrawer component supported YAML renders policy preview tabs 1`] = ` exports[`CiliumNetworkPolicy component supported YAML renders policy preview tabs 1`] = `
<div> <div>
<h5 <h5
class="gl-mt-3" class="gl-mt-3"
...@@ -9,7 +9,7 @@ exports[`PolicyDrawer component supported YAML renders policy preview tabs 1`] = ...@@ -9,7 +9,7 @@ exports[`PolicyDrawer component supported YAML renders policy preview tabs 1`] =
</h5> </h5>
<p> <p>
Container runtime Network policy
</p> </p>
<div> <div>
...@@ -54,7 +54,7 @@ spec: ...@@ -54,7 +54,7 @@ spec:
</div> </div>
`; `;
exports[`PolicyDrawer component unsupported YAML renders policy preview tabs 1`] = ` exports[`CiliumNetworkPolicy component unsupported YAML renders policy preview tabs 1`] = `
<div> <div>
<h5 <h5
class="gl-mt-3" class="gl-mt-3"
...@@ -63,7 +63,7 @@ exports[`PolicyDrawer component unsupported YAML renders policy preview tabs 1`] ...@@ -63,7 +63,7 @@ exports[`PolicyDrawer component unsupported YAML renders policy preview tabs 1`]
</h5> </h5>
<p> <p>
Container runtime Network policy
</p> </p>
<!----> <!---->
......
import CiliumNetworkPolicy from 'ee/threat_monitoring/components/policy_drawer/cilium_network_policy.vue';
import toYaml from 'ee/threat_monitoring/components/policy_editor/lib/to_yaml'; import toYaml from 'ee/threat_monitoring/components/policy_editor/lib/to_yaml';
import PolicyDrawer from 'ee/threat_monitoring/components/policy_editor/policy_drawer.vue';
import PolicyPreview from 'ee/threat_monitoring/components/policy_editor/policy_preview.vue'; import PolicyPreview from 'ee/threat_monitoring/components/policy_editor/policy_preview.vue';
import { shallowMountExtended } from 'helpers/vue_test_utils_helper'; import { shallowMountExtended } from 'helpers/vue_test_utils_helper';
describe('PolicyDrawer component', () => { describe('CiliumNetworkPolicy component', () => {
let wrapper; let wrapper;
const policy = { const policy = {
name: 'test-policy', name: 'test-policy',
...@@ -17,7 +17,7 @@ describe('PolicyDrawer component', () => { ...@@ -17,7 +17,7 @@ describe('PolicyDrawer component', () => {
const findDescription = () => wrapper.findByTestId('description'); const findDescription = () => wrapper.findByTestId('description');
const factory = ({ propsData } = {}) => { const factory = ({ propsData } = {}) => {
wrapper = shallowMountExtended(PolicyDrawer, { wrapper = shallowMountExtended(CiliumNetworkPolicy, {
propsData: { propsData: {
...propsData, ...propsData,
}, },
......
import CiliumNetworkPolicy from 'ee/threat_monitoring/components/policy_drawer/cilium_network_policy.vue';
import NetworkPolicyDrawer from 'ee/threat_monitoring/components/policy_drawer/network_policy_drawer.vue';
import { mountExtended } from 'helpers/vue_test_utils_helper';
import { mockPoliciesResponse, mockCiliumPolicy } from '../../mocks/mock_data';
const [mockGenericPolicy] = mockPoliciesResponse;
describe('NetworkPolicyDrawer component', () => {
let wrapper;
const factory = ({ propsData } = {}) => {
wrapper = mountExtended(NetworkPolicyDrawer, {
propsData: {
editPolicyPath: '/policies/policy/edit?environment_id=-1',
open: true,
...propsData,
},
stubs: { NetworkPolicyEditor: true },
});
};
// Finders
const findEditButton = () => wrapper.findByTestId('edit-button');
const findPolicyEditor = () => wrapper.findByTestId('policyEditor');
const findCiliumNetworkPolicy = () => wrapper.findComponent(CiliumNetworkPolicy);
// Shared assertions
const itRendersEditButton = () => {
it('renders edit button', () => {
const button = findEditButton();
expect(button.exists()).toBe(true);
expect(button.attributes().href).toBe('/policies/policy/edit?environment_id=-1');
});
};
afterEach(() => {
wrapper.destroy();
wrapper = null;
});
describe('by default', () => {
beforeEach(() => {
factory();
});
it('does not render edit button', () => {
expect(findEditButton().exists()).toBe(false);
});
});
describe('given a generic network policy', () => {
beforeEach(() => {
factory({
propsData: {
policy: mockGenericPolicy,
},
});
});
it('renders network policy editor with manifest', () => {
const policyEditor = findPolicyEditor();
expect(policyEditor.exists()).toBe(true);
expect(policyEditor.attributes('value')).toBe(mockGenericPolicy.manifest);
});
itRendersEditButton();
});
describe('given a cilium policy', () => {
beforeEach(() => {
factory({
propsData: {
policy: mockCiliumPolicy,
},
});
});
it('renders the network policy component', () => {
expect(findCiliumNetworkPolicy().exists()).toBe(true);
});
itRendersEditButton();
});
});
...@@ -41,6 +41,17 @@ spec: ...@@ -41,6 +41,17 @@ spec:
}, },
]; ];
export const mockCiliumPolicy = {
name: 'policy',
creationTimestamp: new Date('2021-06-07T00:00:00.000Z'),
manifest: `apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: policy
spec:
endpointSelector: {}`,
};
export const mockNominalHistory = [ export const mockNominalHistory = [
['2019-12-04T00:00:00.000Z', 56], ['2019-12-04T00:00:00.000Z', 56],
['2019-12-05T00:00:00.000Z', 2647], ['2019-12-05T00:00:00.000Z', 2647],
......
...@@ -21815,9 +21815,6 @@ msgstr "" ...@@ -21815,9 +21815,6 @@ msgstr ""
msgid "NetworkPolicies|Are you sure you want to delete this policy? This action cannot be undone." msgid "NetworkPolicies|Are you sure you want to delete this policy? This action cannot be undone."
msgstr "" msgstr ""
msgid "NetworkPolicies|Container runtime"
msgstr ""
msgid "NetworkPolicies|Create policy" msgid "NetworkPolicies|Create policy"
msgstr "" msgstr ""
...@@ -21869,6 +21866,9 @@ msgstr "" ...@@ -21869,6 +21866,9 @@ msgstr ""
msgid "NetworkPolicies|Network Policy" msgid "NetworkPolicies|Network Policy"
msgstr "" msgstr ""
msgid "NetworkPolicies|Network policy"
msgstr ""
msgid "NetworkPolicies|Network traffic" msgid "NetworkPolicies|Network traffic"
msgstr "" 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