Commit 574f3bce authored by Phil Hughes's avatar Phil Hughes

Merge branch '222364-update-banner-create-issue' into 'master'

Improve error message when creating issue

See merge request gitlab-org/gitlab!40525
parents 207688e5 a167b495
...@@ -13,6 +13,8 @@ function createMainApp() { ...@@ -13,6 +13,8 @@ function createMainApp() {
createIssueUrl: vulnerability.create_issue_url, createIssueUrl: vulnerability.create_issue_url,
projectFingerprint: vulnerability.project_fingerprint, projectFingerprint: vulnerability.project_fingerprint,
vulnerabilityId: vulnerability.id, vulnerabilityId: vulnerability.id,
issueTrackingHelpPath: vulnerability.issueTrackingHelpPath,
permissionsHelpPath: vulnerability.permissionsHelpPath,
}, },
render: h => render: h =>
......
<script> <script>
import axios from 'axios'; import axios from 'axios';
import { GlButton } from '@gitlab/ui'; import { GlButton, GlAlert, GlSprintf, GlLink } from '@gitlab/ui';
import RelatedIssuesStore from '~/related_issues/stores/related_issues_store'; import RelatedIssuesStore from '~/related_issues/stores/related_issues_store';
import RelatedIssuesBlock from '~/related_issues/components/related_issues_block.vue'; import RelatedIssuesBlock from '~/related_issues/components/related_issues_block.vue';
import { issuableTypesMap, PathIdSeparator } from '~/related_issues/constants'; import { issuableTypesMap, PathIdSeparator } from '~/related_issues/constants';
...@@ -15,6 +15,9 @@ export default { ...@@ -15,6 +15,9 @@ export default {
components: { components: {
RelatedIssuesBlock, RelatedIssuesBlock,
GlButton, GlButton,
GlAlert,
GlSprintf,
GlLink,
}, },
props: { props: {
endpoint: { endpoint: {
...@@ -45,6 +48,7 @@ export default { ...@@ -45,6 +48,7 @@ export default {
isFetching: false, isFetching: false,
isSubmitting: false, isSubmitting: false,
isFormVisible: false, isFormVisible: false,
errorCreatingIssue: false,
inputValue: '', inputValue: '',
}; };
}, },
...@@ -69,6 +73,12 @@ export default { ...@@ -69,6 +73,12 @@ export default {
reportType: { reportType: {
type: String, type: String,
}, },
issueTrackingHelpPath: {
type: String,
},
permissionsHelpPath: {
type: String,
},
}, },
created() { created() {
this.fetchRelatedIssues(); this.fetchRelatedIssues();
...@@ -76,6 +86,7 @@ export default { ...@@ -76,6 +86,7 @@ export default {
methods: { methods: {
createIssue() { createIssue() {
this.isProcessingAction = true; this.isProcessingAction = true;
this.errorCreatingIssue = false;
return axios return axios
.post(this.createIssueUrl, { .post(this.createIssueUrl, {
...@@ -95,9 +106,7 @@ export default { ...@@ -95,9 +106,7 @@ export default {
}) })
.catch(() => { .catch(() => {
this.isProcessingAction = false; this.isProcessingAction = false;
createFlash( this.errorCreatingIssue = true;
s__('VulnerabilityManagement|Something went wrong, could not create an issue.'),
);
}); });
}, },
toggleFormVisibility() { toggleFormVisibility() {
...@@ -204,44 +213,76 @@ export default { ...@@ -204,44 +213,76 @@ export default {
autoCompleteSources: gl?.GfmAutoComplete?.dataSources, autoCompleteSources: gl?.GfmAutoComplete?.dataSources,
issuableType: issuableTypesMap.ISSUE, issuableType: issuableTypesMap.ISSUE,
pathIdSeparator: PathIdSeparator.Issue, pathIdSeparator: PathIdSeparator.Issue,
i18n: {
relatedIssues: __('Related issues'),
createIssue: __('Create issue'),
createIssueErrorTitle: __('Could not create issue'),
createIssueErrorBody: s__(
'SecurityReports|Ensure that %{trackingStart}issue tracking%{trackingEnd} is enabled for this project and you have %{permissionsStart}permission to create new issues%{permissionsEnd}.',
),
},
}; };
</script> </script>
<template> <template>
<related-issues-block <div>
:help-path="helpPath" <gl-alert
:is-fetching="isFetching" v-if="errorCreatingIssue"
:is-submitting="isSubmitting" variant="danger"
:related-issues="state.relatedIssues" class="gl-mt-5"
:can-admin="canModifyRelatedIssues" @dismiss="errorCreatingIssue = false"
:pending-references="state.pendingReferences" >
:is-form-visible="isFormVisible" <p class="gl-font-weight-bold gl-mb-2">{{ $options.i18n.createIssueErrorTitle }}</p>
:input-value="inputValue" <p class="gl-mb-0">
:auto-complete-sources="$options.autoCompleteSources" <gl-sprintf :message="$options.i18n.createIssueErrorBody">
:issuable-type="$options.issuableType" <template #tracking="{ content }">
:path-id-separator="$options.pathIdSeparator" <gl-link class="gl-display-inline-block" :href="issueTrackingHelpPath" target="_blank">
:show-categorized-issues="false" {{ content }}
@toggleAddRelatedIssuesForm="toggleFormVisibility" </gl-link>
@addIssuableFormInput="addPendingReferences" </template>
@addIssuableFormBlur="processAllReferences" <template #permissions="{ content }">
@addIssuableFormSubmit="addRelatedIssue" <gl-link class="gl-display-inline-block" :href="permissionsHelpPath" target="_blank">
@addIssuableFormCancel="resetForm" {{ content }}
@pendingIssuableRemoveRequest="removePendingReference" </gl-link>
@relatedIssueRemoveRequest="removeRelatedIssue" </template>
> </gl-sprintf>
<template #headerText> </p>
{{ __('Related issues') }} </gl-alert>
</template> <related-issues-block
<template v-if="!isIssueAlreadyCreated && !isFetching" #headerActions> :help-path="helpPath"
<gl-button :is-fetching="isFetching"
ref="createIssue" :is-submitting="isSubmitting"
variant="success" :related-issues="state.relatedIssues"
category="secondary" :can-admin="canModifyRelatedIssues"
:loading="isProcessingAction" :pending-references="state.pendingReferences"
@click="createIssue" :is-form-visible="isFormVisible"
> :input-value="inputValue"
{{ __('Create issue') }} :auto-complete-sources="$options.autoCompleteSources"
</gl-button> :issuable-type="$options.issuableType"
</template> :path-id-separator="$options.pathIdSeparator"
</related-issues-block> :show-categorized-issues="false"
@toggleAddRelatedIssuesForm="toggleFormVisibility"
@addIssuableFormInput="addPendingReferences"
@addIssuableFormBlur="processAllReferences"
@addIssuableFormSubmit="addRelatedIssue"
@addIssuableFormCancel="resetForm"
@pendingIssuableRemoveRequest="removePendingReference"
@relatedIssueRemoveRequest="removeRelatedIssue"
>
<template #headerText>
{{ $options.i18n.relatedIssues }}
</template>
<template v-if="!isIssueAlreadyCreated && !isFetching" #headerActions>
<gl-button
ref="createIssue"
variant="success"
category="secondary"
:loading="isProcessingAction"
@click="createIssue"
>
{{ $options.i18n.createIssue }}
</gl-button>
</template>
</related-issues-block>
</div>
</template> </template>
...@@ -18,7 +18,9 @@ module VulnerabilitiesHelper ...@@ -18,7 +18,9 @@ module VulnerabilitiesHelper
vulnerability_feedback_help_path: help_page_path('user/application_security/index', anchor: 'interacting-with-the-vulnerabilities'), vulnerability_feedback_help_path: help_page_path('user/application_security/index', anchor: 'interacting-with-the-vulnerabilities'),
related_issues_help_path: help_page_path('user/application_security/index', anchor: 'managing-related-issues-for-a-vulnerability'), related_issues_help_path: help_page_path('user/application_security/index', anchor: 'managing-related-issues-for-a-vulnerability'),
pipeline: vulnerability_pipeline_data(pipeline), pipeline: vulnerability_pipeline_data(pipeline),
can_modify_related_issues: current_user.can?(:admin_vulnerability_issue_link, vulnerability) can_modify_related_issues: current_user.can?(:admin_vulnerability_issue_link, vulnerability),
issue_tracking_help_path: help_page_path('user/project/settings', anchor: 'sharing-and-permissions'),
permissions_help_path: help_page_path('user/permissions', anchor: 'project-members-permissions')
} }
result.merge(vulnerability_data(vulnerability), vulnerability_finding_data(vulnerability)) result.merge(vulnerability_data(vulnerability), vulnerability_finding_data(vulnerability))
......
---
title: Improve error message when creating issue fails
merge_request: 40525
author:
type: changed
import { GlAlert } from '@gitlab/ui';
import { shallowMount } from '@vue/test-utils'; import { shallowMount } from '@vue/test-utils';
import MockAdapter from 'axios-mock-adapter'; import MockAdapter from 'axios-mock-adapter';
import RelatedIssues from 'ee/vulnerabilities/components/related_issues.vue'; import RelatedIssues from 'ee/vulnerabilities/components/related_issues.vue';
...@@ -27,6 +28,8 @@ describe('Vulnerability related issues component', () => { ...@@ -27,6 +28,8 @@ describe('Vulnerability related issues component', () => {
const vulnerabilityId = 5131; const vulnerabilityId = 5131;
const createIssueUrl = '/create/issue'; const createIssueUrl = '/create/issue';
const projectFingerprint = 'project-fingerprint'; const projectFingerprint = 'project-fingerprint';
const issueTrackingHelpPath = '/help/issue/tracking';
const permissionsHelpPath = '/help/permissions';
const reportType = 'vulnerability'; const reportType = 'vulnerability';
const issue1 = { id: 3, vulnerabilityLinkId: 987 }; const issue1 = { id: 3, vulnerabilityLinkId: 987 };
const issue2 = { id: 25, vulnerabilityLinkId: 876 }; const issue2 = { id: 25, vulnerabilityLinkId: 876 };
...@@ -40,6 +43,8 @@ describe('Vulnerability related issues component', () => { ...@@ -40,6 +43,8 @@ describe('Vulnerability related issues component', () => {
projectFingerprint, projectFingerprint,
createIssueUrl, createIssueUrl,
reportType, reportType,
issueTrackingHelpPath,
permissionsHelpPath,
}, },
...opts, ...opts,
}); });
...@@ -54,6 +59,7 @@ describe('Vulnerability related issues component', () => { ...@@ -54,6 +59,7 @@ describe('Vulnerability related issues component', () => {
const blockProp = prop => relatedIssuesBlock().props(prop); const blockProp = prop => relatedIssuesBlock().props(prop);
const blockEmit = (eventName, data) => relatedIssuesBlock().vm.$emit(eventName, data); const blockEmit = (eventName, data) => relatedIssuesBlock().vm.$emit(eventName, data);
const findCreateIssueButton = () => wrapper.find({ ref: 'createIssue' }); const findCreateIssueButton = () => wrapper.find({ ref: 'createIssue' });
const findAlert = () => wrapper.find(GlAlert);
afterEach(() => { afterEach(() => {
wrapper.destroy(); wrapper.destroy();
...@@ -275,6 +281,13 @@ describe('Vulnerability related issues component', () => { ...@@ -275,6 +281,13 @@ describe('Vulnerability related issues component', () => {
}); });
describe('when linked issue is not yet created', () => { 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();
};
beforeEach(async () => { beforeEach(async () => {
mockAxios.onGet(propsData.endpoint).replyOnce(httpStatusCodes.OK, [issue1, issue2]); mockAxios.onGet(propsData.endpoint).replyOnce(httpStatusCodes.OK, [issue1, issue2]);
createWrapper({}, { stubs: { RelatedIssuesBlock } }); createWrapper({}, { stubs: { RelatedIssuesBlock } });
...@@ -313,15 +326,17 @@ describe('Vulnerability related issues component', () => { ...@@ -313,15 +326,17 @@ describe('Vulnerability related issues component', () => {
}); });
}); });
it('shows an error message when issue creation fails', () => { it('shows an error message when issue creation fails', async () => {
mockAxios.onPost(createIssueUrl).reply(500); await failCreateIssueAction();
findCreateIssueButton().vm.$emit('click'); expect(mockAxios.history.post).toHaveLength(1);
return waitForPromises().then(() => { expect(findAlert().exists()).toBe(true);
expect(mockAxios.history.post).toHaveLength(1); });
expect(createFlash).toHaveBeenCalledWith(
'Something went wrong, could not create an issue.', it('dismisses the error message', async () => {
); await failCreateIssueAction();
}); findAlert().vm.$emit('dismiss');
await wrapper.vm.$nextTick();
expect(findAlert().exists()).toBe(false);
}); });
}); });
}); });
...@@ -7021,6 +7021,9 @@ msgstr "" ...@@ -7021,6 +7021,9 @@ msgstr ""
msgid "Could not create group" msgid "Could not create group"
msgstr "" msgstr ""
msgid "Could not create issue"
msgstr ""
msgid "Could not create project" msgid "Could not create project"
msgstr "" msgstr ""
...@@ -21867,6 +21870,9 @@ msgstr "" ...@@ -21867,6 +21870,9 @@ msgstr ""
msgid "SecurityReports|Either you don't have permission to view this dashboard or the dashboard has not been setup. Please check your permission settings with your administrator or check your dashboard configurations to proceed." msgid "SecurityReports|Either you don't have permission to view this dashboard or the dashboard has not been setup. Please check your permission settings with your administrator or check your dashboard configurations to proceed."
msgstr "" msgstr ""
msgid "SecurityReports|Ensure that %{trackingStart}issue tracking%{trackingEnd} is enabled for this project and you have %{permissionsStart}permission to create new issues%{permissionsEnd}."
msgstr ""
msgid "SecurityReports|Error fetching the vulnerability counts. Please check your network connection and try again." msgid "SecurityReports|Error fetching the vulnerability counts. Please check your network connection and try again."
msgstr "" msgstr ""
...@@ -27547,9 +27553,6 @@ msgstr "" ...@@ -27547,9 +27553,6 @@ msgstr ""
msgid "VulnerabilityManagement|Something went wrong while trying to unlink the issue. Please try again later." msgid "VulnerabilityManagement|Something went wrong while trying to unlink the issue. Please try again later."
msgstr "" msgstr ""
msgid "VulnerabilityManagement|Something went wrong, could not create an issue."
msgstr ""
msgid "VulnerabilityManagement|Something went wrong, could not get user." msgid "VulnerabilityManagement|Something went wrong, could not get user."
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