Commit d7a0a552 authored by Natalia Tepluhina's avatar Natalia Tepluhina

Merge branch '197371-show-notification-on-related-error-close' into 'master'

Close related GitLab issue on Sentry error resolve

See merge request gitlab-org/gitlab!23610
parents b08c2c02 f4185043
...@@ -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
...@@ -18696,6 +18696,9 @@ msgstr "" ...@@ -18696,6 +18696,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