Commit 5b3203f7 authored by Ezekiel Kigbo's avatar Ezekiel Kigbo

Merge branch '333163-network-policies-graphql' into 'master'

Migrate network policies fetch to GraphQL

See merge request gitlab-org/gitlab!64546
parents 53bc903b 5d4cb809
<script> <script>
import { GlTable, GlEmptyState, 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, mapGetters } from 'vuex';
import { PREDEFINED_NETWORK_POLICIES } from 'ee/threat_monitoring/constants';
import createFlash from '~/flash';
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 networkPoliciesQuery from '../graphql/queries/network_policies.query.graphql';
import EnvironmentPicker from './environment_picker.vue'; import EnvironmentPicker from './environment_picker.vue';
import PolicyDrawer from './policy_drawer/policy_drawer.vue'; import PolicyDrawer from './policy_drawer/policy_drawer.vue';
...@@ -18,6 +21,7 @@ export default { ...@@ -18,6 +21,7 @@ export default {
EnvironmentPicker, EnvironmentPicker,
PolicyDrawer, PolicyDrawer,
}, },
inject: ['projectPath'],
props: { props: {
documentationPath: { documentationPath: {
type: String, type: String,
...@@ -28,26 +32,61 @@ export default { ...@@ -28,26 +32,61 @@ export default {
required: true, required: true,
}, },
}, },
apollo: {
networkPolicies: {
query: networkPoliciesQuery,
variables() {
return {
fullPath: this.projectPath,
environmentId: this.allEnvironments ? null : this.currentEnvironmentGid,
};
},
update(data) {
const policies = data?.project?.networkPolicies?.nodes ?? [];
const predefined = PREDEFINED_NETWORK_POLICIES.filter(
({ name }) => !policies.some((policy) => name === policy.name),
);
return [...policies, ...predefined];
},
error({ gqlError, networkError }) {
const error =
gqlError?.message ||
networkError?.message ||
s__('NetworkPolicies|Something went wrong, unable to fetch policies');
createFlash({
message: error,
});
},
skip() {
return this.isLoadingEnvironments;
},
},
},
data() { data() {
return { selectedPolicyName: null, initialManifest: null, initialEnforcementStatus: null }; return { selectedPolicyName: null, initialManifest: null, initialEnforcementStatus: null };
}, },
computed: { computed: {
...mapState('networkPolicies', ['policies', 'isLoadingPolicies']), ...mapState('threatMonitoring', [
...mapState('threatMonitoring', ['currentEnvironmentId', 'allEnvironments']), 'currentEnvironmentId',
...mapGetters('networkPolicies', ['policiesWithDefaults']), 'allEnvironments',
'isLoadingEnvironments',
]),
...mapGetters('threatMonitoring', ['currentEnvironmentGid']),
documentationFullPath() { documentationFullPath() {
return setUrlFragment(this.documentationPath, 'container-network-policy'); return setUrlFragment(this.documentationPath, 'container-network-policy');
}, },
isLoadingPolicies() {
return this.isLoadingEnvironments || this.$apollo.queries.networkPolicies.loading;
},
hasSelectedPolicy() { hasSelectedPolicy() {
return Boolean(this.selectedPolicyName); return Boolean(this.selectedPolicyName);
}, },
selectedPolicy() { selectedPolicy() {
if (!this.hasSelectedPolicy) return null; if (!this.hasSelectedPolicy) return null;
return this.networkPolicies.find((policy) => policy.name === this.selectedPolicyName);
return this.policiesWithDefaults.find((policy) => policy.name === this.selectedPolicyName);
}, },
hasAutoDevopsPolicy() { hasAutoDevopsPolicy() {
return this.policiesWithDefaults.some((policy) => policy.isAutodevops); return Boolean(this.networkPolicies?.some((policy) => policy.fromAutoDevops));
}, },
editPolicyPath() { editPolicyPath() {
return this.hasSelectedPolicy return this.hasSelectedPolicy
...@@ -75,7 +114,7 @@ export default { ...@@ -75,7 +114,7 @@ export default {
thClass: 'font-weight-bold', thClass: 'font-weight-bold',
}, },
{ {
key: 'creationTimestamp', key: 'updatedAt',
label: s__('NetworkPolicies|Last modified'), label: s__('NetworkPolicies|Last modified'),
thClass: 'font-weight-bold', thClass: 'font-weight-bold',
}, },
...@@ -86,27 +125,18 @@ export default { ...@@ -86,27 +125,18 @@ export default {
return fields; return fields;
}, },
}, },
watch: {
currentEnvironmentId(envId) {
this.fetchPolicies(envId);
},
},
created() {
this.fetchPolicies(this.currentEnvironmentId);
},
methods: { methods: {
...mapActions('networkPolicies', ['fetchPolicies', 'createPolicy', 'updatePolicy']), getTimeAgoString(updatedAt) {
getTimeAgoString(creationTimestamp) { if (!updatedAt) return '';
if (!creationTimestamp) return ''; return getTimeago().format(updatedAt);
return getTimeago().format(creationTimestamp);
}, },
presentPolicyDrawer(rows) { presentPolicyDrawer(rows) {
if (rows.length === 0) return; if (rows.length === 0) return;
const [selectedPolicy] = rows; const [selectedPolicy] = rows;
this.selectedPolicyName = selectedPolicy?.name; this.selectedPolicyName = selectedPolicy?.name;
this.initialManifest = selectedPolicy?.manifest; this.initialManifest = selectedPolicy?.yaml;
this.initialEnforcementStatus = selectedPolicy?.isEnabled; this.initialEnforcementStatus = selectedPolicy?.enabled;
}, },
deselectPolicy() { deselectPolicy() {
this.selectedPolicyName = null; this.selectedPolicyName = null;
...@@ -161,7 +191,7 @@ export default { ...@@ -161,7 +191,7 @@ export default {
<gl-table <gl-table
ref="policiesTable" ref="policiesTable"
:busy="isLoadingPolicies" :busy="isLoadingPolicies"
:items="policiesWithDefaults" :items="networkPolicies"
:fields="fields" :fields="fields"
head-variant="white" head-variant="white"
stacked="md" stacked="md"
...@@ -175,11 +205,11 @@ export default { ...@@ -175,11 +205,11 @@ export default {
@row-selected="presentPolicyDrawer" @row-selected="presentPolicyDrawer"
> >
<template #cell(status)="value"> <template #cell(status)="value">
{{ value.item.isEnabled ? __('Enabled') : __('Disabled') }} {{ value.item.enabled ? __('Enabled') : __('Disabled') }}
</template> </template>
<template #cell(creationTimestamp)="value"> <template #cell(updatedAt)="value">
{{ getTimeAgoString(value.item.creationTimestamp) }} {{ getTimeAgoString(value.item.updatedAt) }}
</template> </template>
<template #empty> <template #empty>
......
...@@ -33,7 +33,7 @@ export default { ...@@ -33,7 +33,7 @@ export default {
}, },
computed: { computed: {
policyKind() { policyKind() {
return getPolicyKind(this.policy); return getPolicyKind(this.policy?.yaml);
}, },
policyComponent() { policyComponent() {
return policyComponent[this.policyKind] || null; return policyComponent[this.policyKind] || null;
...@@ -72,7 +72,7 @@ export default { ...@@ -72,7 +72,7 @@ export default {
</div> </div>
</template> </template>
<div v-if="policy"> <div v-if="policy">
<component :is="policyComponent" v-if="policyComponent" :value="policy.manifest" /> <component :is="policyComponent" v-if="policyComponent" :value="policy.yaml" />
<div v-else> <div v-else>
<h5>{{ s__('NetworkPolicies|Policy definition') }}</h5> <h5>{{ s__('NetworkPolicies|Policy definition') }}</h5>
<p> <p>
...@@ -80,7 +80,7 @@ export default { ...@@ -80,7 +80,7 @@ export default {
</p> </p>
<div class="gl-p-3 gl-bg-gray-50"> <div class="gl-p-3 gl-bg-gray-50">
<policy-yaml-editor <policy-yaml-editor
:value="policy.manifest" :value="policy.yaml"
data-testid="policy-yaml-editor" data-testid="policy-yaml-editor"
class="network-policy-editor" class="network-policy-editor"
/> />
......
...@@ -5,8 +5,8 @@ export const INVALID_CURRENT_ENVIRONMENT_NAME = '–'; ...@@ -5,8 +5,8 @@ export const INVALID_CURRENT_ENVIRONMENT_NAME = '–';
export const PREDEFINED_NETWORK_POLICIES = [ export const PREDEFINED_NETWORK_POLICIES = [
{ {
name: 'drop-outbound', name: 'drop-outbound',
isEnabled: false, enabled: false,
manifest: `--- yaml: `---
apiVersion: cilium.io/v2 apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy kind: CiliumNetworkPolicy
metadata: metadata:
...@@ -18,8 +18,8 @@ spec: ...@@ -18,8 +18,8 @@ spec:
}, },
{ {
name: 'allow-inbound-http', name: 'allow-inbound-http',
isEnabled: false, enabled: false,
manifest: `--- yaml: `---
apiVersion: cilium.io/v2 apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy kind: CiliumNetworkPolicy
metadata: metadata:
......
query networkPolicies($fullPath: ID!, $environmentId: EnvironmentID) {
project(fullPath: $fullPath) {
networkPolicies(environmentId: $environmentId) {
nodes {
name
yaml
enabled
fromAutoDevops
updatedAt
}
}
}
}
...@@ -11,6 +11,7 @@ const apolloProvider = new VueApollo({ ...@@ -11,6 +11,7 @@ const apolloProvider = new VueApollo({
defaultClient: createDefaultClient( defaultClient: createDefaultClient(
{}, {},
{ {
assumeImmutableResults: true,
cacheConfig: { cacheConfig: {
dataIdFromObject: (object) => { dataIdFromObject: (object) => {
// eslint-disable-next-line no-underscore-dangle // eslint-disable-next-line no-underscore-dangle
......
...@@ -8,31 +8,6 @@ export const setEndpoints = ({ commit }, endpoints) => { ...@@ -8,31 +8,6 @@ export const setEndpoints = ({ commit }, endpoints) => {
commit(types.SET_ENDPOINT, endpoints.networkPoliciesEndpoint); commit(types.SET_ENDPOINT, endpoints.networkPoliciesEndpoint);
}; };
const commitReceivePoliciesError = (commit, data) => {
const error =
data?.error || s__('NetworkPolicies|Something went wrong, unable to fetch policies');
const policies = data?.payload?.length ? data.payload : [];
commit(types.RECEIVE_POLICIES_ERROR, policies);
createFlash({
message: error,
});
};
export const fetchPolicies = ({ state, commit }, environmentId) => {
if (!state.policiesEndpoint) return commitReceivePoliciesError(commit);
commit(types.REQUEST_POLICIES);
const params = environmentId ? { params: { environment_id: environmentId } } : {};
return axios
.get(state.policiesEndpoint, params)
.then(({ data }) => commit(types.RECEIVE_POLICIES_SUCCESS, data))
.catch(({ response }) => {
commitReceivePoliciesError(commit, response?.data);
});
};
const commitPolicyError = (commit, type, payload) => { const commitPolicyError = (commit, type, payload) => {
const error = const error =
payload?.error || s__('NetworkPolicies|Something went wrong, failed to update policy'); payload?.error || s__('NetworkPolicies|Something went wrong, failed to update policy');
......
import { PREDEFINED_NETWORK_POLICIES } from 'ee/threat_monitoring/constants';
export const policiesWithDefaults = ({ policies }) => {
// Predefined policies that were enabled by users will be present in
// the list of policies we received from the backend. We want to
// filter out enabled predefined policies and only append the ones
// that are not present in a cluster.
const predefined = PREDEFINED_NETWORK_POLICIES.filter(
({ name }) => !policies.some((policy) => name === policy.name),
);
return [...policies, ...predefined];
};
import * as actions from './actions'; import * as actions from './actions';
import * as getters from './getters';
import mutations from './mutations'; import mutations from './mutations';
import state from './state'; import state from './state';
export default () => ({ export default () => ({
namespaced: true, namespaced: true,
actions, actions,
getters,
mutations, mutations,
state, state,
}); });
export const SET_ENDPOINT = 'SET_ENDPOINT'; export const SET_ENDPOINT = 'SET_ENDPOINT';
export const REQUEST_POLICIES = 'REQUEST_POLICIES';
export const RECEIVE_POLICIES_SUCCESS = 'RECEIVE_POLICIES_SUCCESS';
export const RECEIVE_POLICIES_ERROR = 'RECEIVE_POLICIES_ERROR';
export const REQUEST_CREATE_POLICY = 'REQUEST_CREATE_POLICY'; export const REQUEST_CREATE_POLICY = 'REQUEST_CREATE_POLICY';
export const RECEIVE_CREATE_POLICY_SUCCESS = 'RECEIVE_CREATE_POLICY_SUCCESS'; export const RECEIVE_CREATE_POLICY_SUCCESS = 'RECEIVE_CREATE_POLICY_SUCCESS';
export const RECEIVE_CREATE_POLICY_ERROR = 'RECEIVE_CREATE_POLICY_ERROR'; export const RECEIVE_CREATE_POLICY_ERROR = 'RECEIVE_CREATE_POLICY_ERROR';
......
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils';
import * as types from './mutation_types'; import * as types from './mutation_types';
const setPolicies = (state, policies) => {
state.policies = policies.map((policy) => convertObjectPropsToCamelCase(policy));
};
export default { export default {
[types.SET_ENDPOINT](state, endpoint) { [types.SET_ENDPOINT](state, endpoint) {
state.policiesEndpoint = endpoint; state.policiesEndpoint = endpoint;
}, },
[types.REQUEST_POLICIES](state) {
state.isLoadingPolicies = true;
state.errorLoadingPolicies = false;
},
[types.RECEIVE_POLICIES_SUCCESS](state, policies) {
setPolicies(state, policies);
state.isLoadingPolicies = false;
state.errorLoadingPolicies = false;
},
[types.RECEIVE_POLICIES_ERROR](state, policies = []) {
setPolicies(state, policies);
state.isLoadingPolicies = false;
state.errorLoadingPolicies = true;
},
[types.REQUEST_CREATE_POLICY](state) { [types.REQUEST_CREATE_POLICY](state) {
state.isUpdatingPolicy = true; state.isUpdatingPolicy = true;
state.errorUpdatingPolicy = false; state.errorUpdatingPolicy = false;
}, },
[types.RECEIVE_CREATE_POLICY_SUCCESS](state, policy) { [types.RECEIVE_CREATE_POLICY_SUCCESS](state) {
const newPolicy = convertObjectPropsToCamelCase(policy);
state.policies = [...state.policies, newPolicy];
state.isUpdatingPolicy = false; state.isUpdatingPolicy = false;
state.errorUpdatingPolicy = false; state.errorUpdatingPolicy = false;
}, },
...@@ -41,9 +20,7 @@ export default { ...@@ -41,9 +20,7 @@ export default {
state.isUpdatingPolicy = true; state.isUpdatingPolicy = true;
state.errorUpdatingPolicy = false; state.errorUpdatingPolicy = false;
}, },
[types.RECEIVE_UPDATE_POLICY_SUCCESS](state, { policy, updatedPolicy }) { [types.RECEIVE_UPDATE_POLICY_SUCCESS](state) {
const newPolicy = convertObjectPropsToCamelCase(updatedPolicy);
state.policies = state.policies.map((pol) => (pol.name === policy.name ? newPolicy : pol));
state.isUpdatingPolicy = false; state.isUpdatingPolicy = false;
state.errorUpdatingPolicy = false; state.errorUpdatingPolicy = false;
}, },
...@@ -55,8 +32,7 @@ export default { ...@@ -55,8 +32,7 @@ export default {
state.isRemovingPolicy = true; state.isRemovingPolicy = true;
state.errorRemovingPolicy = false; state.errorRemovingPolicy = false;
}, },
[types.RECEIVE_DELETE_POLICY_SUCCESS](state, { policy }) { [types.RECEIVE_DELETE_POLICY_SUCCESS](state) {
state.policies = state.policies.filter(({ name }) => name !== policy.name);
state.isRemovingPolicy = false; state.isRemovingPolicy = false;
state.errorRemovingPolicy = false; state.errorRemovingPolicy = false;
}, },
......
export default () => ({ export default () => ({
policiesEndpoint: '', policiesEndpoint: '',
policies: [],
isLoadingPolicies: false,
errorLoadingPolicies: false,
isUpdatingPolicy: false, isUpdatingPolicy: false,
errorUpdatingPolicy: false, errorUpdatingPolicy: false,
isRemovingPolicy: false, isRemovingPolicy: false,
......
...@@ -61,7 +61,6 @@ export const setCurrentTimeWindow = ({ commit }, timeWindow) => { ...@@ -61,7 +61,6 @@ export const setCurrentTimeWindow = ({ commit }, timeWindow) => {
commit(types.SET_CURRENT_TIME_WINDOW, timeWindow.name); commit(types.SET_CURRENT_TIME_WINDOW, timeWindow.name);
}; };
export const setAllEnvironments = ({ commit, dispatch }) => { export const setAllEnvironments = ({ commit }) => {
commit(types.SET_ALL_ENVIRONMENTS); commit(types.SET_ALL_ENVIRONMENTS);
dispatch(`networkPolicies/fetchPolicies`, null, { root: true });
}; };
import { INVALID_CURRENT_ENVIRONMENT_NAME } from '../../../constants'; import { INVALID_CURRENT_ENVIRONMENT_NAME } from '../../../constants';
export const currentEnvironmentName = ({ currentEnvironmentId, environments }) => { export const currentEnvironment = ({ currentEnvironmentId, environments }) =>
const environment = environments.find(({ id }) => id === currentEnvironmentId); environments.find((environment) => environment.id === currentEnvironmentId);
return environment ? environment.name : INVALID_CURRENT_ENVIRONMENT_NAME;
}; export const currentEnvironmentName = (state, getters) =>
getters.currentEnvironment?.name ?? INVALID_CURRENT_ENVIRONMENT_NAME;
export const currentEnvironmentGid = (state, getters) => getters.currentEnvironment?.global_id;
export const canChangeEnvironment = ({ export const canChangeEnvironment = ({
isLoadingEnvironments, isLoadingEnvironments,
......
...@@ -14,14 +14,14 @@ export const getContentWrapperHeight = (contentWrapperClass) => { ...@@ -14,14 +14,14 @@ export const getContentWrapperHeight = (contentWrapperClass) => {
/** /**
* Get a policy's type * Get a policy's type
* @param {Object} policy policy information including a manifest in yaml * @param {String} yaml policy's YAML manifest
* @returns {String|null} policy type if available * @returns {String|null} policy type if available
*/ */
export const getPolicyKind = (policy) => { export const getPolicyKind = (yaml = '') => {
if (policy?.manifest?.includes(POLICY_KINDS.ciliumNetwork)) { if (yaml?.includes(POLICY_KINDS.ciliumNetwork)) {
return POLICY_KINDS.ciliumNetwork; return POLICY_KINDS.ciliumNetwork;
} }
if (policy?.manifest?.includes(POLICY_KINDS.scanExecution)) { if (yaml?.includes(POLICY_KINDS.scanExecution)) {
return POLICY_KINDS.scanExecution; return POLICY_KINDS.scanExecution;
} }
return null; return null;
......
...@@ -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__181__caption_" aria-describedby="__BVID__243__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__181" id="__BVID__243"
role="table" role="table"
> >
<!----> <!---->
......
import { GlTable, GlDrawer } from '@gitlab/ui'; import { GlTable, GlDrawer } from '@gitlab/ui';
import { createLocalVue } from '@vue/test-utils';
import VueApollo from 'vue-apollo';
import NetworkPolicyList from 'ee/threat_monitoring/components/network_policy_list.vue'; import NetworkPolicyList from 'ee/threat_monitoring/components/network_policy_list.vue';
import networkPoliciesQuery from 'ee/threat_monitoring/graphql/queries/network_policies.query.graphql';
import createStore from 'ee/threat_monitoring/store'; import createStore from 'ee/threat_monitoring/store';
import { mountExtended } from 'helpers/vue_test_utils_helper'; import createMockApolloProvider from 'helpers/mock_apollo_helper';
import { convertObjectPropsToCamelCase } from '~/lib/utils/common_utils'; import { mountExtended, shallowMountExtended } from 'helpers/vue_test_utils_helper';
import { networkPolicies } from '../mocks/mock_apollo';
import { mockPoliciesResponse, mockCiliumPolicy } from '../mocks/mock_data'; import { mockPoliciesResponse, mockCiliumPolicy } from '../mocks/mock_data';
const mockData = mockPoliciesResponse.map((policy) => convertObjectPropsToCamelCase(policy)); const localVue = createLocalVue();
localVue.use(VueApollo);
const fullPath = 'project/path';
const environments = [
{
id: 2,
global_id: 'gid://gitlab/Environment/2',
},
];
const defaultRequestHandlers = {
networkPolicies: networkPolicies(mockPoliciesResponse),
};
const pendingHandler = jest.fn(() => new Promise(() => {}));
describe('NetworkPolicyList component', () => { describe('NetworkPolicyList component', () => {
let store; let store;
let wrapper; let wrapper;
let requestHandlers;
const factory = ({ propsData, state, data, provide } = {}) => { const factory = ({ mountFn = mountExtended, propsData, state, data, handlers } = {}) => {
store = createStore(); store = createStore();
Object.assign(store.state.networkPolicies, { Object.assign(store.state.networkPolicies, {
isLoadingPolicies: false,
policies: mockData,
...state, ...state,
}); });
store.state.threatMonitoring.environments = environments;
requestHandlers = {
...defaultRequestHandlers,
...handlers,
};
jest.spyOn(store, 'dispatch').mockImplementation(() => Promise.resolve()); jest.spyOn(store, 'dispatch').mockImplementation(() => Promise.resolve());
wrapper = mountExtended(NetworkPolicyList, { wrapper = mountFn(NetworkPolicyList, {
propsData: { propsData: {
documentationPath: 'documentation_path', documentationPath: 'documentation_path',
newPolicyPath: '/policies/new', newPolicyPath: '/policies/new',
...@@ -29,8 +50,14 @@ describe('NetworkPolicyList component', () => { ...@@ -29,8 +50,14 @@ describe('NetworkPolicyList component', () => {
}, },
data, data,
store, store,
provide, provide: {
projectPath: fullPath,
},
apolloProvider: createMockApolloProvider([
[networkPoliciesQuery, requestHandlers.networkPolicies],
]),
stubs: { PolicyDrawer: GlDrawer }, stubs: { PolicyDrawer: GlDrawer },
localVue,
}); });
}; };
...@@ -58,25 +85,40 @@ describe('NetworkPolicyList component', () => { ...@@ -58,25 +85,40 @@ describe('NetworkPolicyList component', () => {
expect(button.exists()).toBe(true); expect(button.exists()).toBe(true);
}); });
it('fetches policies', () => { describe('initial state', () => {
expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/fetchPolicies', -1); beforeEach(() => {
factory({
mountFn: shallowMountExtended,
handlers: {
networkPolicies: pendingHandler,
},
});
});
it('fetches policies', () => {
expect(requestHandlers.networkPolicies).toHaveBeenCalledWith({
fullPath,
});
});
it("sets table's loading state", () => {
expect(findPoliciesTable().attributes('busy')).toBe('true');
});
}); });
it('fetches policies on environment change', async () => { it('fetches policies on environment change', async () => {
store.dispatch.mockReset(); store.dispatch.mockReset();
await store.commit('threatMonitoring/SET_CURRENT_ENVIRONMENT_ID', 2); await store.commit('threatMonitoring/SET_CURRENT_ENVIRONMENT_ID', 2);
expect(requestHandlers.networkPolicies).toHaveBeenCalledTimes(2);
expect(store.dispatch).toHaveBeenCalledWith('networkPolicies/fetchPolicies', 2); expect(requestHandlers.networkPolicies.mock.calls[1][0]).toEqual({
fullPath: 'project/path',
environmentId: environments[0].global_id,
});
}); });
describe('given selected policy is a cilium policy', () => { describe('given selected policy is a cilium policy', () => {
beforeEach(() => { beforeEach(() => {
factory({ findPoliciesTable().vm.$emit('row-selected', [mockCiliumPolicy]);
data: () => ({ selectedPolicyName: 'policy' }),
state: {
policies: [mockCiliumPolicy],
},
});
}); });
it('renders the new policy drawer', () => { it('renders the new policy drawer', () => {
...@@ -121,13 +163,7 @@ describe('NetworkPolicyList component', () => { ...@@ -121,13 +163,7 @@ describe('NetworkPolicyList component', () => {
describe('given there is a selected policy', () => { describe('given there is a selected policy', () => {
beforeEach(() => { beforeEach(() => {
factory({ findPoliciesTable().vm.$emit('row-selected', [mockPoliciesResponse[0]]);
data: () => ({
selectedPolicyName: 'policy',
initialManifest: mockData[0].manifest,
initialEnforcementStatus: mockData[0].isEnabled,
}),
});
}); });
it('renders opened editor drawer', () => { it('renders opened editor drawer', () => {
...@@ -153,11 +189,15 @@ describe('NetworkPolicyList component', () => { ...@@ -153,11 +189,15 @@ describe('NetworkPolicyList component', () => {
describe('given autodevops selected policy', () => { describe('given autodevops selected policy', () => {
beforeEach(() => { beforeEach(() => {
const policies = mockPoliciesResponse; const autoDevOpsPolicy = {
policies[0].isAutodevops = true; ...mockPoliciesResponse[0],
name: 'auto-devops',
fromAutoDevops: true,
};
factory({ factory({
state: { policies }, handlers: {
data: () => ({ selectedPolicyName: 'policy' }), networkPolicies: networkPolicies([autoDevOpsPolicy]),
},
}); });
}); });
......
...@@ -66,7 +66,7 @@ describe('PolicyDrawer component', () => { ...@@ -66,7 +66,7 @@ describe('PolicyDrawer component', () => {
it('renders network policy editor with manifest', () => { it('renders network policy editor with manifest', () => {
const policyEditor = findPolicyEditor(); const policyEditor = findPolicyEditor();
expect(policyEditor.exists()).toBe(true); expect(policyEditor.exists()).toBe(true);
expect(policyEditor.attributes('value')).toBe(mockGenericPolicy.manifest); expect(policyEditor.attributes('value')).toBe(mockGenericPolicy.yaml);
}); });
itRendersEditButton(); itRendersEditButton();
......
...@@ -23,7 +23,7 @@ describe('ScanExecutionPolicy component', () => { ...@@ -23,7 +23,7 @@ describe('ScanExecutionPolicy component', () => {
describe('supported YAML', () => { describe('supported YAML', () => {
beforeEach(() => { beforeEach(() => {
factory({ propsData: { value: mockScanExecutionPolicy.manifest } }); factory({ propsData: { value: mockScanExecutionPolicy.yaml } });
}); });
it('does render the policy description', () => { it('does render the policy description', () => {
......
...@@ -27,3 +27,14 @@ export const getAlertDetailsQueryErrorMessage = ...@@ -27,3 +27,14 @@ export const getAlertDetailsQueryErrorMessage =
export const erroredGetAlertDetailsQuerySpy = jest.fn().mockResolvedValue({ export const erroredGetAlertDetailsQuerySpy = jest.fn().mockResolvedValue({
errors: [{ message: getAlertDetailsQueryErrorMessage }], errors: [{ message: getAlertDetailsQueryErrorMessage }],
}); });
export const networkPolicies = (nodes) =>
jest.fn().mockResolvedValue({
data: {
project: {
networkPolicies: {
nodes,
},
},
},
});
...@@ -18,8 +18,7 @@ export const mockEnvironmentsResponse = { ...@@ -18,8 +18,7 @@ export const mockEnvironmentsResponse = {
export const mockPoliciesResponse = [ export const mockPoliciesResponse = [
{ {
name: 'policy', name: 'policy',
namespace: 'production', yaml: `---
manifest: `---
apiVersion: networking.k8s.io/v1 apiVersion: networking.k8s.io/v1
kind: NetworkPolicy kind: NetworkPolicy
metadata: metadata:
...@@ -36,15 +35,16 @@ spec: ...@@ -36,15 +35,16 @@ spec:
- namespaceSelector: - namespaceSelector:
matchLabels: matchLabels:
project: myproject`, project: myproject`,
creation_timestamp: '2020-04-14T00:08:30Z', updatedAt: '2020-04-14T00:08:30Z',
is_enabled: true, enabled: true,
fromAutoDevops: false,
}, },
]; ];
export const mockCiliumPolicy = { export const mockCiliumPolicy = {
name: 'policy', name: 'policy',
creationTimestamp: new Date('2021-06-07T00:00:00.000Z'), updatedAt: new Date('2021-06-07T00:00:00.000Z'),
manifest: `apiVersion: cilium.io/v2 yaml: `apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy kind: CiliumNetworkPolicy
metadata: metadata:
name: policy name: policy
...@@ -54,8 +54,8 @@ spec: ...@@ -54,8 +54,8 @@ spec:
export const mockScanExecutionPolicy = { export const mockScanExecutionPolicy = {
name: 'Scheduled DAST scan', name: 'Scheduled DAST scan',
creationTimestamp: new Date('2021-06-07T00:00:00.000Z'), updatedAt: new Date('2021-06-07T00:00:00.000Z'),
manifest: `--- yaml: `---
name: Enforce DAST in every pipeline name: Enforce DAST in every pipeline
description: This policy enforces pipeline configuration to have a job with DAST scan description: This policy enforces pipeline configuration to have a job with DAST scan
enabled: true enabled: true
......
...@@ -9,8 +9,6 @@ import httpStatus from '~/lib/utils/http_status'; ...@@ -9,8 +9,6 @@ import httpStatus from '~/lib/utils/http_status';
import { joinPaths } from '~/lib/utils/url_utility'; import { joinPaths } from '~/lib/utils/url_utility';
import { s__ } from '~/locale'; import { s__ } from '~/locale';
import { mockPoliciesResponse } from '../../../mocks/mock_data';
jest.mock('~/flash'); jest.mock('~/flash');
const networkPoliciesEndpoint = 'networkPoliciesEndpoint'; const networkPoliciesEndpoint = 'networkPoliciesEndpoint';
...@@ -48,118 +46,6 @@ describe('Network Policy actions', () => { ...@@ -48,118 +46,6 @@ describe('Network Policy actions', () => {
)); ));
}); });
describe('fetchPolicies', () => {
describe('on success', () => {
beforeEach(() => {
mock
.onGet(networkPoliciesEndpoint, {
params: { environment_id: environmentId },
})
.replyOnce(httpStatus.OK, mockPoliciesResponse);
});
it('should dispatch the request and success actions', () =>
testAction(
actions.fetchPolicies,
environmentId,
state,
[
{ type: types.REQUEST_POLICIES },
{
type: types.RECEIVE_POLICIES_SUCCESS,
payload: mockPoliciesResponse,
},
],
[],
));
describe('without environment id', () => {
beforeEach(() => {
mock.onGet(networkPoliciesEndpoint, null).replyOnce(httpStatus.OK, mockPoliciesResponse);
});
it('should dispatch the request and success actions', () =>
testAction(
actions.fetchPolicies,
null,
state,
[
{ type: types.REQUEST_POLICIES },
{
type: types.RECEIVE_POLICIES_SUCCESS,
payload: mockPoliciesResponse,
},
],
[],
));
});
});
describe('on error', () => {
describe('without payload', () => {
const error = { error: 'foo' };
beforeEach(() => {
mock.onGet(networkPoliciesEndpoint).replyOnce(500, error);
});
it('should dispatch the request, error actions and updates payload', () =>
testAction(
actions.fetchPolicies,
environmentId,
state,
[{ type: types.REQUEST_POLICIES }, { type: types.RECEIVE_POLICIES_ERROR, payload: [] }],
[],
).then(() => {
expect(createFlash).toHaveBeenCalled();
}));
});
describe('with payload', () => {
const payload = { error: 'foo', payload: [policy] };
beforeEach(() => {
mock.onGet(networkPoliciesEndpoint).replyOnce(500, payload);
});
it('should dispatch the request, error actions and updates payload', () =>
testAction(
actions.fetchPolicies,
environmentId,
state,
[
{ type: types.REQUEST_POLICIES },
{ type: types.RECEIVE_POLICIES_ERROR, payload: [policy] },
],
[],
).then(() => {
expect(createFlash).toHaveBeenCalled();
}));
});
});
describe('with an empty endpoint', () => {
beforeEach(() => {
state.policiesEndpoint = '';
});
it('should dispatch RECEIVE_POLICES_ERROR', () =>
testAction(
actions.fetchPolicies,
environmentId,
state,
[
{
type: types.RECEIVE_POLICIES_ERROR,
payload: [],
},
],
[],
).then(() => {
expect(createFlash).toHaveBeenCalled();
}));
});
});
describe('createPolicy', () => { describe('createPolicy', () => {
const createdPolicy = { name: 'policy', manifest: 'bar', isEnabled: true }; const createdPolicy = { name: 'policy', manifest: 'bar', isEnabled: true };
......
import * as getters from 'ee/threat_monitoring/store/modules/network_policies/getters';
import createState from 'ee/threat_monitoring/store/modules/network_policies/state';
describe('networkPolicies module getters', () => {
let state;
beforeEach(() => {
state = createState();
});
describe('policiesWithDefaults', () => {
describe('without policies in the state', () => {
it('returns predefined policies', () => {
expect(getters.policiesWithDefaults(state).map(({ name }) => name)).toEqual([
'drop-outbound',
'allow-inbound-http',
]);
});
});
describe('with policies in the state', () => {
beforeEach(() => {
state.policies = [{ name: 'user-policy' }];
});
it('returns user owned and predefined policies', () => {
expect(getters.policiesWithDefaults(state).map(({ name }) => name)).toEqual([
'user-policy',
'drop-outbound',
'allow-inbound-http',
]);
});
describe('with deployed predefined policy', () => {
beforeEach(() => {
state.policies = [{ name: 'user-policy' }, { name: 'drop-outbound' }];
});
it('returns user policies and a single predefined policy', () => {
expect(getters.policiesWithDefaults(state).map(({ name }) => name)).toEqual([
'user-policy',
'drop-outbound',
'allow-inbound-http',
]);
});
});
});
});
});
...@@ -15,46 +15,6 @@ describe('Network Policies mutations', () => { ...@@ -15,46 +15,6 @@ describe('Network Policies mutations', () => {
}); });
}); });
describe(types.REQUEST_POLICIES, () => {
beforeEach(() => {
mutations[types.REQUEST_POLICIES](state);
});
it('sets isLoadingPolicies to true and sets errorLoadingPolicies to false', () => {
expect(state.isLoadingPolicies).toBe(true);
expect(state.errorLoadingPolicies).toBe(false);
});
});
describe(types.RECEIVE_POLICIES_SUCCESS, () => {
let policies;
beforeEach(() => {
policies = [{ id: 1, name: 'production' }];
mutations[types.RECEIVE_POLICIES_SUCCESS](state, policies);
});
it('sets policies to the payload', () => {
expect(state.policies).toEqual(expect.objectContaining(policies));
});
it('sets isLoadingPolicies to false and sets errorLoadingPolicies to false', () => {
expect(state.isLoadingPolicies).toBe(false);
expect(state.errorLoadingPolicies).toBe(false);
});
});
describe(types.RECEIVE_POLICIES_ERROR, () => {
beforeEach(() => {
mutations[types.RECEIVE_POLICIES_ERROR](state);
});
it('sets isLoadingPolicies to false and sets errorLoadingPolicies to true', () => {
expect(state.isLoadingPolicies).toBe(false);
expect(state.errorLoadingPolicies).toBe(true);
});
});
describe(types.REQUEST_CREATE_POLICY, () => { describe(types.REQUEST_CREATE_POLICY, () => {
beforeEach(() => { beforeEach(() => {
mutations[types.REQUEST_CREATE_POLICY](state); mutations[types.REQUEST_CREATE_POLICY](state);
...@@ -67,14 +27,8 @@ describe('Network Policies mutations', () => { ...@@ -67,14 +27,8 @@ describe('Network Policies mutations', () => {
}); });
describe(types.RECEIVE_CREATE_POLICY_SUCCESS, () => { describe(types.RECEIVE_CREATE_POLICY_SUCCESS, () => {
const policy = { id: 1, name: 'production', manifest: 'foo' };
beforeEach(() => { beforeEach(() => {
mutations[types.RECEIVE_CREATE_POLICY_SUCCESS](state, policy); mutations[types.RECEIVE_CREATE_POLICY_SUCCESS](state);
});
it('adds a policy to the store', () => {
expect(state.policies).toEqual(expect.objectContaining([policy]));
}); });
it('sets isUpdatingPolicy to false and sets errorUpdatingPolicy to false', () => { it('sets isUpdatingPolicy to false and sets errorUpdatingPolicy to false', () => {
...@@ -106,20 +60,8 @@ describe('Network Policies mutations', () => { ...@@ -106,20 +60,8 @@ describe('Network Policies mutations', () => {
}); });
describe(types.RECEIVE_UPDATE_POLICY_SUCCESS, () => { describe(types.RECEIVE_UPDATE_POLICY_SUCCESS, () => {
const policy = { id: 1, name: 'production', manifest: 'foo' };
const updatedPolicy = { id: 1, name: 'production', manifest: 'bar' };
beforeEach(() => { beforeEach(() => {
state.policies.push(policy); mutations[types.RECEIVE_UPDATE_POLICY_SUCCESS](state);
mutations[types.RECEIVE_UPDATE_POLICY_SUCCESS](state, {
policy,
updatedPolicy,
});
});
it('replaces policies with the updatedPolicy', () => {
expect(state.policies).not.toEqual(expect.objectContaining(policy));
expect(state.policies).toEqual(expect.objectContaining([updatedPolicy]));
}); });
it('sets isUpdatingPolicy to false and sets errorUpdatingPolicy to false', () => { it('sets isUpdatingPolicy to false and sets errorUpdatingPolicy to false', () => {
......
...@@ -221,12 +221,6 @@ describe('Threat Monitoring actions', () => { ...@@ -221,12 +221,6 @@ describe('Threat Monitoring actions', () => {
describe('setAllEnvironments', () => { describe('setAllEnvironments', () => {
it('commits the SET_ALL_ENVIRONMENTS mutation and dispatches Network Policy fetch action', () => it('commits the SET_ALL_ENVIRONMENTS mutation and dispatches Network Policy fetch action', () =>
testAction( testAction(actions.setAllEnvironments, null, state, [{ type: types.SET_ALL_ENVIRONMENTS }]));
actions.setAllEnvironments,
null,
state,
[{ type: types.SET_ALL_ENVIRONMENTS }],
[{ type: 'networkPolicies/fetchPolicies', payload: null }],
));
}); });
}); });
...@@ -9,20 +9,53 @@ describe('threatMonitoring module getters', () => { ...@@ -9,20 +9,53 @@ describe('threatMonitoring module getters', () => {
state = createState(); state = createState();
}); });
describe('currentEnvironmentName', () => { describe('currentEnvironment', () => {
describe.each` beforeEach(() => {
context | currentEnvironmentId | environments | expectedName state.environments = [{ id: 1 }, { id: 2 }];
${'no environments'} | ${1} | ${[]} | ${INVALID_CURRENT_ENVIRONMENT_NAME} });
${'a non-existent environment id'} | ${2} | ${[{ id: 1 }]} | ${INVALID_CURRENT_ENVIRONMENT_NAME}
${'an existing environment id'} | ${3} | ${[{ id: 3, name: 'foo' }]} | ${'foo'} it('returns the current environment given a valid ID', () => {
`('given $context', ({ currentEnvironmentId, environments, expectedName }) => { state.currentEnvironmentId = 2;
beforeEach(() => {
state.currentEnvironmentId = currentEnvironmentId; expect(getters.currentEnvironment(state)).toEqual({ id: 2 });
state.environments = environments; });
});
it('returns undefined if the environment can not be found', () => {
state.currentEnvironmentId = 3;
expect(getters.currentEnvironment(state)).toBe(undefined);
});
});
describe.each`
context | currentEnvironmentId | environments | expectedName | expectedGid
${'no environments'} | ${1} | ${[]} | ${INVALID_CURRENT_ENVIRONMENT_NAME} | ${undefined}
${'a non-existent environment id'} | ${2} | ${[{ id: 1 }]} | ${INVALID_CURRENT_ENVIRONMENT_NAME} | ${undefined}
${'an existing environment id'} | ${3} | ${[{ id: 3, name: 'foo', global_id: 'gid://gitlab/Environment/3' }]} | ${'foo'} | ${'gid://gitlab/Environment/3'}
`('given $context', ({ currentEnvironmentId, environments, expectedName, expectedGid }) => {
let mockedGetters;
beforeEach(() => {
state.currentEnvironmentId = currentEnvironmentId;
state.environments = environments;
mockedGetters = {
currentEnvironment: {
id: currentEnvironmentId,
global_id: expectedGid,
name: expectedName,
},
};
});
describe('currentEnvironmentName', () => {
it('returns the expected name', () => { it('returns the expected name', () => {
expect(getters.currentEnvironmentName(state)).toBe(expectedName); expect(getters.currentEnvironmentName(state, mockedGetters)).toBe(expectedName);
});
});
describe('currentEnvironmentGid', () => {
it('returns the expected global ID', () => {
expect(getters.currentEnvironmentGid(state, mockedGetters)).toBe(expectedGid);
}); });
}); });
}); });
......
...@@ -32,12 +32,11 @@ describe('Threat Monitoring Utils', () => { ...@@ -32,12 +32,11 @@ describe('Threat Monitoring Utils', () => {
describe('getPolicyKind', () => { describe('getPolicyKind', () => {
it.each` it.each`
input | output input | output
${{}} | ${null} ${''} | ${null}
${{ manifest: '' }} | ${null} ${'ciliumNetworkPolicy'} | ${null}
${{ manifest: 'ciliumNetworkPolicy' }} | ${null} ${mockL3Manifest} | ${POLICY_KINDS.ciliumNetwork}
${{ manifest: mockL3Manifest }} | ${POLICY_KINDS.ciliumNetwork} ${mockDastScanExecutionManifest} | ${POLICY_KINDS.scanExecution}
${{ manifest: mockDastScanExecutionManifest }} | ${POLICY_KINDS.scanExecution}
`('returns $output when used on $input', ({ input, output }) => { `('returns $output when used on $input', ({ input, output }) => {
expect(getPolicyKind(input)).toBe(output); expect(getPolicyKind(input)).toBe(output);
}); });
......
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