Commit 788beacf authored by Mayra Cabrera's avatar Mayra Cabrera

Merge branch...

Merge branch '284376-update-frontend-to-use-new-call-to-build-an-issue-from-a-vulnerability' into 'master'

Use issue creation for new vulnerability issues

See merge request gitlab-org/gitlab!48926
parents 2b1fcb44 f5f3ca18
......@@ -47,9 +47,9 @@ and allows you to comment on a change.
You can create an issue for a vulnerability by selecting the **Create issue** button.
This creates a [confidential issue](../../project/issues/confidential_issues.md) in the
project the vulnerability came from and pre-populates it with useful information from
the vulnerability report. After the issue is created, GitLab redirects you to the
This allows the user to create a [confidential issue](../../project/issues/confidential_issues.md)
in the project the vulnerability came from. Fields are pre-populated with pertinent information
from the vulnerability report. After the issue is created, GitLab redirects you to the
issue page so you can edit, assign, or comment on the issue.
## Link issues to the vulnerability
......
<script>
import axios from 'axios';
import { GlButton, GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import { GlButton } from '@gitlab/ui';
import RelatedIssuesStore from '~/related_issues/stores/related_issues_store';
import RelatedIssuesBlock from '~/related_issues/components/related_issues_block.vue';
import { issuableTypesMap, PathIdSeparator } from '~/related_issues/constants';
......@@ -15,9 +15,6 @@ export default {
components: {
RelatedIssuesBlock,
GlButton,
GlAlert,
GlSprintf,
GlLink,
},
props: {
endpoint: {
......@@ -60,7 +57,7 @@ export default {
return Boolean(this.state.relatedIssues.find(i => i.lockIssueRemoval));
},
canCreateIssue() {
return !this.isIssueAlreadyCreated && !this.isFetching && Boolean(this.createIssueUrl);
return !this.isIssueAlreadyCreated && !this.isFetching && Boolean(this.newIssueUrl);
},
},
inject: {
......@@ -70,7 +67,7 @@ export default {
projectFingerprint: {
default: '',
},
createIssueUrl: {
newIssueUrl: {
default: '',
},
reportType: {
......@@ -89,17 +86,7 @@ export default {
methods: {
createIssue() {
this.isProcessingAction = true;
this.errorCreatingIssue = false;
return axios
.post(this.createIssueUrl)
.then(({ data: { web_url } }) => {
redirectTo(web_url);
})
.catch(() => {
this.isProcessingAction = false;
this.errorCreatingIssue = true;
});
redirectTo(this.newIssueUrl, { params: { vulnerability_id: this.vulnerabilityId } });
},
toggleFormVisibility() {
this.isFormVisible = !this.isFormVisible;
......@@ -218,28 +205,6 @@ export default {
<template>
<div>
<gl-alert
v-if="errorCreatingIssue"
variant="danger"
class="gl-mt-5"
@dismiss="errorCreatingIssue = false"
>
<p class="gl-font-weight-bold gl-mb-2">{{ $options.i18n.createIssueErrorTitle }}</p>
<p class="gl-mb-0">
<gl-sprintf :message="$options.i18n.createIssueErrorBody">
<template #tracking="{ content }">
<gl-link class="gl-display-inline-block" :href="issueTrackingHelpPath" target="_blank">
{{ content }}
</gl-link>
</template>
<template #permissions="{ content }">
<gl-link class="gl-display-inline-block" :href="permissionsHelpPath" target="_blank">
{{ content }}
</gl-link>
</template>
</gl-sprintf>
</p>
</gl-alert>
<related-issues-block
:help-path="helpPath"
:is-fetching="isFetching"
......
......@@ -15,7 +15,7 @@ export default el => {
provide: {
reportType: vulnerability.reportType,
createIssueUrl: vulnerability.createIssueUrl,
newIssueUrl: vulnerability.newIssueUrl,
projectFingerprint: vulnerability.projectFingerprint,
vulnerabilityId: vulnerability.id,
issueTrackingHelpPath: vulnerability.issueTrackingHelpPath,
......
......@@ -10,7 +10,7 @@ module VulnerabilitiesHelper
result = {
timestamp: Time.now.to_i,
create_issue_url: create_issue_url_for(vulnerability),
new_issue_url: new_issue_url_for(vulnerability),
create_jira_issue_url: create_jira_issue_url_for(vulnerability),
related_jira_issues_path: project_integrations_jira_issues_path(vulnerability.project, vulnerability_ids: [vulnerability.id]),
has_mr: !!vulnerability.finding.merge_request_feedback.try(:merge_request_iid),
......@@ -27,10 +27,10 @@ module VulnerabilitiesHelper
result.merge(vulnerability_data(vulnerability), vulnerability_finding_data(vulnerability))
end
def create_issue_url_for(vulnerability)
def new_issue_url_for(vulnerability)
return unless vulnerability.project.issues_enabled?
create_issue_project_security_vulnerability_path(vulnerability.project, vulnerability)
new_project_issue_path(vulnerability.project, { vulnerability_id: vulnerability.id })
end
def create_jira_issue_url_for(vulnerability)
......
---
title: Creating an issue from a vulnerability takes user to the new issue page
merge_request: 48926
author:
type: changed
......@@ -30,7 +30,7 @@ describe('Vulnerability Header', () => {
reportType: 'sast',
state: 'detected',
createMrUrl: '/create_mr_url',
createIssueUrl: '/create_issue_url',
newIssueUrl: '/new_issue_url',
projectFingerprint: 'abc123',
pipeline: {
id: 2,
......
import { GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter';
import RelatedIssues from 'ee/vulnerabilities/components/related_issues.vue';
import waitForPromises from 'helpers/wait_for_promises';
import { deprecatedCreateFlash as createFlash } from '~/flash';
import axios from '~/lib/utils/axios_utils';
import httpStatusCodes from '~/lib/utils/http_status';
......@@ -25,7 +23,7 @@ describe('Vulnerability related issues component', () => {
};
const vulnerabilityId = 5131;
const createIssueUrl = '/create/issue';
const newIssueUrl = '/new/issue';
const projectFingerprint = 'project-fingerprint';
const issueTrackingHelpPath = '/help/issue/tracking';
const permissionsHelpPath = '/help/permissions';
......@@ -40,7 +38,7 @@ describe('Vulnerability related issues component', () => {
provide: {
vulnerabilityId,
projectFingerprint,
createIssueUrl,
newIssueUrl,
reportType,
issueTrackingHelpPath,
permissionsHelpPath,
......@@ -59,7 +57,6 @@ describe('Vulnerability related issues component', () => {
const blockProp = prop => relatedIssuesBlock().props(prop);
const blockEmit = (eventName, data) => relatedIssuesBlock().vm.$emit(eventName, data);
const findCreateIssueButton = () => wrapper.find({ ref: 'createIssue' });
const findAlert = () => wrapper.find(GlAlert);
afterEach(() => {
wrapper.destroy();
......@@ -283,14 +280,10 @@ describe('Vulnerability related issues component', () => {
});
describe('when linked issue is not yet created', () => {
const failCreateIssueAction = async () => {
mockAxios.onPost(createIssueUrl).reply(500);
expect(findAlert().exists()).toBe(false);
findCreateIssueButton().vm.$emit('click');
await waitForPromises();
};
let redirectToSpy;
beforeEach(async () => {
redirectToSpy = jest.spyOn(urlUtility, 'redirectTo').mockImplementation(() => {});
mockAxios.onGet(propsData.endpoint).replyOnce(httpStatusCodes.OK, [issue1, issue2]);
createWrapper({ stubs: { RelatedIssuesBlock } });
await axios.waitForAll();
......@@ -300,34 +293,11 @@ describe('Vulnerability related issues component', () => {
expect(findCreateIssueButton().exists()).toBe(true);
});
it('calls create issue endpoint on click and redirects to new issue', async () => {
const issueUrl = `/group/project/-/security/vulnerabilities/${vulnerabilityId}/create_issue`;
const spy = jest.spyOn(urlUtility, 'redirectTo');
mockAxios.onPost(propsData.createIssueUrl).reply(200, {
web_url: issueUrl,
});
it('calls new issue endpoint on click', () => {
findCreateIssueButton().vm.$emit('click');
await waitForPromises();
const [postRequest] = mockAxios.history.post;
expect(mockAxios.history.post).toHaveLength(1);
expect(postRequest.url).toBe(createIssueUrl);
expect(spy).toHaveBeenCalledWith(issueUrl);
});
it('shows an error message when issue creation fails', async () => {
await failCreateIssueAction();
expect(mockAxios.history.post).toHaveLength(1);
expect(findAlert().exists()).toBe(true);
});
it('dismisses the error message', async () => {
await failCreateIssueAction();
findAlert().vm.$emit('dismiss');
await wrapper.vm.$nextTick();
expect(findAlert().exists()).toBe(false);
expect(redirectToSpy).toHaveBeenCalledWith(newIssueUrl, {
params: { vulnerability_id: vulnerabilityId },
});
});
});
......@@ -335,7 +305,7 @@ describe('Vulnerability related issues component', () => {
it('hides the "Create Issue" button', () => {
createWrapper({
provide: {
createIssueUrl: undefined,
newIssueUrl: undefined,
},
});
......
......@@ -16,7 +16,7 @@ describe('Vulnerability', () => {
report_type: 'sast',
state: 'detected',
create_mr_url: '/create_mr_url',
create_issue_url: '/create_issue_url',
new_issue_url: '/new_issue_url',
project_fingerprint: 'abc123',
pipeline: {
id: 2,
......
......@@ -58,7 +58,7 @@ RSpec.describe VulnerabilitiesHelper do
it 'has expected vulnerability properties' do
expect(subject).to include(
timestamp: Time.now.to_i,
create_issue_url: "/#{project.full_path}/-/security/vulnerabilities/#{vulnerability.id}/create_issue",
new_issue_url: "/#{project.full_path}/-/issues/new?vulnerability_id=#{vulnerability.id}",
create_jira_issue_url: nil,
related_jira_issues_path: "/#{project.full_path}/-/integrations/jira/issues?vulnerability_ids%5B%5D=#{vulnerability.id}",
has_mr: anything,
......@@ -76,8 +76,8 @@ RSpec.describe VulnerabilitiesHelper do
allow(project).to receive(:issues_enabled?).and_return(false)
end
it 'has `create_issue_url` set as nil' do
expect(subject).to include(create_issue_url: nil)
it 'has `new_issue_url` set as nil' do
expect(subject).to include(new_issue_url: nil)
end
end
end
......
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