Commit f4185043 authored by Olena Horal-Koretska's avatar Olena Horal-Koretska

Show alert on related issue close

parent 146eef9f
...@@ -2,7 +2,15 @@ ...@@ -2,7 +2,15 @@
import { mapActions, mapGetters, mapState } from 'vuex'; import { mapActions, mapGetters, mapState } from 'vuex';
import dateFormat from 'dateformat'; import dateFormat from 'dateformat';
import createFlash from '~/flash'; import createFlash from '~/flash';
import { GlButton, GlFormInput, GlLink, GlLoadingIcon, GlBadge } from '@gitlab/ui'; import {
GlButton,
GlFormInput,
GlLink,
GlLoadingIcon,
GlBadge,
GlAlert,
GlSprintf,
} from '@gitlab/ui';
import { __, sprintf, n__ } from '~/locale'; import { __, sprintf, n__ } from '~/locale';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import Icon from '~/vue_shared/components/icon.vue'; import Icon from '~/vue_shared/components/icon.vue';
...@@ -26,6 +34,8 @@ export default { ...@@ -26,6 +34,8 @@ export default {
Icon, Icon,
Stacktrace, Stacktrace,
GlBadge, GlBadge,
GlAlert,
GlSprintf,
}, },
directives: { directives: {
TrackEvent: TrackEventDirective, TrackEvent: TrackEventDirective,
...@@ -85,6 +95,8 @@ export default { ...@@ -85,6 +95,8 @@ export default {
return { return {
GQLerror: null, GQLerror: null,
issueCreationInProgress: false, issueCreationInProgress: false,
isAlertVisible: false,
closedIssueId: null,
}; };
}, },
computed: { computed: {
...@@ -184,7 +196,14 @@ export default { ...@@ -184,7 +196,14 @@ export default {
onResolveStatusUpdate() { onResolveStatusUpdate() {
const status = const status =
this.errorStatus === errorStatus.RESOLVED ? errorStatus.UNRESOLVED : errorStatus.RESOLVED; this.errorStatus === errorStatus.RESOLVED ? errorStatus.UNRESOLVED : errorStatus.RESOLVED;
this.updateResolveStatus({ endpoint: this.issueUpdatePath, status });
// eslint-disable-next-line promise/catch-or-return
this.updateResolveStatus({ endpoint: this.issueUpdatePath, status }).then(res => {
this.closedIssueId = res.closed_issue_iid;
if (this.closedIssueId) {
this.isAlertVisible = true;
}
});
}, },
formatDate(date) { formatDate(date) {
return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`; return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
...@@ -199,6 +218,18 @@ export default { ...@@ -199,6 +218,18 @@ export default {
<gl-loading-icon :size="3" /> <gl-loading-icon :size="3" />
</div> </div>
<div v-else-if="showDetails" class="error-details"> <div v-else-if="showDetails" class="error-details">
<gl-alert v-if="isAlertVisible" @dismiss="isAlertVisible = false">
<gl-sprintf
:message="
__('The associated issue #%{issueId} has been closed as the error is now resolved.')
"
>
<template #issueId>
<span>{{ closedIssueId }}</span>
</template>
</gl-sprintf>
</gl-alert>
<div class="top-area align-items-center justify-content-between py-3"> <div class="top-area align-items-center justify-content-between py-3">
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span> <span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
<div class="d-inline-flex"> <div class="d-inline-flex">
......
...@@ -11,9 +11,11 @@ export const setStatus = ({ commit }, status) => { ...@@ -11,9 +11,11 @@ export const setStatus = ({ commit }, status) => {
export const updateStatus = ({ commit }, { endpoint, redirectUrl, status }) => export const updateStatus = ({ commit }, { endpoint, redirectUrl, status }) =>
service service
.updateErrorStatus(endpoint, status) .updateErrorStatus(endpoint, status)
.then(() => { .then(resp => {
if (redirectUrl) visitUrl(redirectUrl);
commit(types.SET_ERROR_STATUS, status); commit(types.SET_ERROR_STATUS, status);
if (redirectUrl) visitUrl(redirectUrl);
return resp.data.result;
}) })
.catch(() => createFlash(__('Failed to update issue status'))); .catch(() => createFlash(__('Failed to update issue status')));
......
---
title: Close related GitLab issue on Sentry error resolve
merge_request: 23610
author:
type: added
...@@ -18666,6 +18666,9 @@ msgstr "" ...@@ -18666,6 +18666,9 @@ msgstr ""
msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential." msgid "The application will be used where the client secret can be kept confidential. Native mobile apps and Single Page Apps are considered non-confidential."
msgstr "" msgstr ""
msgid "The associated issue #%{issueId} has been closed as the error is now resolved."
msgstr ""
msgid "The branch for this project has no active pipeline configuration." msgid "The branch for this project has no active pipeline configuration."
msgstr "" msgstr ""
......
import { createLocalVue, shallowMount } from '@vue/test-utils'; import { createLocalVue, shallowMount } from '@vue/test-utils';
import Vuex from 'vuex'; import Vuex from 'vuex';
import { __ } from '~/locale'; import { __ } from '~/locale';
import { GlLoadingIcon, GlLink, GlBadge, GlFormInput } from '@gitlab/ui'; import { GlLoadingIcon, GlLink, GlBadge, GlFormInput, GlAlert, GlSprintf } from '@gitlab/ui';
import LoadingButton from '~/vue_shared/components/loading_button.vue'; import LoadingButton from '~/vue_shared/components/loading_button.vue';
import Stacktrace from '~/error_tracking/components/stacktrace.vue'; import Stacktrace from '~/error_tracking/components/stacktrace.vue';
import ErrorDetails from '~/error_tracking/components/error_details.vue'; import ErrorDetails from '~/error_tracking/components/error_details.vue';
...@@ -28,7 +28,7 @@ describe('ErrorDetails', () => { ...@@ -28,7 +28,7 @@ describe('ErrorDetails', () => {
function mountComponent() { function mountComponent() {
wrapper = shallowMount(ErrorDetails, { wrapper = shallowMount(ErrorDetails, {
stubs: { LoadingButton }, stubs: { LoadingButton, GlSprintf },
localVue, localVue,
store, store,
mocks, mocks,
...@@ -62,7 +62,7 @@ describe('ErrorDetails', () => { ...@@ -62,7 +62,7 @@ describe('ErrorDetails', () => {
startPollingDetails: () => {}, startPollingDetails: () => {},
startPollingStacktrace: () => {}, startPollingStacktrace: () => {},
updateIgnoreStatus: jest.fn(), updateIgnoreStatus: jest.fn(),
updateResolveStatus: jest.fn(), updateResolveStatus: jest.fn().mockResolvedValue({ closed_issue_iid: 1 }),
}; };
getters = { getters = {
...@@ -313,6 +313,20 @@ describe('ErrorDetails', () => { ...@@ -313,6 +313,20 @@ describe('ErrorDetails', () => {
expect.objectContaining({ status: errorStatus.UNRESOLVED }), expect.objectContaining({ status: errorStatus.UNRESOLVED }),
); );
}); });
it('should show alert with closed issueId', () => {
const findAlert = () => wrapper.find(GlAlert);
const closedIssueId = 123;
wrapper.setData({
isAlertVisible: true,
closedIssueId,
});
return wrapper.vm.$nextTick().then(() => {
expect(findAlert().exists()).toBe(true);
expect(findAlert().text()).toContain(`#${closedIssueId}`);
});
});
}); });
}); });
......
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