Commit 59e29b6b authored by Alexander Turinske's avatar Alexander Turinske Committed by Natalia Tepluhina

Fix bug with assignSecurityPolicyProject

parent 1de1210e
...@@ -5,7 +5,7 @@ export const DEFAULT_MR_TITLE = s__('SecurityOrchestration|Update scan execution ...@@ -5,7 +5,7 @@ export const DEFAULT_MR_TITLE = s__('SecurityOrchestration|Update scan execution
export const GRAPHQL_ERROR_MESSAGE = s__( export const GRAPHQL_ERROR_MESSAGE = s__(
'SecurityOrchestration|There was a problem creating the new security policy', 'SecurityOrchestration|There was a problem creating the new security policy',
); );
export const NO_RULE_MESSAGE = s__('SecurityOrhestration|No rules defined - policy will not run.'); export const NO_RULE_MESSAGE = s__('SecurityOrchestration|No rules defined - policy will not run.');
export const SECURITY_POLICY_ACTIONS = { export const SECURITY_POLICY_ACTIONS = {
APPEND: 'APPEND', APPEND: 'APPEND',
......
...@@ -14,26 +14,6 @@ const checkForErrors = ({ errors }) => { ...@@ -14,26 +14,6 @@ const checkForErrors = ({ errors }) => {
} }
}; };
/**
* Creates a new security policy project and assigns it to the current project
* @param {String} projectPath
* @returns {Object} contains the new security policy project and any errors
*/
const assignSecurityPolicyProject = async (projectPath) => {
const {
data: {
securityPolicyProjectCreate: { project, errors },
},
} = await gqClient.mutate({
mutation: createPolicyProject,
variables: {
projectPath,
},
});
return { ...project, branch: project?.branch?.rootRef, errors };
};
/** /**
* Creates a merge request for the changes to the policy file * Creates a merge request for the changes to the policy file
* @param {Object} payload contains the path to the parent project, the branch to merge on the project, and the branch to merge into * @param {Object} payload contains the path to the parent project, the branch to merge on the project, and the branch to merge into
...@@ -102,14 +82,6 @@ export const modifyPolicy = async ({ ...@@ -102,14 +82,6 @@ export const modifyPolicy = async ({
projectPath, projectPath,
yamlEditorValue, yamlEditorValue,
}) => { }) => {
let currentAssignedPolicyProject = assignedPolicyProject;
if (!currentAssignedPolicyProject.fullPath) {
currentAssignedPolicyProject = await assignSecurityPolicyProject(projectPath);
}
checkForErrors(currentAssignedPolicyProject);
const newPolicyCommitBranch = await updatePolicy({ const newPolicyCommitBranch = await updatePolicy({
action, action,
name, name,
...@@ -120,12 +92,34 @@ export const modifyPolicy = async ({ ...@@ -120,12 +92,34 @@ export const modifyPolicy = async ({
checkForErrors(newPolicyCommitBranch); checkForErrors(newPolicyCommitBranch);
const mergeRequest = await createMergeRequest({ const mergeRequest = await createMergeRequest({
projectPath: currentAssignedPolicyProject.fullPath, projectPath: assignedPolicyProject.fullPath,
sourceBranch: newPolicyCommitBranch.branch, sourceBranch: newPolicyCommitBranch.branch,
targetBranch: currentAssignedPolicyProject.branch, targetBranch: assignedPolicyProject.branch,
}); });
checkForErrors(mergeRequest); checkForErrors(mergeRequest);
return { mergeRequest, policyProject: currentAssignedPolicyProject }; return mergeRequest;
};
/**
* Creates a new security policy project and assigns it to the current project
* @param {String} projectPath
* @returns {Object} contains the new security policy project and any errors
*/
export const assignSecurityPolicyProject = async (projectPath) => {
const {
data: {
securityPolicyProjectCreate: { project, errors },
},
} = await gqClient.mutate({
mutation: createPolicyProject,
variables: {
projectPath,
},
});
checkForErrors({ errors });
return { ...project, branch: project?.branch?.rootRef, errors };
}; };
...@@ -5,6 +5,7 @@ import { __, s__ } from '~/locale'; ...@@ -5,6 +5,7 @@ import { __, s__ } from '~/locale';
import { EDITOR_MODES, EDITOR_MODE_YAML } from '../constants'; import { EDITOR_MODES, EDITOR_MODE_YAML } from '../constants';
import PolicyEditorLayout from '../policy_editor_layout.vue'; import PolicyEditorLayout from '../policy_editor_layout.vue';
import { import {
assignSecurityPolicyProject,
DEFAULT_SCAN_EXECUTION_POLICY, DEFAULT_SCAN_EXECUTION_POLICY,
fromYaml, fromYaml,
GRAPHQL_ERROR_MESSAGE, GRAPHQL_ERROR_MESSAGE,
...@@ -60,6 +61,7 @@ export default { ...@@ -60,6 +61,7 @@ export default {
error: '', error: '',
isCreatingMR: false, isCreatingMR: false,
isRemovingPolicy: false, isRemovingPolicy: false,
newlyCreatedPolicyProject: null,
policy: fromYaml(yamlEditorValue), policy: fromYaml(yamlEditorValue),
yamlEditorValue, yamlEditorValue,
}; };
...@@ -77,6 +79,13 @@ export default { ...@@ -77,6 +79,13 @@ export default {
this.$emit('error', error.message); this.$emit('error', error.message);
} }
}, },
async getSecurityPolicyProject() {
if (!this.newlyCreatedPolicyProject && !this.assignedPolicyProject.fullPath) {
this.newlyCreatedPolicyProject = await assignSecurityPolicyProject(this.projectPath);
}
return this.newlyCreatedPolicyProject || this.assignedPolicyProject;
},
async handleModifyPolicy(act) { async handleModifyPolicy(act) {
const action = const action =
act || act ||
...@@ -88,15 +97,17 @@ export default { ...@@ -88,15 +97,17 @@ export default {
this.setLoadingFlag(action, true); this.setLoadingFlag(action, true);
try { try {
const { mergeRequest, policyProject } = await modifyPolicy({ const assignedPolicyProject = await this.getSecurityPolicyProject();
const mergeRequest = await modifyPolicy({
action, action,
assignedPolicyProject: this.assignedPolicyProject, assignedPolicyProject,
name: this.originalName || fromYaml(this.yamlEditorValue)?.name, name: this.originalName || fromYaml(this.yamlEditorValue)?.name,
projectPath: this.projectPath, projectPath: this.projectPath,
yamlEditorValue: this.yamlEditorValue, yamlEditorValue: this.yamlEditorValue,
}); });
this.redirectToMergeRequest({ mergeRequest, policyProject }); this.redirectToMergeRequest({ mergeRequest, assignedPolicyProject });
} catch (e) { } catch (e) {
this.handleError(e); this.handleError(e);
this.setLoadingFlag(action, false); this.setLoadingFlag(action, false);
...@@ -109,11 +120,11 @@ export default { ...@@ -109,11 +120,11 @@ export default {
this.isCreatingMR = val; this.isCreatingMR = val;
} }
}, },
redirectToMergeRequest({ mergeRequest, policyProject }) { redirectToMergeRequest({ mergeRequest, assignedPolicyProject }) {
visitUrl( visitUrl(
joinPaths( joinPaths(
gon.relative_url_root || '/', gon.relative_url_root || '/',
policyProject.fullPath, assignedPolicyProject.fullPath,
'/-/merge_requests', '/-/merge_requests',
mergeRequest.id, mergeRequest.id,
), ),
......
import { modifyPolicy } from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib/utils'; import {
assignSecurityPolicyProject,
modifyPolicy,
} from 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib/utils';
import { DEFAULT_ASSIGNED_POLICY_PROJECT } from 'ee/threat_monitoring/constants'; import { DEFAULT_ASSIGNED_POLICY_PROJECT } from 'ee/threat_monitoring/constants';
import createPolicyProject from 'ee/threat_monitoring/graphql/mutations/create_policy_project.mutation.graphql'; import createPolicyProject from 'ee/threat_monitoring/graphql/mutations/create_policy_project.mutation.graphql';
import createScanExecutionPolicy from 'ee/threat_monitoring/graphql/mutations/create_scan_execution_policy.mutation.graphql'; import createScanExecutionPolicy from 'ee/threat_monitoring/graphql/mutations/create_scan_execution_policy.mutation.graphql';
...@@ -33,7 +36,7 @@ const mockApolloResponses = (shouldReject) => { ...@@ -33,7 +36,7 @@ const mockApolloResponses = (shouldReject) => {
data: { data: {
securityPolicyProjectCreate: { securityPolicyProjectCreate: {
project: newAssignedPolicyProject, project: newAssignedPolicyProject,
errors: [], errors: shouldReject ? [error] : [],
}, },
}, },
}); });
...@@ -55,30 +58,42 @@ const mockApolloResponses = (shouldReject) => { ...@@ -55,30 +58,42 @@ const mockApolloResponses = (shouldReject) => {
}; };
}; };
describe('modifyPolicy', () => { describe('assignSecurityPolicyProject', () => {
it('returns the policy project and merge request on success when a policy project does not exist', async () => { it('returns the newly created policy project', async () => {
gqClient.mutate.mockImplementation(mockApolloResponses()); gqClient.mutate.mockImplementation(mockApolloResponses());
const { mergeRequest, policyProject } = await modifyPolicy( const newlyCreatedPolicyProject = await assignSecurityPolicyProject(projectPath);
createSavePolicyInput(DEFAULT_ASSIGNED_POLICY_PROJECT),
);
expect(policyProject).toStrictEqual({ expect(newlyCreatedPolicyProject).toStrictEqual({
id: newAssignedPolicyProject.id, branch: 'main',
fullPath: newAssignedPolicyProject.fullPath, id: '02',
branch: newAssignedPolicyProject.branch.rootRef,
errors: [], errors: [],
fullPath: 'path/to/new-project',
});
}); });
it('throws when an error is detected', async () => {
gqClient.mutate.mockImplementation(mockApolloResponses(true));
await expect(assignSecurityPolicyProject(projectPath)).rejects.toThrowError(error);
});
});
describe('modifyPolicy', () => {
it('returns the policy project and merge request on success when a policy project does not exist', async () => {
gqClient.mutate.mockImplementation(mockApolloResponses());
const mergeRequest = await modifyPolicy(createSavePolicyInput(DEFAULT_ASSIGNED_POLICY_PROJECT));
expect(mergeRequest).toStrictEqual({ id: '01', errors: [] }); expect(mergeRequest).toStrictEqual({ id: '01', errors: [] });
}); });
it('returns the policy project and merge request on success when a policy project does exist', async () => { it('returns the policy project and merge request on success when a policy project does exist', async () => {
gqClient.mutate.mockImplementation(mockApolloResponses()); gqClient.mutate.mockImplementation(mockApolloResponses());
const { mergeRequest, policyProject } = await modifyPolicy(createSavePolicyInput()); const mergeRequest = await modifyPolicy(createSavePolicyInput());
expect(mergeRequest).toStrictEqual({ id: '01', errors: [] }); expect(mergeRequest).toStrictEqual({ id: '01', errors: [] });
expect(policyProject).toStrictEqual(defaultAssignedPolicyProject);
}); });
it('throws when an error is detected', async () => { it('throws when an error is detected', async () => {
......
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import { GlEmptyState } from '@gitlab/ui'; import { GlEmptyState } from '@gitlab/ui';
import waitForPromises from 'helpers/wait_for_promises';
import PolicyEditorLayout from 'ee/threat_monitoring/components/policy_editor/policy_editor_layout.vue'; import PolicyEditorLayout from 'ee/threat_monitoring/components/policy_editor/policy_editor_layout.vue';
import { import {
DEFAULT_SCAN_EXECUTION_POLICY, DEFAULT_SCAN_EXECUTION_POLICY,
...@@ -20,7 +21,16 @@ jest.mock('~/lib/utils/url_utility', () => ({ ...@@ -20,7 +21,16 @@ jest.mock('~/lib/utils/url_utility', () => ({
visitUrl: jest.fn().mockName('visitUrlMock'), visitUrl: jest.fn().mockName('visitUrlMock'),
})); }));
const newlyCreatedPolicyProject = {
branch: 'main',
fullPath: 'path/to/new-project',
};
jest.mock('ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib', () => ({ jest.mock('ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib', () => ({
assignSecurityPolicyProject: jest.fn().mockResolvedValue({
branch: 'main',
fullPath: 'path/to/new-project',
}),
DEFAULT_SCAN_EXECUTION_POLICY: jest.requireActual( DEFAULT_SCAN_EXECUTION_POLICY: jest.requireActual(
'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib', 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib',
).DEFAULT_SCAN_EXECUTION_POLICY, ).DEFAULT_SCAN_EXECUTION_POLICY,
...@@ -33,10 +43,7 @@ jest.mock('ee/threat_monitoring/components/policy_editor/scan_execution_policy/l ...@@ -33,10 +43,7 @@ jest.mock('ee/threat_monitoring/components/policy_editor/scan_execution_policy/l
SECURITY_POLICY_ACTIONS: jest.requireActual( SECURITY_POLICY_ACTIONS: jest.requireActual(
'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib', 'ee/threat_monitoring/components/policy_editor/scan_execution_policy/lib',
).SECURITY_POLICY_ACTIONS, ).SECURITY_POLICY_ACTIONS,
modifyPolicy: jest.fn().mockResolvedValue({ modifyPolicy: jest.fn().mockResolvedValue({ id: '2' }),
mergeRequest: { id: '2' },
policyProject: { fullPath: 'tests' },
}),
})); }));
describe('ScanExecutionPolicyEditor', () => { describe('ScanExecutionPolicyEditor', () => {
...@@ -44,6 +51,10 @@ describe('ScanExecutionPolicyEditor', () => { ...@@ -44,6 +51,10 @@ describe('ScanExecutionPolicyEditor', () => {
const defaultProjectPath = 'path/to/project'; const defaultProjectPath = 'path/to/project';
const policyEditorEmptyStateSvgPath = 'path/to/svg'; const policyEditorEmptyStateSvgPath = 'path/to/svg';
const scanExecutionDocumentationPath = 'path/to/docs'; const scanExecutionDocumentationPath = 'path/to/docs';
const assignedPolicyProject = {
branch: 'main',
fullPath: 'path/to/existing-project',
};
const factory = ({ propsData = {}, provide = {} } = {}) => { const factory = ({ propsData = {}, provide = {} } = {}) => {
wrapper = shallowMount(ScanExecutionPolicyEditor, { wrapper = shallowMount(ScanExecutionPolicyEditor, {
...@@ -63,7 +74,13 @@ describe('ScanExecutionPolicyEditor', () => { ...@@ -63,7 +74,13 @@ describe('ScanExecutionPolicyEditor', () => {
}; };
const factoryWithExistingPolicy = () => { const factoryWithExistingPolicy = () => {
return factory({ propsData: { existingPolicy: mockDastScanExecutionObject, isEditing: true } }); return factory({
propsData: {
assignedPolicyProject,
existingPolicy: mockDastScanExecutionObject,
isEditing: true,
},
});
}; };
const findEmptyState = () => wrapper.findComponent(GlEmptyState); const findEmptyState = () => wrapper.findComponent(GlEmptyState);
...@@ -86,21 +103,21 @@ describe('ScanExecutionPolicyEditor', () => { ...@@ -86,21 +103,21 @@ describe('ScanExecutionPolicyEditor', () => {
}); });
it.each` it.each`
status | action | event | factoryFn | yamlEditorValue status | action | event | factoryFn | yamlEditorValue | currentlyAssignedPolicyProject
${'to save a new policy'} | ${SECURITY_POLICY_ACTIONS.APPEND} | ${'save-policy'} | ${factory} | ${DEFAULT_SCAN_EXECUTION_POLICY} ${'to save a new policy'} | ${SECURITY_POLICY_ACTIONS.APPEND} | ${'save-policy'} | ${factory} | ${DEFAULT_SCAN_EXECUTION_POLICY} | ${newlyCreatedPolicyProject}
${'to update an existing policy'} | ${SECURITY_POLICY_ACTIONS.REPLACE} | ${'save-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest} ${'to update an existing policy'} | ${SECURITY_POLICY_ACTIONS.REPLACE} | ${'save-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest} | ${assignedPolicyProject}
${'to delete an existing policy'} | ${SECURITY_POLICY_ACTIONS.REMOVE} | ${'remove-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest} ${'to delete an existing policy'} | ${SECURITY_POLICY_ACTIONS.REMOVE} | ${'remove-policy'} | ${factoryWithExistingPolicy} | ${mockDastScanExecutionManifest} | ${assignedPolicyProject}
`( `(
'navigates to the new merge request when "modifyPolicy" is emitted $status', 'navigates to the new merge request when "modifyPolicy" is emitted $status',
async ({ action, event, factoryFn, yamlEditorValue }) => { async ({ action, event, factoryFn, yamlEditorValue, currentlyAssignedPolicyProject }) => {
factoryFn(); factoryFn();
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
findPolicyEditorLayout().vm.$emit(event); findPolicyEditorLayout().vm.$emit(event);
await wrapper.vm.$nextTick(); await waitForPromises();
expect(modifyPolicy).toHaveBeenCalledTimes(1); expect(modifyPolicy).toHaveBeenCalledTimes(1);
expect(modifyPolicy).toHaveBeenCalledWith({ expect(modifyPolicy).toHaveBeenCalledWith({
action, action,
assignedPolicyProject: DEFAULT_ASSIGNED_POLICY_PROJECT, assignedPolicyProject: currentlyAssignedPolicyProject,
name: name:
action === SECURITY_POLICY_ACTIONS.APPEND action === SECURITY_POLICY_ACTIONS.APPEND
? fromYaml(yamlEditorValue).name ? fromYaml(yamlEditorValue).name
...@@ -110,7 +127,9 @@ describe('ScanExecutionPolicyEditor', () => { ...@@ -110,7 +127,9 @@ describe('ScanExecutionPolicyEditor', () => {
}); });
await wrapper.vm.$nextTick(); await wrapper.vm.$nextTick();
expect(visitUrl).toHaveBeenCalled(); expect(visitUrl).toHaveBeenCalled();
expect(visitUrl).toHaveBeenCalledWith('/tests/-/merge_requests/2'); expect(visitUrl).toHaveBeenCalledWith(
`/${currentlyAssignedPolicyProject.fullPath}/-/merge_requests/2`,
);
}, },
); );
}); });
......
...@@ -31312,6 +31312,9 @@ msgstr "" ...@@ -31312,6 +31312,9 @@ msgstr ""
msgid "SecurityOrchestration|New policy" msgid "SecurityOrchestration|New policy"
msgstr "" msgstr ""
msgid "SecurityOrchestration|No rules defined - policy will not run."
msgstr ""
msgid "SecurityOrchestration|Only owners can update Security Policy Project" msgid "SecurityOrchestration|Only owners can update Security Policy Project"
msgstr "" msgstr ""
...@@ -31396,9 +31399,6 @@ msgstr "" ...@@ -31396,9 +31399,6 @@ msgstr ""
msgid "SecurityOrchestration|view results" msgid "SecurityOrchestration|view results"
msgstr "" msgstr ""
msgid "SecurityOrhestration|No rules defined - policy will not run."
msgstr ""
msgid "SecurityPolicies|+%{count} more" msgid "SecurityPolicies|+%{count} more"
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