Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gitlab-ce
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
1
Merge Requests
1
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
gitlab-ce
Commits
fe22a870
Commit
fe22a870
authored
Jan 14, 2020
by
Olena Horal-Koretska
Committed by
Natalia Tepluhina
Jan 14, 2020
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Use GraphQL to load error tracking detail page content
REST call is filling up missing fields
parent
afa09780
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
159 additions
and
49 deletions
+159
-49
app/assets/javascripts/error_tracking/components/error_details.vue
...s/javascripts/error_tracking/components/error_details.vue
+66
-33
app/assets/javascripts/error_tracking/details.js
app/assets/javascripts/error_tracking/details.js
+18
-1
app/assets/javascripts/error_tracking/queries/details.query.graphql
.../javascripts/error_tracking/queries/details.query.graphql
+18
-0
app/helpers/projects/error_tracking_helper.rb
app/helpers/projects/error_tracking_helper.rb
+3
-1
changelogs/unreleased/39979-grapql-for-error-details.yml
changelogs/unreleased/39979-grapql-for-error-details.yml
+5
-0
spec/frontend/error_tracking/components/error_details_spec.js
.../frontend/error_tracking/components/error_details_spec.js
+40
-14
spec/helpers/projects/error_tracking_helper_spec.rb
spec/helpers/projects/error_tracking_helper_spec.rb
+9
-0
No files found.
app/assets/javascripts/error_tracking/components/error_details.vue
View file @
fe22a870
<
script
>
import
{
mapActions
,
mapGetters
,
mapState
}
from
'
vuex
'
;
import
dateFormat
from
'
dateformat
'
;
import
createFlash
from
'
~/flash
'
;
import
{
GlFormInput
,
GlLink
,
GlLoadingIcon
,
GlBadge
}
from
'
@gitlab/ui
'
;
import
{
__
,
sprintf
,
n__
}
from
'
~/locale
'
;
import
LoadingButton
from
'
~/vue_shared/components/loading_button.vue
'
;
...
...
@@ -11,6 +12,8 @@ import TrackEventDirective from '~/vue_shared/directives/track_event';
import
timeagoMixin
from
'
~/vue_shared/mixins/timeago
'
;
import
{
trackClickErrorLinkToSentryOptions
}
from
'
../utils
'
;
import
query
from
'
../queries/details.query.graphql
'
;
export
default
{
components
:
{
LoadingButton
,
...
...
@@ -27,6 +30,14 @@ export default {
},
mixins
:
[
timeagoMixin
],
props
:
{
issueId
:
{
type
:
String
,
required
:
true
,
},
projectPath
:
{
type
:
String
,
required
:
true
,
},
issueDetailsPath
:
{
type
:
String
,
required
:
true
,
...
...
@@ -44,8 +55,28 @@ export default {
required
:
true
,
},
},
apollo
:
{
GQLerror
:
{
query
,
variables
()
{
return
{
fullPath
:
this
.
projectPath
,
errorId
:
`gid://gitlab/Gitlab::ErrorTracking::DetailedError/
${
this
.
issueId
}
`
,
};
},
pollInterval
:
2000
,
update
:
data
=>
data
.
project
.
sentryDetailedError
,
error
:
()
=>
createFlash
(
__
(
'
Failed to load error details from Sentry.
'
)),
result
(
res
)
{
if
(
res
.
data
.
project
?.
sentryDetailedError
)
{
this
.
$apollo
.
queries
.
GQLerror
.
stopPolling
();
}
},
},
},
data
()
{
return
{
GQLerror
:
null
,
issueCreationInProgress
:
false
,
};
},
...
...
@@ -56,26 +87,28 @@ export default {
return
sprintf
(
__
(
'
Reported %{timeAgo} by %{reportedBy}
'
),
{
reportedBy
:
`<strong>
${
this
.
error
.
culprit
}
</strong>`
,
reportedBy
:
`<strong>
${
this
.
GQL
error
.
culprit
}
</strong>`
,
timeAgo
:
this
.
timeFormatted
(
this
.
stacktraceData
.
date_received
),
},
false
,
);
},
firstReleaseLink
()
{
return
`
${
this
.
error
.
external_base_url
}
/releases/
${
this
.
error
.
first_release_short_v
ersion
}
`
;
return
`
${
this
.
error
.
external_base_url
}
/releases/
${
this
.
GQLerror
.
firstReleaseShortV
ersion
}
`
;
},
lastReleaseLink
()
{
return
`
${
this
.
error
.
external_base_url
}
releases/
${
this
.
error
.
last_release_short_v
ersion
}
`
;
return
`
${
this
.
error
.
external_base_url
}
releases/
${
this
.
GQLerror
.
lastReleaseShortV
ersion
}
`
;
},
showDetails
()
{
return
Boolean
(
!
this
.
loading
&&
this
.
error
&&
this
.
error
.
id
);
return
Boolean
(
!
this
.
loading
&&
!
this
.
$apollo
.
queries
.
GQLerror
.
loading
&&
this
.
error
&&
this
.
GQLerror
,
);
},
showStacktrace
()
{
return
Boolean
(
!
this
.
loadingStacktrace
&&
this
.
stacktrace
&&
this
.
stacktrace
.
length
);
},
issueTitle
()
{
return
this
.
error
.
title
;
return
this
.
GQL
error
.
title
;
},
issueDescription
()
{
return
sprintf
(
...
...
@@ -84,13 +117,13 @@ export default {
),
{
description
:
'
# Error Details:
\n
'
,
errorUrl
:
`
${
this
.
error
.
external_u
rl
}
\n`
,
firstSeen
:
`\n
${
this
.
error
.
first_s
een
}
\n`
,
lastSeen
:
`
${
this
.
error
.
last_s
een
}
\n`
,
countLabel
:
n__
(
'
- Event
'
,
'
- Events
'
,
this
.
error
.
count
),
count
:
`
${
this
.
error
.
count
}
\n`
,
userCountLabel
:
n__
(
'
- User
'
,
'
- Users
'
,
this
.
error
.
user_c
ount
),
userCount
:
`
${
this
.
error
.
user_c
ount
}
\n`
,
errorUrl
:
`
${
this
.
GQLerror
.
externalU
rl
}
\n`
,
firstSeen
:
`\n
${
this
.
GQLerror
.
firstS
een
}
\n`
,
lastSeen
:
`
${
this
.
GQLerror
.
lastS
een
}
\n`
,
countLabel
:
n__
(
'
- Event
'
,
'
- Events
'
,
this
.
GQL
error
.
count
),
count
:
`
${
this
.
GQL
error
.
count
}
\n`
,
userCountLabel
:
n__
(
'
- User
'
,
'
- Users
'
,
this
.
GQLerror
.
userC
ount
),
userCount
:
`
${
this
.
GQLerror
.
userC
ount
}
\n`
,
},
false
,
);
...
...
@@ -119,7 +152,7 @@ export default {
<
template
>
<div>
<div
v-if=
"loading"
class=
"py-3"
>
<div
v-if=
"
$apollo.queries.GQLerror.loading ||
loading"
class=
"py-3"
>
<gl-loading-icon
:size=
"3"
/>
</div>
<div
v-else-if=
"showDetails"
class=
"error-details"
>
...
...
@@ -129,7 +162,7 @@ export default {
<gl-form-input
class=
"hidden"
name=
"issue[title]"
:value=
"issueTitle"
/>
<input
name=
"issue[description]"
:value=
"issueDescription"
type=
"hidden"
/>
<gl-form-input
:value=
"error.id"
:value=
"
GQL
error.id"
class=
"hidden"
name=
"issue[sentry_issue_attributes][sentry_issue_identifier]"
/>
...
...
@@ -145,16 +178,16 @@ export default {
</form>
</div>
<div>
<tooltip-on-truncate
:title=
"error.title"
truncate-target=
"child"
placement=
"top"
>
<h2
class=
"text-truncate"
>
{{
error
.
title
}}
</h2>
<tooltip-on-truncate
:title=
"
GQL
error.title"
truncate-target=
"child"
placement=
"top"
>
<h2
class=
"text-truncate"
>
{{
GQL
error
.
title
}}
</h2>
</tooltip-on-truncate>
<template
v-if=
"error.tags"
>
<gl-badge
v-if=
"error.tags.level"
variant=
"danger"
class=
"rounded-pill mr-2"
>
{{
errorLevel
}}
</gl-badge>
<gl-badge
v-if=
"error.tags.logger"
variant=
"light"
class=
"rounded-pill"
>
{{
error
.
tags
.
logger
}}
</gl-badge>
<gl-badge
v-if=
"error.tags.level"
variant=
"danger"
class=
"rounded-pill mr-2"
>
{{
errorLevel
}}
</gl-badge>
<gl-badge
v-if=
"error.tags.logger"
variant=
"light"
class=
"rounded-pill"
>
{{
error
.
tags
.
logger
}}
</gl-badge>
</
template
>
<h3>
{{ __('Error details') }}
</h3>
...
...
@@ -168,35 +201,35 @@ export default {
<li>
<span
class=
"bold"
>
{{ __('Sentry event') }}:
</span>
<gl-link
v-track-event=
"trackClickErrorLinkToSentryOptions(
error.external_u
rl)"
:href=
"
error.external_u
rl"
v-track-event=
"trackClickErrorLinkToSentryOptions(
GQLerror.externalU
rl)"
:href=
"
GQLerror.externalU
rl"
target=
"_blank"
>
<span
class=
"text-truncate"
>
{{
error.external_u
rl }}
</span>
<span
class=
"text-truncate"
>
{{
GQLerror.externalU
rl }}
</span>
<icon
name=
"external-link"
class=
"ml-1 flex-shrink-0"
/>
</gl-link>
</li>
<li
v-if=
"
error.first_release_short_v
ersion"
>
<li
v-if=
"
GQLerror.firstReleaseShortV
ersion"
>
<span
class=
"bold"
>
{{ __('First seen') }}:
</span>
{{ formatDate(
error.first_s
een) }}
{{ formatDate(
GQLerror.firstS
een) }}
<gl-link
:href=
"firstReleaseLink"
target=
"_blank"
>
<span>
{{ __('Release') }}: {{
error.first_release_short_v
ersion }}
</span>
<span>
{{ __('Release') }}: {{
GQLerror.firstReleaseShortV
ersion }}
</span>
</gl-link>
</li>
<li
v-if=
"
error.last_release_short_v
ersion"
>
<li
v-if=
"
GQLerror.lastReleaseShortV
ersion"
>
<span
class=
"bold"
>
{{ __('Last seen') }}:
</span>
{{ formatDate(
error.last_s
een) }}
{{ formatDate(
GQLerror.lastS
een) }}
<gl-link
:href=
"lastReleaseLink"
target=
"_blank"
>
<span>
{{ __('Release') }}: {{
error.last_release_short_v
ersion }}
</span>
<span>
{{ __('Release') }}: {{
GQLerror.lastReleaseShortV
ersion }}
</span>
</gl-link>
</li>
<li>
<span
class=
"bold"
>
{{ __('Events') }}:
</span>
<span>
{{ error.count }}
</span>
<span>
{{
GQL
error.count }}
</span>
</li>
<li>
<span
class=
"bold"
>
{{ __('Users') }}:
</span>
<span>
{{
error.user_c
ount }}
</span>
<span>
{{
GQLerror.userC
ount }}
</span>
</li>
</ul>
...
...
app/assets/javascripts/error_tracking/details.js
View file @
fe22a870
import
Vue
from
'
vue
'
;
import
VueApollo
from
'
vue-apollo
'
;
import
createDefaultClient
from
'
~/lib/graphql
'
;
import
store
from
'
./store
'
;
import
ErrorDetails
from
'
./components/error_details.vue
'
;
import
csrf
from
'
~/lib/utils/csrf
'
;
Vue
.
use
(
VueApollo
);
export
default
()
=>
{
const
apolloProvider
=
new
VueApollo
({
defaultClient
:
createDefaultClient
(),
});
// eslint-disable-next-line no-new
new
Vue
({
el
:
'
#js-error_details
'
,
apolloProvider
,
components
:
{
ErrorDetails
,
},
store
,
render
(
createElement
)
{
const
domEl
=
document
.
querySelector
(
this
.
$options
.
el
);
const
{
issueDetailsPath
,
issueStackTracePath
,
projectIssuesPath
}
=
domEl
.
dataset
;
const
{
issueId
,
projectPath
,
issueDetailsPath
,
issueStackTracePath
,
projectIssuesPath
,
}
=
domEl
.
dataset
;
return
createElement
(
'
error-details
'
,
{
props
:
{
issueId
,
projectPath
,
issueDetailsPath
,
issueStackTracePath
,
projectIssuesPath
,
...
...
app/assets/javascripts/error_tracking/queries/details.query.graphql
0 → 100644
View file @
fe22a870
query
errorDetails
(
$fullPath
:
ID
!,
$errorId
:
ID
!)
{
project
(
fullPath
:
$fullPath
)
{
sentryDetailedError
(
id
:
$errorId
)
{
id
sentryId
title
userCount
count
firstSeen
lastSeen
message
culprit
externalUrl
firstReleaseShortVersion
lastReleaseShortVersion
}
}
}
app/helpers/projects/error_tracking_helper.rb
View file @
fe22a870
...
...
@@ -18,9 +18,11 @@ module Projects::ErrorTrackingHelper
opts
=
[
project
,
issue_id
,
{
format: :json
}]
{
'project-issues-path'
=>
project_issues_path
(
project
),
'issue-id'
=>
issue_id
,
'project-path'
=>
project
.
full_path
,
'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
),
'issue-stack-trace-path'
=>
stack_trace_project_error_tracking_index_path
(
*
opts
)
}
end
...
...
changelogs/unreleased/39979-grapql-for-error-details.yml
0 → 100644
View file @
fe22a870
---
title
:
Use GraphQL to load error tracking detail page content
merge_request
:
22422
author
:
type
:
performance
spec/frontend/error_tracking/components/error_details_spec.js
View file @
fe22a870
...
...
@@ -13,6 +13,7 @@ describe('ErrorDetails', () => {
let
wrapper
;
let
actions
;
let
getters
;
let
mocks
;
const
findInput
=
name
=>
{
const
inputs
=
wrapper
.
findAll
(
GlFormInput
).
filter
(
c
=>
c
.
attributes
(
'
name
'
)
===
name
);
...
...
@@ -24,13 +25,27 @@ describe('ErrorDetails', () => {
stubs
:
{
LoadingButton
},
localVue
,
store
,
mocks
,
propsData
:
{
issueId
:
'
123
'
,
projectPath
:
'
/root/gitlab-test
'
,
issueDetailsPath
:
'
/123/details
'
,
issueStackTracePath
:
'
/stacktrace
'
,
projectIssuesPath
:
'
/test-project/issues/
'
,
csrfToken
:
'
fakeToken
'
,
},
});
wrapper
.
setData
({
GQLerror
:
{
id
:
129381
,
title
:
'
Issue title
'
,
externalUrl
:
'
http://sentry.gitlab.net/gitlab
'
,
firstSeen
:
'
2017-05-26T13:32:48Z
'
,
lastSeen
:
'
2018-05-26T13:32:48Z
'
,
count
:
12
,
userCount
:
2
,
},
});
}
beforeEach
(()
=>
{
...
...
@@ -61,6 +76,19 @@ describe('ErrorDetails', () => {
},
},
});
const
query
=
jest
.
fn
();
mocks
=
{
$apollo
:
{
query
,
queries
:
{
GQLerror
:
{
loading
:
true
,
stopPolling
:
jest
.
fn
(),
},
},
},
};
});
afterEach
(()
=>
{
...
...
@@ -85,10 +113,11 @@ describe('ErrorDetails', () => {
beforeEach
(()
=>
{
store
.
state
.
details
.
loading
=
false
;
store
.
state
.
details
.
error
.
id
=
1
;
mocks
.
$apollo
.
queries
.
GQLerror
.
loading
=
false
;
mountComponent
();
});
it
(
'
should show Sentry error details without stacktrace
'
,
()
=>
{
mountComponent
();
expect
(
wrapper
.
find
(
GlLink
).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
find
(
GlLoadingIcon
).
exists
()).
toBe
(
true
);
expect
(
wrapper
.
find
(
Stacktrace
).
exists
()).
toBe
(
false
);
...
...
@@ -99,13 +128,17 @@ describe('ErrorDetails', () => {
it
(
'
should show language and error level badges
'
,
()
=>
{
store
.
state
.
details
.
error
.
tags
=
{
level
:
'
error
'
,
logger
:
'
ruby
'
};
mountComponent
();
expect
(
wrapper
.
findAll
(
GlBadge
).
length
).
toBe
(
2
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
findAll
(
GlBadge
).
length
).
toBe
(
2
);
});
});
it
(
'
should NOT show the badge if the tag is not present
'
,
()
=>
{
store
.
state
.
details
.
error
.
tags
=
{
level
:
'
error
'
};
mountComponent
();
expect
(
wrapper
.
findAll
(
GlBadge
).
length
).
toBe
(
1
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
findAll
(
GlBadge
).
length
).
toBe
(
1
);
});
});
});
...
...
@@ -113,8 +146,10 @@ describe('ErrorDetails', () => {
it
(
'
should show stacktrace
'
,
()
=>
{
store
.
state
.
details
.
loadingStacktrace
=
false
;
mountComponent
();
expect
(
wrapper
.
find
(
GlLoadingIcon
).
exists
()).
toBe
(
false
);
expect
(
wrapper
.
find
(
Stacktrace
).
exists
()).
toBe
(
true
);
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
expect
(
wrapper
.
find
(
GlLoadingIcon
).
exists
()).
toBe
(
false
);
expect
(
wrapper
.
find
(
Stacktrace
).
exists
()).
toBe
(
true
);
});
});
it
(
'
should NOT show stacktrace if no entries
'
,
()
=>
{
...
...
@@ -128,15 +163,6 @@ describe('ErrorDetails', () => {
describe
(
'
When a user clicks the create issue button
'
,
()
=>
{
beforeEach
(()
=>
{
store
.
state
.
details
.
error
=
{
id
:
129381
,
title
:
'
Issue title
'
,
external_url
:
'
http://sentry.gitlab.net/gitlab
'
,
first_seen
:
'
2017-05-26T13:32:48Z
'
,
last_seen
:
'
2018-05-26T13:32:48Z
'
,
count
:
12
,
user_count
:
2
,
};
mountComponent
();
});
...
...
spec/helpers/projects/error_tracking_helper_spec.rb
View file @
fe22a870
...
...
@@ -80,11 +80,20 @@ describe Projects::ErrorTrackingHelper do
let
(
:issue_id
)
{
1234
}
let
(
:route_params
)
{
[
project
.
owner
,
project
,
issue_id
,
{
format: :json
}]
}
let
(
:details_path
)
{
details_namespace_project_error_tracking_index_path
(
*
route_params
)
}
let
(
:project_path
)
{
project
.
full_path
}
let
(
:stack_trace_path
)
{
stack_trace_namespace_project_error_tracking_index_path
(
*
route_params
)
}
let
(
:issues_path
)
{
project_issues_path
(
project
)
}
let
(
:result
)
{
helper
.
error_details_data
(
project
,
issue_id
)
}
it
'returns the correct issue id'
do
expect
(
result
[
'issue-id'
]).
to
eq
issue_id
end
it
'returns the correct project path'
do
expect
(
result
[
'project-path'
]).
to
eq
project_path
end
it
'returns the correct details path'
do
expect
(
result
[
'issue-details-path'
]).
to
eq
details_path
end
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment