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
b0574055
Commit
b0574055
authored
Mar 04, 2021
by
David O'Regan
Committed by
Simon Knox
Mar 04, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Feat(incidents): add issue state to alerts table
Show the current status of an linked issue in the alerts table
parent
da42ee6f
Changes
15
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
171 additions
and
61 deletions
+171
-61
app/assets/javascripts/alert_management/components/alert_management_table.vue
...ts/alert_management/components/alert_management_table.vue
+15
-5
app/assets/javascripts/graphql_shared/fragments/alert.fragment.graphql
...vascripts/graphql_shared/fragments/alert.fragment.graphql
+5
-1
app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
...pts/vue_shared/alert_details/components/alert_details.vue
+2
-2
app/graphql/resolvers/alert_management/alert_resolver.rb
app/graphql/resolvers/alert_management/alert_resolver.rb
+2
-1
app/graphql/types/alert_management/alert_type.rb
app/graphql/types/alert_management/alert_type.rb
+6
-0
changelogs/unreleased/228733-alert-issue-status.yml
changelogs/unreleased/228733-alert-issue-status.yml
+5
-0
doc/api/graphql/reference/index.md
doc/api/graphql/reference/index.md
+2
-1
locale/gitlab.pot
locale/gitlab.pot
+3
-0
spec/frontend/alert_management/components/alert_management_table_spec.js
...lert_management/components/alert_management_table_spec.js
+46
-36
spec/frontend/vue_shared/alert_details/alert_details_spec.js
spec/frontend/vue_shared/alert_details/alert_details_spec.js
+6
-8
spec/frontend/vue_shared/alert_details/mocks/alerts.json
spec/frontend/vue_shared/alert_details/mocks/alerts.json
+1
-1
spec/graphql/types/alert_management/alert_type_spec.rb
spec/graphql/types/alert_management/alert_type_spec.rb
+2
-1
spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb
...ations/alert_management/alerts/create_alert_issue_spec.rb
+4
-2
spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb
.../api/graphql/project/alert_management/alert/issue_spec.rb
+71
-0
spec/requests/api/graphql/project/alert_management/alerts_spec.rb
...uests/api/graphql/project/alert_management/alerts_spec.rb
+1
-3
No files found.
app/assets/javascripts/alert_management/components/alert_management_table.vue
View file @
b0574055
...
...
@@ -42,6 +42,7 @@ export default {
"
AlertManagement|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear.
"
,
),
unassigned
:
__
(
'
Unassigned
'
),
closed
:
__
(
'
closed
'
),
},
fields
:
[
{
...
...
@@ -75,7 +76,7 @@ export default {
{
key
:
'
issue
'
,
label
:
s__
(
'
AlertManagement|Incident
'
),
thClass
:
'
gl-w-1
2
gl-pointer-events-none
'
,
thClass
:
'
gl-w-1
5p
gl-pointer-events-none
'
,
tdClass
,
},
{
...
...
@@ -221,8 +222,11 @@ export default {
hasAssignees
(
assignees
)
{
return
Boolean
(
assignees
.
nodes
?.
length
);
},
getIssueLink
(
item
)
{
return
joinPaths
(
'
/
'
,
this
.
projectPath
,
'
-
'
,
'
issues
'
,
item
.
issueIid
);
getIssueMeta
({
issue
:
{
iid
,
state
}
})
{
return
{
state
:
state
===
'
closed
'
?
`(
${
this
.
$options
.
i18n
.
closed
}
)`
:
''
,
link
:
joinPaths
(
'
/
'
,
this
.
projectPath
,
'
-
'
,
'
issues
'
,
iid
),
};
},
tbodyTrClass
(
item
)
{
return
{
...
...
@@ -343,8 +347,14 @@ export default {
</
template
>
<
template
#cell(issue)=
"{ item }"
>
<gl-link
v-if=
"item.issueIid"
data-testid=
"issueField"
:href=
"getIssueLink(item)"
>
#
{{
item
.
issueIid
}}
<gl-link
v-if=
"item.issue"
v-gl-tooltip
:title=
"item.issue.title"
data-testid=
"issueField"
:href=
"getIssueMeta(item).link"
>
#
{{
item
.
issue
.
iid
}}
{{
getIssueMeta
(
item
).
state
}}
</gl-link>
<div
v-else
data-testid=
"issueField"
>
{{
s__
(
'
AlertManagement|None
'
)
}}
</div>
</
template
>
...
...
app/assets/javascripts/graphql_shared/fragments/alert.fragment.graphql
View file @
b0574055
...
...
@@ -5,7 +5,11 @@ fragment AlertListItem on AlertManagementAlert {
status
startedAt
eventCount
issueIid
issue
{
iid
state
title
}
assignees
{
nodes
{
name
...
...
app/assets/javascripts/vue_shared/alert_details/components/alert_details.vue
View file @
b0574055
...
...
@@ -268,10 +268,10 @@ export default {
</span>
</div>
<gl-button
v-if=
"alert.issue
Iid
"
v-if=
"alert.issue"
class=
"gl-mt-3 mt-sm-0 align-self-center align-self-sm-baseline alert-details-incident-button"
data-testid=
"viewIncidentBtn"
:href=
"incidentPath(alert.issue
I
id)"
:href=
"incidentPath(alert.issue
.i
id)"
category=
"primary"
variant=
"success"
>
...
...
app/graphql/resolvers/alert_management/alert_resolver.rb
View file @
b0574055
...
...
@@ -43,7 +43,8 @@ module Resolvers
def
preloads
{
assignees:
[
:assignees
],
notes:
[
:ordered_notes
,
{
ordered_notes:
[
:system_note_metadata
,
:project
,
:noteable
]
}]
notes:
[
:ordered_notes
,
{
ordered_notes:
[
:system_note_metadata
,
:project
,
:noteable
]
}],
issue:
[
:issue
]
}
end
end
...
...
app/graphql/types/alert_management/alert_type.rb
View file @
b0574055
...
...
@@ -20,8 +20,14 @@ module Types
field
:issue_iid
,
GraphQL
::
ID_TYPE
,
null:
true
,
deprecated:
{
reason:
'Use issue field'
,
milestone:
'13.10'
},
description:
'Internal ID of the GitLab issue attached to the alert.'
field
:issue
,
Types
::
IssueType
,
null:
true
,
description:
'Issue attached to the alert.'
field
:title
,
GraphQL
::
STRING_TYPE
,
null:
true
,
...
...
changelogs/unreleased/228733-alert-issue-status.yml
0 → 100644
View file @
b0574055
---
title
:
'
Incident
management:
add
issue
state
to
alerts
table'
merge_request
:
55185
author
:
type
:
added
doc/api/graphql/reference/index.md
View file @
b0574055
...
...
@@ -427,7 +427,8 @@ Describes an alert from the project's Alert Management.
|
`eventCount`
| Int | Number of events of this alert. |
|
`hosts`
| String! => Array | List of hosts the alert came from. |
|
`iid`
| ID! | Internal ID of the alert. |
|
`issueIid`
| ID | Internal ID of the GitLab issue attached to the alert. |
|
`issue`
| Issue | Issue attached to the alert. |
|
`issueIid`
**{warning-solid}**
| ID |
**Deprecated:**
Use issue field. Deprecated in 13.10. |
|
`metricsDashboardUrl`
| String | URL for metrics embed for the alert. |
|
`monitoringTool`
| String | Monitoring tool the alert came from. |
|
`notes`
| NoteConnection! | All notes on this noteable. |
...
...
locale/gitlab.pot
View file @
b0574055
...
...
@@ -35195,6 +35195,9 @@ msgstr ""
msgid "ciReport|is loading, errors when loading results"
msgstr ""
msgid "closed"
msgstr ""
msgid "closed issue"
msgstr ""
...
...
spec/frontend/alert_management/components/alert_management_table_spec.js
View file @
b0574055
...
...
@@ -2,6 +2,8 @@ import { GlTable, GlAlert, GlLoadingIcon, GlDropdown, GlIcon, GlAvatar } from '@
import
{
mount
}
from
'
@vue/test-utils
'
;
import
axios
from
'
axios
'
;
import
MockAdapter
from
'
axios-mock-adapter
'
;
import
{
createMockDirective
,
getBinding
}
from
'
helpers/vue_mock_directive
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
mockAlerts
from
'
jest/vue_shared/alert_details/mocks/alerts.json
'
;
import
AlertManagementTable
from
'
~/alert_management/components/alert_management_table.vue
'
;
import
{
visitUrl
}
from
'
~/lib/utils/url_utility
'
;
...
...
@@ -18,19 +20,18 @@ describe('AlertManagementTable', () => {
let
wrapper
;
let
mock
;
const
findAlertsTable
=
()
=>
wrapper
.
find
(
GlTable
);
const
findAlertsTable
=
()
=>
wrapper
.
find
Component
(
GlTable
);
const
findAlerts
=
()
=>
wrapper
.
findAll
(
'
table tbody tr
'
);
const
findAlert
=
()
=>
wrapper
.
find
(
GlAlert
);
const
findLoader
=
()
=>
wrapper
.
find
(
GlLoadingIcon
);
const
findStatusDropdown
=
()
=>
wrapper
.
find
(
GlDropdown
);
const
findDateFields
=
()
=>
wrapper
.
findAll
(
TimeAgo
);
const
findSearch
=
()
=>
wrapper
.
find
(
FilteredSearchBar
);
const
findSeverityColumnHeader
=
()
=>
wrapper
.
find
(
'
[data-testid="alert-management-severity-sort"]
'
);
const
findFirstIDField
=
()
=>
wrapper
.
findAll
(
'
[data-testid="idField"]
'
).
at
(
0
);
const
findAssignees
=
()
=>
wrapper
.
findAll
(
'
[data-testid="assigneesField"]
'
);
const
findSeverityFields
=
()
=>
wrapper
.
findAll
(
'
[data-testid="severityField"]
'
);
const
findIssueFields
=
()
=>
wrapper
.
findAll
(
'
[data-testid="issueField"]
'
);
const
findAlert
=
()
=>
wrapper
.
findComponent
(
GlAlert
);
const
findLoader
=
()
=>
wrapper
.
findComponent
(
GlLoadingIcon
);
const
findStatusDropdown
=
()
=>
wrapper
.
findComponent
(
GlDropdown
);
const
findDateFields
=
()
=>
wrapper
.
findAllComponents
(
TimeAgo
);
const
findSearch
=
()
=>
wrapper
.
findComponent
(
FilteredSearchBar
);
const
findSeverityColumnHeader
=
()
=>
wrapper
.
findByTestId
(
'
alert-management-severity-sort
'
);
const
findFirstIDField
=
()
=>
wrapper
.
findAllByTestId
(
'
idField
'
).
at
(
0
);
const
findAssignees
=
()
=>
wrapper
.
findAllByTestId
(
'
assigneesField
'
);
const
findSeverityFields
=
()
=>
wrapper
.
findAllByTestId
(
'
severityField
'
);
const
findIssueFields
=
()
=>
wrapper
.
findAllByTestId
(
'
issueField
'
);
const
alertsCount
=
{
open
:
24
,
triggered
:
20
,
...
...
@@ -40,29 +41,34 @@ describe('AlertManagementTable', () => {
};
function
mountComponent
({
provide
=
{},
data
=
{},
loading
=
false
,
stubs
=
{}
}
=
{})
{
wrapper
=
mount
(
AlertManagementTable
,
{
provide
:
{
...
defaultProvideValues
,
alertManagementEnabled
:
true
,
userCanEnableAlertManagement
:
true
,
...
provide
,
},
data
()
{
return
data
;
},
mocks
:
{
$apollo
:
{
mutate
:
jest
.
fn
(),
query
:
jest
.
fn
(),
queries
:
{
alerts
:
{
loading
,
wrapper
=
extendedWrapper
(
mount
(
AlertManagementTable
,
{
provide
:
{
...
defaultProvideValues
,
alertManagementEnabled
:
true
,
userCanEnableAlertManagement
:
true
,
...
provide
,
},
data
()
{
return
data
;
},
mocks
:
{
$apollo
:
{
mutate
:
jest
.
fn
(),
query
:
jest
.
fn
(),
queries
:
{
alerts
:
{
loading
,
},
},
},
},
},
stubs
,
});
stubs
,
directives
:
{
GlTooltip
:
createMockDirective
(),
},
}),
);
}
beforeEach
(()
=>
{
...
...
@@ -72,7 +78,6 @@ describe('AlertManagementTable', () => {
afterEach
(()
=>
{
if
(
wrapper
)
{
wrapper
.
destroy
();
wrapper
=
null
;
}
mock
.
restore
();
});
...
...
@@ -241,9 +246,14 @@ describe('AlertManagementTable', () => {
expect
(
findIssueFields
().
at
(
0
).
text
()).
toBe
(
'
None
'
);
});
it
(
'
renders a link when one exists
'
,
()
=>
{
expect
(
findIssueFields
().
at
(
1
).
text
()).
toBe
(
'
#1
'
);
expect
(
findIssueFields
().
at
(
1
).
attributes
(
'
href
'
)).
toBe
(
'
/gitlab-org/gitlab/-/issues/1
'
);
it
(
'
renders a link when one exists with the issue state and title tooltip
'
,
()
=>
{
const
issueField
=
findIssueFields
().
at
(
1
);
const
tooltip
=
getBinding
(
issueField
.
element
,
'
gl-tooltip
'
);
expect
(
issueField
.
text
()).
toBe
(
`#1 (closed)`
);
expect
(
issueField
.
attributes
(
'
href
'
)).
toBe
(
'
/gitlab-org/gitlab/-/issues/1
'
);
expect
(
issueField
.
attributes
(
'
title
'
)).
toBe
(
'
My test issue
'
);
expect
(
tooltip
).
not
.
toBe
(
undefined
);
});
});
...
...
spec/frontend/vue_shared/alert_details/alert_details_spec.js
View file @
b0574055
...
...
@@ -89,7 +89,7 @@ describe('AlertDetails', () => {
const
findIncidentCreationAlert
=
()
=>
wrapper
.
findByTestId
(
'
incidentCreationError
'
);
const
findEnvironmentName
=
()
=>
wrapper
.
findByTestId
(
'
environmentName
'
);
const
findEnvironmentPath
=
()
=>
wrapper
.
findByTestId
(
'
environmentPath
'
);
const
findDetailsTable
=
()
=>
wrapper
.
find
(
AlertDetailsTable
);
const
findDetailsTable
=
()
=>
wrapper
.
find
Component
(
AlertDetailsTable
);
const
findMetricsTab
=
()
=>
wrapper
.
findByTestId
(
'
metrics
'
);
describe
(
'
Alert details
'
,
()
=>
{
...
...
@@ -192,23 +192,21 @@ describe('AlertDetails', () => {
describe
(
'
Create incident from alert
'
,
()
=>
{
it
(
'
should display "View incident" button that links the incident page when incident exists
'
,
()
=>
{
const
i
ssueI
id
=
'
3
'
;
const
iid
=
'
3
'
;
mountComponent
({
data
:
{
alert
:
{
...
mockAlert
,
issue
Iid
},
sidebarStatus
:
false
},
data
:
{
alert
:
{
...
mockAlert
,
issue
:
{
iid
}
},
sidebarStatus
:
false
},
});
expect
(
findViewIncidentBtn
().
exists
()).
toBe
(
true
);
expect
(
findViewIncidentBtn
().
attributes
(
'
href
'
)).
toBe
(
joinPaths
(
projectIssuesPath
,
issueIid
),
);
expect
(
findViewIncidentBtn
().
attributes
(
'
href
'
)).
toBe
(
joinPaths
(
projectIssuesPath
,
iid
));
expect
(
findCreateIncidentBtn
().
exists
()).
toBe
(
false
);
});
it
(
'
should display "Create incident" button when incident doesn
\'
t exist yet
'
,
()
=>
{
const
issue
Iid
=
null
;
const
issue
=
null
;
mountComponent
({
mountMethod
:
mount
,
data
:
{
alert
:
{
...
mockAlert
,
issue
Iid
}
},
data
:
{
alert
:
{
...
mockAlert
,
issue
}
},
});
return
wrapper
.
vm
.
$nextTick
().
then
(()
=>
{
...
...
spec/frontend/vue_shared/alert_details/mocks/alerts.json
View file @
b0574055
...
...
@@ -21,7 +21,7 @@
"endedAt"
:
"2020-04-17T23:18:14.996Z"
,
"status"
:
"ACKNOWLEDGED"
,
"assignees"
:
{
"nodes"
:
[{
"username"
:
"root"
,
"avatarUrl"
:
"/url"
,
"name"
:
"root"
}]
},
"issue
Iid"
:
"1"
,
"issue
"
:
{
"state"
:
"closed"
,
"iid"
:
"1"
,
"title"
:
"My test issue"
}
,
"notes"
:
{
"nodes"
:
[
{
...
...
spec/graphql/types/alert_management/alert_type_spec.rb
View file @
b0574055
...
...
@@ -10,7 +10,8 @@ RSpec.describe GitlabSchema.types['AlertManagementAlert'] do
it
'exposes the expected fields'
do
expected_fields
=
%i[
iid
issue_iid
issueIid
issue
title
description
severity
...
...
spec/requests/api/graphql/mutations/alert_management/alerts/create_alert_issue_spec.rb
View file @
b0574055
...
...
@@ -20,7 +20,9 @@ RSpec.describe 'Create an alert issue from an alert' do
errors
alert {
iid
issueIid
issue {
iid
}
}
issue {
iid
...
...
@@ -46,7 +48,7 @@ RSpec.describe 'Create an alert issue from an alert' do
expect
(
mutation_response
.
slice
(
'alert'
,
'issue'
)).
to
eq
(
'alert'
=>
{
'iid'
=>
alert
.
iid
.
to_s
,
'issue
Iid'
=>
new_issue
.
iid
.
to_s
'issue
'
=>
{
'iid'
=>
new_issue
.
iid
.
to_s
}
},
'issue'
=>
{
'iid'
=>
new_issue
.
iid
.
to_s
,
...
...
spec/requests/api/graphql/project/alert_management/alert/issue_spec.rb
0 → 100644
View file @
b0574055
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
'getting Alert Management Alert Issue'
do
include
GraphqlHelpers
let_it_be
(
:project
)
{
create
(
:project
)
}
let_it_be
(
:current_user
)
{
create
(
:user
)
}
let
(
:payload
)
{
{}
}
let
(
:query
)
{
'avg(metric) > 1.0'
}
let
(
:fields
)
do
<<~
QUERY
nodes {
iid
issue {
iid
state
}
}
QUERY
end
let
(
:graphql_query
)
do
graphql_query_for
(
'project'
,
{
'fullPath'
=>
project
.
full_path
},
query_graphql_field
(
'alertManagementAlerts'
,
{},
fields
)
)
end
let
(
:alerts
)
{
graphql_data
.
dig
(
'project'
,
'alertManagementAlerts'
,
'nodes'
)
}
let
(
:first_alert
)
{
alerts
.
first
}
before
do
project
.
add_developer
(
current_user
)
end
context
'with gitlab alert'
do
before
do
create
(
:alert_management_alert
,
:with_issue
,
project:
project
,
payload:
payload
)
end
it
'includes the correct alert issue payload data'
do
post_graphql
(
graphql_query
,
current_user:
current_user
)
expect
(
first_alert
).
to
include
(
'issue'
=>
{
"iid"
=>
"1"
,
"state"
=>
"opened"
})
end
end
describe
'performance'
do
let
(
:first_n
)
{
var
(
'Int'
)
}
let
(
:params
)
{
{
first:
first_n
}
}
let
(
:limited_query
)
{
with_signature
([
first_n
],
query
)
}
context
'with gitlab alert'
do
before
do
create
(
:alert_management_alert
,
:with_issue
,
project:
project
,
payload:
payload
)
end
it
'avoids N+1 queries'
do
base_count
=
ActiveRecord
::
QueryRecorder
.
new
do
post_graphql
(
limited_query
,
current_user:
current_user
,
variables:
first_n
.
with
(
1
))
end
expect
{
post_graphql
(
limited_query
,
current_user:
current_user
)
}.
not_to
exceed_query_limit
(
base_count
)
end
end
end
end
spec/requests/api/graphql/project/alert_management/alerts_spec.rb
View file @
b0574055
...
...
@@ -7,7 +7,7 @@ RSpec.describe 'getting Alert Management Alerts' do
let_it_be
(
:payload
)
{
{
'custom'
=>
{
'alert'
=>
'payload'
},
'runbook'
=>
'runbook'
}
}
let_it_be
(
:project
)
{
create
(
:project
,
:repository
)
}
let_it_be
(
:current_user
)
{
create
(
:user
)
}
let_it_be
(
:resolved_alert
)
{
create
(
:alert_management_alert
,
:all_fields
,
:resolved
,
project:
project
,
issue:
nil
,
severity: :low
).
present
}
let_it_be
(
:resolved_alert
)
{
create
(
:alert_management_alert
,
:all_fields
,
:resolved
,
project:
project
,
severity: :low
).
present
}
let_it_be
(
:triggered_alert
)
{
create
(
:alert_management_alert
,
:all_fields
,
project:
project
,
severity: :critical
,
payload:
payload
).
present
}
let_it_be
(
:other_project_alert
)
{
create
(
:alert_management_alert
,
:all_fields
).
present
}
...
...
@@ -60,7 +60,6 @@ RSpec.describe 'getting Alert Management Alerts' do
it
'returns the correct properties of the alerts'
do
expect
(
first_alert
).
to
include
(
'iid'
=>
triggered_alert
.
iid
.
to_s
,
'issueIid'
=>
triggered_alert
.
issue_iid
.
to_s
,
'title'
=>
triggered_alert
.
title
,
'description'
=>
triggered_alert
.
description
,
'severity'
=>
triggered_alert
.
severity
.
upcase
,
...
...
@@ -82,7 +81,6 @@ RSpec.describe 'getting Alert Management Alerts' do
expect
(
second_alert
).
to
include
(
'iid'
=>
resolved_alert
.
iid
.
to_s
,
'issueIid'
=>
resolved_alert
.
issue_iid
.
to_s
,
'status'
=>
'RESOLVED'
,
'endedAt'
=>
resolved_alert
.
ended_at
.
strftime
(
'%Y-%m-%dT%H:%M:%SZ'
)
)
...
...
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