Commit 24d71ccd authored by GitLab Bot's avatar GitLab Bot
Browse files

Add latest changes from gitlab-org/gitlab@master

parent fdb07c732cf0
......@@ -184,7 +184,7 @@ GEM
unicode_utils (~> 1.4)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.5)
crass (1.0.6)
creole (0.5.0)
css_parser (1.7.0)
addressable
......@@ -526,7 +526,7 @@ GEM
mime-types (~> 3.0)
multi_xml (>= 0.5.2)
httpclient (2.8.3)
i18n (1.7.0)
i18n (1.8.2)
concurrent-ruby (~> 1.0)
i18n_data (0.8.0)
icalendar (2.4.1)
......
......@@ -13,3 +13,9 @@ export const severityLevelVariant = {
[severityLevel.INFO]: 'info',
[severityLevel.DEBUG]: 'light',
};
export const errorStatus = {
IGNORED: 'ignored',
RESOLVED: 'resolved',
UNRESOLVED: 'unresolved',
};
......@@ -11,7 +11,7 @@ import Stacktrace from './stacktrace.vue';
import TrackEventDirective from '~/vue_shared/directives/track_event';
import timeagoMixin from '~/vue_shared/mixins/timeago';
import { trackClickErrorLinkToSentryOptions } from '../utils';
import { severityLevel, severityLevelVariant } from './constants';
import { severityLevel, severityLevelVariant, errorStatus } from './constants';
import query from '../queries/details.query.graphql';
......@@ -32,10 +32,6 @@ export default {
},
mixins: [timeagoMixin],
props: {
listPath: {
type: String,
required: true,
},
issueUpdatePath: {
type: String,
required: true,
......@@ -80,6 +76,7 @@ export default {
result(res) {
if (res.data.project?.sentryDetailedError) {
this.$apollo.queries.GQLerror.stopPolling();
this.setStatus(this.GQLerror.status);
}
},
},
......@@ -98,6 +95,7 @@ export default {
'stacktraceData',
'updatingResolveStatus',
'updatingIgnoreStatus',
'errorStatus',
]),
...mapGetters('details', ['stacktrace']),
reported() {
......@@ -153,20 +151,40 @@ export default {
severityLevelVariant[this.error.tags.level] || severityLevelVariant[severityLevel.ERROR]
);
},
ignoreBtnLabel() {
return this.errorStatus !== errorStatus.IGNORED ? __('Ignore') : __('Undo ignore');
},
resolveBtnLabel() {
return this.errorStatus !== errorStatus.RESOLVED ? __('Resolve') : __('Unresolve');
},
},
mounted() {
this.startPollingDetails(this.issueDetailsPath);
this.startPollingStacktrace(this.issueStackTracePath);
},
methods: {
...mapActions('details', ['startPollingDetails', 'startPollingStacktrace', 'updateStatus']),
...mapActions('details', [
'startPollingDetails',
'startPollingStacktrace',
'updateStatus',
'setStatus',
'updateResolveStatus',
'updateIgnoreStatus',
]),
trackClickErrorLinkToSentryOptions,
createIssue() {
this.issueCreationInProgress = true;
this.$refs.sentryIssueForm.submit();
},
updateIssueStatus(status) {
this.updateStatus({ endpoint: this.issueUpdatePath, redirectUrl: this.listPath, status });
onIgnoreStatusUpdate() {
const status =
this.errorStatus === errorStatus.IGNORED ? errorStatus.UNRESOLVED : errorStatus.IGNORED;
this.updateIgnoreStatus({ endpoint: this.issueUpdatePath, status });
},
onResolveStatusUpdate() {
const status =
this.errorStatus === errorStatus.RESOLVED ? errorStatus.UNRESOLVED : errorStatus.RESOLVED;
this.updateResolveStatus({ endpoint: this.issueUpdatePath, status });
},
formatDate(date) {
return `${this.timeFormatted(date)} (${dateFormat(date, 'UTC:yyyy-mm-dd h:MM:ssTT Z')})`;
......@@ -185,15 +203,17 @@ export default {
<span v-if="!loadingStacktrace && stacktrace" v-html="reported"></span>
<div class="d-inline-flex">
<loading-button
:label="__('Ignore')"
:label="ignoreBtnLabel"
:loading="updatingIgnoreStatus"
@click="updateIssueStatus('ignored')"
data-qa-selector="update_ignore_status_button"
@click="onIgnoreStatusUpdate"
/>
<loading-button
class="btn-outline-info ml-2"
:label="__('Resolve')"
:label="resolveBtnLabel"
:loading="updatingResolveStatus"
@click="updateIssueStatus('resolved')"
data-qa-selector="update_resolve_status_button"
@click="onResolveStatusUpdate"
/>
<gl-button
v-if="error.gitlab_issue"
......
......@@ -25,7 +25,6 @@ export default () => {
const {
issueId,
projectPath,
listPath,
issueUpdatePath,
issueDetailsPath,
issueStackTracePath,
......@@ -36,7 +35,6 @@ export default () => {
props: {
issueId,
projectPath,
listPath,
issueUpdatePath,
issueDetailsPath,
issueStackTracePath,
......
......@@ -6,6 +6,7 @@ query errorDetails($fullPath: ID!, $errorId: ID!) {
title
userCount
count
status
firstSeen
lastSeen
message
......
......@@ -4,16 +4,33 @@ import createFlash from '~/flash';
import { visitUrl } from '~/lib/utils/url_utility';
import { __ } from '~/locale';
export function updateStatus({ commit }, { endpoint, redirectUrl, status }) {
const type =
status === 'resolved' ? types.SET_UPDATING_RESOLVE_STATUS : types.SET_UPDATING_IGNORE_STATUS;
commit(type, true);
export const setStatus = ({ commit }, status) => {
commit(types.SET_ERROR_STATUS, status.toLowerCase());
};
return service
export const updateStatus = ({ commit }, { endpoint, redirectUrl, status }) =>
service
.updateErrorStatus(endpoint, status)
.then(() => visitUrl(redirectUrl))
.catch(() => createFlash(__('Failed to update issue status')))
.finally(() => commit(type, false));
}
.then(() => {
if (redirectUrl) visitUrl(redirectUrl);
commit(types.SET_ERROR_STATUS, status);
})
.catch(() => createFlash(__('Failed to update issue status')));
export const updateResolveStatus = ({ commit, dispatch }, params) => {
commit(types.SET_UPDATING_RESOLVE_STATUS, true);
return dispatch('updateStatus', params).finally(() => {
commit(types.SET_UPDATING_RESOLVE_STATUS, false);
});
};
export const updateIgnoreStatus = ({ commit, dispatch }, params) => {
commit(types.SET_UPDATING_IGNORE_STATUS, true);
return dispatch('updateStatus', params).finally(() => {
commit(types.SET_UPDATING_IGNORE_STATUS, false);
});
};
export default () => {};
......@@ -5,4 +5,5 @@ export default () => ({
loadingStacktrace: true,
updatingResolveStatus: false,
updatingIgnoreStatus: false,
errorStatus: '',
});
export const SET_UPDATING_RESOLVE_STATUS = 'SET_UPDATING_RESOLVE_STATUS';
export const SET_UPDATING_IGNORE_STATUS = 'SET_UPDATING_IGNORE_STATUS';
export const SET_ERROR_STATUS = 'SET_ERROR_STATUS';
......@@ -7,4 +7,7 @@ export default {
[types.SET_UPDATING_RESOLVE_STATUS](state, updating) {
state.updatingResolveStatus = updating;
},
[types.SET_ERROR_STATUS](state, status) {
state.errorStatus = status;
},
};
# frozen_string_literal: true
module Resolvers
module ErrorTracking
class SentryErrorStackTraceResolver < BaseResolver
argument :id, GraphQL::ID_TYPE,
required: true,
description: 'ID of the Sentry issue'
def resolve(**args)
issue_id = GlobalID.parse(args[:id]).model_id
# Get data from Sentry
response = ::ErrorTracking::IssueLatestEventService.new(
project,
current_user,
{ issue_id: issue_id }
).execute
event = response[:latest_event]
event.gitlab_project = project if event
event
end
private
def project
return object.gitlab_project if object.respond_to?(:gitlab_project)
object
end
end
end
end
......@@ -28,6 +28,10 @@ class SentryErrorCollectionType < ::Types::BaseObject
null: true,
description: 'Detailed version of a Sentry error on the project',
resolver: Resolvers::ErrorTracking::SentryDetailedErrorResolver
field :error_stack_trace, Types::ErrorTracking::SentryErrorStackTraceType,
null: true,
description: 'Stack Trace of Sentry Error',
resolver: Resolvers::ErrorTracking::SentryErrorStackTraceResolver
field :external_url,
GraphQL::STRING_TYPE,
null: true,
......
# frozen_string_literal: true
module Types
module ErrorTracking
# rubocop: disable Graphql/AuthorizeTypes
class SentryErrorStackTraceContextType < ::Types::BaseObject
graphql_name 'SentryErrorStackTraceContext'
description 'An object context for a Sentry error stack trace'
field :line,
GraphQL::INT_TYPE,
null: false,
description: 'Line number of the context'
field :code,
GraphQL::STRING_TYPE,
null: false,
description: 'Code number of the context'
def line
object[0]
end
def code
object[1]
end
end
# rubocop: enable Graphql/AuthorizeTypes
end
end
# frozen_string_literal: true
module Types
module ErrorTracking
# rubocop: disable Graphql/AuthorizeTypes
class SentryErrorStackTraceEntryType < ::Types::BaseObject
graphql_name 'SentryErrorStackTraceEntry'
description 'An object containing a stack trace entry for a Sentry error.'
field :function, GraphQL::STRING_TYPE,
null: true,
description: 'Function in which the Sentry error occurred'
field :col, GraphQL::STRING_TYPE,
null: true,
description: 'Function in which the Sentry error occurred'
field :line, GraphQL::STRING_TYPE,
null: true,
description: 'Function in which the Sentry error occurred'
field :file_name, GraphQL::STRING_TYPE,
null: true,
description: 'File in which the Sentry error occurred'
field :trace_context, [Types::ErrorTracking::SentryErrorStackTraceContextType],
null: true,
description: 'Context of the Sentry error'
def function
object['function']
end
def col
object['colNo']
end
def line
object['lineNo']
end
def file_name
object['filename']
end
def trace_context
object['context']
end
end
# rubocop: enable Graphql/AuthorizeTypes
end
end
# frozen_string_literal: true
module Types
module ErrorTracking
class SentryErrorStackTraceType < ::Types::BaseObject
graphql_name 'SentryErrorStackTrace'
description 'An object containing a stack trace entry for a Sentry error.'
authorize :read_sentry_issue
field :issue_id, GraphQL::STRING_TYPE,
null: false,
description: 'ID of the Sentry error'
field :date_received, GraphQL::STRING_TYPE,
null: false,
description: 'Time the stack trace was received by Sentry'
field :stack_trace_entries, [Types::ErrorTracking::SentryErrorStackTraceEntryType],
null: false,
description: 'Stack trace entries for the Sentry error'
end
end
end
......@@ -22,7 +22,6 @@ def error_details_data(project, issue_id)
{
'issue-id' => issue_id,
'project-path' => project.full_path,
'list-path' => project_error_tracking_index_path(project),
'issue-details-path' => details_project_error_tracking_index_path(*opts),
'issue-update-path' => update_project_error_tracking_index_path(*opts),
'project-issues-path' => project_issues_path(project),
......
.top-area
%ul.nav-links.nav.nav-tabs
= nav_link(page: [trending_explore_projects_path, explore_root_path]) do
= link_to trending_explore_projects_path do
= _('Trending')
= nav_link(page: [explore_projects_path, explore_root_path]) do
= link_to explore_projects_path do
= _('All')
= nav_link(page: starred_explore_projects_path) do
= link_to starred_explore_projects_path do
= _('Most stars')
= nav_link(page: explore_projects_path) do
= link_to explore_projects_path do
= _('All')
= nav_link(page: trending_explore_projects_path) do
= link_to trending_explore_projects_path do
= _('Trending')
.nav-controls
- unless current_user
......
---
title: Make Explore Projects default to All
merge_request: 23811
author:
type: changed
---
title: Reverse actions for resolve/ignore Sentry issue
merge_request: 23516
author:
type: added
---
title: Add Sentry error stack trace to GraphQL API
merge_request: 23750
author:
type: added
---
title: Separate snippet entities into own class files
merge_request: 24183
author: Rajendra Kadam
type: added
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