2020-04-21 15:21:10 +00:00
< script >
2020-05-04 06:10:10 +00:00
import {
GlEmptyState ,
GlDeprecatedButton ,
GlLoadingIcon ,
GlTable ,
GlAlert ,
2020-05-13 18:08:47 +00:00
GlIcon ,
GlDropdown ,
GlDropdownItem ,
2020-06-18 00:08:35 +00:00
GlLink ,
2020-05-07 15:09:29 +00:00
GlTabs ,
GlTab ,
2020-05-29 21:08:35 +00:00
GlBadge ,
2020-06-01 15:08:16 +00:00
GlPagination ,
2020-06-18 00:08:35 +00:00
GlSprintf ,
2020-05-04 06:10:10 +00:00
} from '@gitlab/ui' ;
2020-05-13 18:08:47 +00:00
import createFlash from '~/flash' ;
2020-04-24 18:09:46 +00:00
import { s _ _ } from '~/locale' ;
2020-05-18 21:08:42 +00:00
import { joinPaths , visitUrl } from '~/lib/utils/url_utility' ;
2020-05-21 15:08:18 +00:00
import { fetchPolicies } from '~/lib/graphql' ;
2020-04-30 12:09:45 +00:00
import TimeAgo from '~/vue_shared/components/time_ago_tooltip.vue' ;
2020-05-21 15:08:18 +00:00
import getAlerts from '../graphql/queries/get_alerts.query.graphql' ;
import getAlertsCountByStatus from '../graphql/queries/get_count_by_status.query.graphql' ;
2020-05-26 15:08:17 +00:00
import {
ALERTS _STATUS _TABS ,
ALERTS _SEVERITY _LABELS ,
2020-06-01 15:08:16 +00:00
DEFAULT _PAGE _SIZE ,
2020-05-26 15:08:17 +00:00
trackAlertListViewsOptions ,
trackAlertStatusUpdateOptions ,
} from '../constants' ;
2020-05-13 18:08:47 +00:00
import updateAlertStatus from '../graphql/mutations/update_alert_status.graphql' ;
2020-05-28 00:08:37 +00:00
import { convertToSnakeCase } from '~/lib/utils/text_utility' ;
2020-05-26 15:08:17 +00:00
import Tracking from '~/tracking' ;
2020-04-24 09:09:44 +00:00
2020-06-15 18:08:43 +00:00
const tdClass = 'table-col gl-display-flex d-md-table-cell gl-align-items-center' ;
const thClass = 'gl-hover-bg-blue-50' ;
2020-05-15 18:07:52 +00:00
const bodyTrClass =
2020-05-26 15:08:17 +00:00
'gl-border-1 gl-border-t-solid gl-border-gray-100 gl-hover-bg-blue-50 gl-hover-cursor-pointer gl-hover-border-b-solid gl-hover-border-blue-200' ;
2020-05-07 06:09:38 +00:00
2020-06-01 15:08:16 +00:00
const initialPaginationState = {
currentPage : 1 ,
prevPageCursor : '' ,
nextPageCursor : '' ,
firstPageSize : DEFAULT _PAGE _SIZE ,
lastPageSize : null ,
} ;
2020-04-21 15:21:10 +00:00
export default {
2020-04-24 18:09:46 +00:00
i18n : {
noAlertsMsg : s _ _ (
2020-06-18 00:08:35 +00:00
'AlertManagement|No alerts available to display. See %{linkStart}enabling alert management%{linkEnd} for more information on adding alerts to the list.' ,
2020-04-24 18:09:46 +00:00
) ,
errorMsg : s _ _ (
"AlertManagement|There was an error displaying the alerts. Confirm your endpoint's configuration details to ensure alerts appear." ,
) ,
} ,
2020-04-24 09:09:44 +00:00
fields : [
2020-05-14 00:07:47 +00:00
{
key : 'severity' ,
label : s _ _ ( 'AlertManagement|Severity' ) ,
tdClass : ` ${ tdClass } rounded-top text-capitalize ` ,
2020-06-15 18:08:43 +00:00
thClass ,
2020-05-25 15:07:58 +00:00
sortable : true ,
2020-05-14 00:07:47 +00:00
} ,
2020-04-24 09:09:44 +00:00
{
2020-05-27 00:08:11 +00:00
key : 'startedAt' ,
2020-04-30 12:09:45 +00:00
label : s _ _ ( 'AlertManagement|Start time' ) ,
2020-06-15 18:08:43 +00:00
thClass : ` ${ thClass } js-started-at ` ,
2020-05-07 06:09:38 +00:00
tdClass ,
2020-05-25 15:07:58 +00:00
sortable : true ,
2020-04-24 09:09:44 +00:00
} ,
{
2020-05-27 00:08:11 +00:00
key : 'endedAt' ,
2020-04-30 12:09:45 +00:00
label : s _ _ ( 'AlertManagement|End time' ) ,
2020-06-15 18:08:43 +00:00
thClass ,
2020-05-07 06:09:38 +00:00
tdClass ,
2020-05-25 15:07:58 +00:00
sortable : true ,
2020-04-24 09:09:44 +00:00
} ,
{
2020-04-30 12:09:45 +00:00
key : 'title' ,
2020-04-24 18:09:46 +00:00
label : s _ _ ( 'AlertManagement|Alert' ) ,
2020-06-15 18:08:43 +00:00
thClass : ` ${ thClass } w-30p gl-pointer-events-none ` ,
2020-05-07 06:09:38 +00:00
tdClass ,
2020-05-25 15:07:58 +00:00
sortable : false ,
2020-04-24 09:09:44 +00:00
} ,
{
2020-05-27 00:08:11 +00:00
key : 'eventCount' ,
2020-04-24 18:09:46 +00:00
label : s _ _ ( 'AlertManagement|Events' ) ,
2020-06-15 18:08:43 +00:00
thClass : ` ${ thClass } text-right gl-pr-9 w-3rem ` ,
2020-05-20 12:07:52 +00:00
tdClass : ` ${ tdClass } text-md-right ` ,
2020-05-25 15:07:58 +00:00
sortable : true ,
2020-04-24 09:09:44 +00:00
} ,
2020-05-29 21:08:35 +00:00
{
key : 'assignees' ,
label : s _ _ ( 'AlertManagement|Assignees' ) ,
tdClass ,
} ,
2020-04-24 09:09:44 +00:00
{
key : 'status' ,
2020-06-15 18:08:43 +00:00
thClass : ` ${ thClass } w-15p ` ,
2020-04-24 18:09:46 +00:00
label : s _ _ ( 'AlertManagement|Status' ) ,
2020-05-15 18:07:52 +00:00
tdClass : ` ${ tdClass } rounded-bottom ` ,
2020-05-25 15:07:58 +00:00
sortable : true ,
2020-04-24 09:09:44 +00:00
} ,
] ,
2020-05-04 06:10:10 +00:00
statuses : {
2020-05-28 00:08:37 +00:00
TRIGGERED : s _ _ ( 'AlertManagement|Triggered' ) ,
ACKNOWLEDGED : s _ _ ( 'AlertManagement|Acknowledged' ) ,
RESOLVED : s _ _ ( 'AlertManagement|Resolved' ) ,
2020-05-04 06:10:10 +00:00
} ,
2020-05-14 00:07:47 +00:00
severityLabels : ALERTS _SEVERITY _LABELS ,
2020-05-07 15:09:29 +00:00
statusTabs : ALERTS _STATUS _TABS ,
2020-04-21 15:21:10 +00:00
components : {
GlEmptyState ,
GlLoadingIcon ,
2020-04-24 09:09:44 +00:00
GlTable ,
GlAlert ,
2020-04-27 18:09:41 +00:00
GlDeprecatedButton ,
2020-04-30 12:09:45 +00:00
TimeAgo ,
2020-05-13 18:08:47 +00:00
GlDropdown ,
GlDropdownItem ,
GlIcon ,
2020-06-18 00:08:35 +00:00
GlLink ,
2020-05-07 15:09:29 +00:00
GlTabs ,
GlTab ,
2020-05-21 15:08:18 +00:00
GlBadge ,
2020-06-01 15:08:16 +00:00
GlPagination ,
2020-06-18 00:08:35 +00:00
GlSprintf ,
2020-04-21 15:21:10 +00:00
} ,
props : {
2020-04-30 12:09:45 +00:00
projectPath : {
2020-04-21 15:21:10 +00:00
type : String ,
required : true ,
} ,
2020-04-24 09:09:44 +00:00
alertManagementEnabled : {
type : Boolean ,
2020-04-27 18:09:41 +00:00
required : true ,
2020-04-24 09:09:44 +00:00
} ,
2020-04-21 15:21:10 +00:00
enableAlertManagementPath : {
type : String ,
required : true ,
} ,
2020-06-18 00:08:35 +00:00
populatingAlertsHelpUrl : {
type : String ,
required : true ,
} ,
2020-04-27 18:09:41 +00:00
userCanEnableAlertManagement : {
type : Boolean ,
required : true ,
} ,
2020-04-21 15:21:10 +00:00
emptyAlertSvgPath : {
type : String ,
required : true ,
} ,
} ,
2020-04-24 18:09:46 +00:00
apollo : {
alerts : {
2020-05-21 15:08:18 +00:00
fetchPolicy : fetchPolicies . CACHE _AND _NETWORK ,
2020-04-24 18:09:46 +00:00
query : getAlerts ,
variables ( ) {
return {
2020-04-30 12:09:45 +00:00
projectPath : this . projectPath ,
2020-05-15 09:07:59 +00:00
statuses : this . statusFilter ,
2020-05-25 15:07:58 +00:00
sort : this . sort ,
2020-06-01 15:08:16 +00:00
firstPageSize : this . pagination . firstPageSize ,
lastPageSize : this . pagination . lastPageSize ,
prevPageCursor : this . pagination . prevPageCursor ,
nextPageCursor : this . pagination . nextPageCursor ,
2020-04-24 18:09:46 +00:00
} ;
} ,
2020-04-30 12:09:45 +00:00
update ( data ) {
2020-06-01 15:08:16 +00:00
const { alertManagementAlerts : { nodes : list = [ ] , pageInfo = { } } = { } } =
data . project || { } ;
return {
list ,
pageInfo ,
} ;
2020-04-30 12:09:45 +00:00
} ,
2020-04-24 18:09:46 +00:00
error ( ) {
this . errored = true ;
} ,
} ,
2020-05-21 15:08:18 +00:00
alertsCount : {
query : getAlertsCountByStatus ,
variables ( ) {
return {
projectPath : this . projectPath ,
} ;
} ,
update ( data ) {
return data . project ? . alertManagementAlertStatusCounts ;
} ,
} ,
2020-04-24 18:09:46 +00:00
} ,
2020-04-21 15:21:10 +00:00
data ( ) {
return {
2020-04-24 18:09:46 +00:00
errored : false ,
2020-04-24 09:09:44 +00:00
isAlertDismissed : false ,
2020-04-24 18:09:46 +00:00
isErrorAlertDismissed : false ,
2020-06-06 03:08:19 +00:00
sort : 'STARTED_AT_DESC' ,
2020-06-01 15:08:16 +00:00
statusFilter : [ ] ,
filteredByStatus : '' ,
pagination : initialPaginationState ,
2020-06-06 03:08:19 +00:00
sortBy : 'startedAt' ,
sortDesc : true ,
2020-06-15 18:08:43 +00:00
sortDirection : 'desc' ,
2020-04-21 15:21:10 +00:00
} ;
} ,
2020-04-24 09:09:44 +00:00
computed : {
showNoAlertsMsg ( ) {
2020-05-21 15:08:18 +00:00
return (
! this . errored && ! this . loading && this . alertsCount ? . all === 0 && ! this . isAlertDismissed
) ;
2020-04-24 18:09:46 +00:00
} ,
showErrorMsg ( ) {
return this . errored && ! this . isErrorAlertDismissed ;
} ,
loading ( ) {
return this . $apollo . queries . alerts . loading ;
2020-04-24 09:09:44 +00:00
} ,
2020-05-20 15:08:20 +00:00
hasAlerts ( ) {
2020-06-01 15:08:16 +00:00
return this . alerts ? . list ? . length ;
2020-05-20 15:08:20 +00:00
} ,
tbodyTrClass ( ) {
return ! this . loading && this . hasAlerts ? bodyTrClass : '' ;
} ,
2020-06-01 15:08:16 +00:00
showPaginationControls ( ) {
return Boolean ( this . prevPage || this . nextPage ) ;
} ,
alertsForCurrentTab ( ) {
return this . alertsCount ? this . alertsCount [ this . filteredByStatus . toLowerCase ( ) ] : 0 ;
} ,
prevPage ( ) {
return Math . max ( this . pagination . currentPage - 1 , 0 ) ;
} ,
nextPage ( ) {
const nextPage = this . pagination . currentPage + 1 ;
return nextPage > Math . ceil ( this . alertsForCurrentTab / DEFAULT _PAGE _SIZE ) ? null : nextPage ;
} ,
2020-04-24 09:09:44 +00:00
} ,
2020-05-25 15:07:58 +00:00
mounted ( ) {
2020-05-26 15:08:17 +00:00
this . trackPageViews ( ) ;
2020-05-25 15:07:58 +00:00
} ,
2020-05-07 15:09:29 +00:00
methods : {
2020-05-15 12:08:28 +00:00
filterAlertsByStatus ( tabIndex ) {
2020-06-01 15:08:16 +00:00
this . resetPagination ( ) ;
const { filters , status } = this . $options . statusTabs [ tabIndex ] ;
this . statusFilter = filters ;
this . filteredByStatus = status ;
2020-05-07 15:09:29 +00:00
} ,
2020-05-25 15:07:58 +00:00
fetchSortedData ( { sortBy , sortDesc } ) {
2020-06-15 18:08:43 +00:00
const sortingDirection = sortDesc ? 'DESC' : 'ASC' ;
const sortingColumn = convertToSnakeCase ( sortBy ) . toUpperCase ( ) ;
2020-05-25 15:07:58 +00:00
2020-06-01 15:08:16 +00:00
this . resetPagination ( ) ;
2020-06-15 18:08:43 +00:00
this . sort = ` ${ sortingColumn } _ ${ sortingDirection } ` ;
2020-05-25 15:07:58 +00:00
} ,
2020-05-13 18:08:47 +00:00
updateAlertStatus ( status , iid ) {
this . $apollo
. mutate ( {
mutation : updateAlertStatus ,
variables : {
iid ,
status : status . toUpperCase ( ) ,
projectPath : this . projectPath ,
} ,
} )
2020-05-18 21:08:42 +00:00
. then ( ( ) => {
2020-05-26 15:08:17 +00:00
this . trackStatusUpdate ( status ) ;
2020-05-18 21:08:42 +00:00
this . $apollo . queries . alerts . refetch ( ) ;
2020-05-21 15:08:18 +00:00
this . $apollo . queries . alertsCount . refetch ( ) ;
2020-06-01 15:08:16 +00:00
this . resetPagination ( ) ;
2020-05-18 21:08:42 +00:00
} )
2020-05-13 18:08:47 +00:00
. catch ( ( ) => {
createFlash (
s _ _ (
'AlertManagement|There was an error while updating the status of the alert. Please try again.' ,
) ,
) ;
} ) ;
} ,
2020-05-18 21:08:42 +00:00
navigateToAlertDetails ( { iid } ) {
return visitUrl ( joinPaths ( window . location . pathname , iid , 'details' ) ) ;
2020-05-15 18:07:52 +00:00
} ,
2020-05-26 15:08:17 +00:00
trackPageViews ( ) {
const { category , action } = trackAlertListViewsOptions ;
Tracking . event ( category , action ) ;
} ,
trackStatusUpdate ( status ) {
const { category , action , label } = trackAlertStatusUpdateOptions ;
Tracking . event ( category , action , { label , property : status } ) ;
} ,
2020-05-29 21:08:35 +00:00
getAssignees ( assignees ) {
// TODO: Update to show list of assignee(s) after https://gitlab.com/gitlab-org/gitlab/-/issues/218405
2020-06-11 00:08:35 +00:00
return assignees . nodes ? . length > 0
? assignees . nodes [ 0 ] ? . username
: s _ _ ( 'AlertManagement|Unassigned' ) ;
2020-05-29 21:08:35 +00:00
} ,
2020-06-01 15:08:16 +00:00
handlePageChange ( page ) {
const { startCursor , endCursor } = this . alerts . pageInfo ;
if ( page > this . pagination . currentPage ) {
this . pagination = {
... initialPaginationState ,
nextPageCursor : endCursor ,
currentPage : page ,
} ;
} else {
this . pagination = {
lastPageSize : DEFAULT _PAGE _SIZE ,
firstPageSize : null ,
prevPageCursor : startCursor ,
nextPageCursor : '' ,
currentPage : page ,
} ;
}
} ,
resetPagination ( ) {
this . pagination = initialPaginationState ;
} ,
2020-05-07 15:09:29 +00:00
} ,
2020-04-21 15:21:10 +00:00
} ;
< / script >
< template >
< div >
2020-04-24 09:09:44 +00:00
< div v-if ="alertManagementEnabled" class="alert-management-list" >
< gl -alert v-if ="showNoAlertsMsg" @dismiss="isAlertDismissed = true" >
2020-06-18 00:08:35 +00:00
< gl -sprintf :message ="$options.i18n.noAlertsMsg" >
< template # link = "{ content }" >
< gl -link
class = "gl-display-inline-block"
: href = "populatingAlertsHelpUrl"
target = "_blank"
>
{ { content } }
< / g l - l i n k >
< / template >
< / g l - s p r i n t f >
2020-04-24 18:09:46 +00:00
< / g l - a l e r t >
< gl -alert v-if ="showErrorMsg" variant="danger" @dismiss="isErrorAlertDismissed = true" >
{ { $options . i18n . errorMsg } }
2020-04-24 09:09:44 +00:00
< / g l - a l e r t >
2020-05-21 15:08:18 +00:00
< gl -tabs @input ="filterAlertsByStatus" >
2020-05-07 15:09:29 +00:00
< gl -tab v-for ="tab in $options.statusTabs" :key="tab.status" >
< template slot = "title" >
< span > { { tab . title } } < / span >
2020-05-21 15:08:18 +00:00
< gl -badge v-if ="alertsCount" pill size="sm" class="gl-tab-counter-badge" >
{ { alertsCount [ tab . status . toLowerCase ( ) ] } }
< / g l - b a d g e >
2020-05-07 15:09:29 +00:00
< / template >
< / g l - t a b >
< / g l - t a b s >
2020-05-07 06:09:38 +00:00
< h4 class = "d-block d-md-none my-3" >
{ { s _ _ ( 'AlertManagement|Alerts' ) } }
< / h4 >
2020-04-24 09:09:44 +00:00
< gl -table
2020-05-07 06:09:38 +00:00
class = "alert-management-table mt-3"
2020-06-01 15:08:16 +00:00
: items = "alerts ? alerts.list : []"
2020-04-24 09:09:44 +00:00
: fields = "$options.fields"
: show - empty = "true"
2020-04-24 18:09:46 +00:00
: busy = "loading"
2020-04-30 12:09:45 +00:00
stacked = "md"
2020-05-20 15:08:20 +00:00
: tbody - tr - class = "tbodyTrClass"
2020-05-25 15:07:58 +00:00
: no - local - sorting = "true"
2020-06-15 18:08:43 +00:00
: sort - direction = "sortDirection"
2020-06-06 03:08:19 +00:00
: sort - desc . sync = "sortDesc"
: sort - by . sync = "sortBy"
2020-05-25 15:07:58 +00:00
sort - icon - left
2020-05-18 21:08:42 +00:00
@ row - clicked = "navigateToAlertDetails"
2020-05-25 15:07:58 +00:00
@ sort - changed = "fetchSortedData"
2020-04-24 09:09:44 +00:00
>
2020-05-14 00:07:47 +00:00
< template # cell ( severity ) = " { item } " >
< div
class = "d-inline-flex align-items-center justify-content-between"
data - testid = "severityField"
>
< gl -icon
class = "mr-2"
: size = "12"
: name = "`severity-${item.severity.toLowerCase()}`"
: class = "`icon-${item.severity.toLowerCase()}`"
/ >
{ { $options . severityLabels [ item . severity ] } }
< / div >
< / template >
2020-05-27 00:08:11 +00:00
< template # cell ( startedAt ) = " { item } " >
2020-05-11 09:09:45 +00:00
< time -ago v -if = " item.startedAt " :time ="item.startedAt" / >
2020-04-30 12:09:45 +00:00
< / template >
2020-05-27 00:08:11 +00:00
< template # cell ( endedAt ) = " { item } " >
2020-05-11 09:09:45 +00:00
< time -ago v -if = " item.endedAt " :time ="item.endedAt" / >
2020-04-30 12:09:45 +00:00
< / template >
2020-05-27 00:08:11 +00:00
< template # cell ( eventCount ) = " { item } " >
2020-05-25 15:07:58 +00:00
{ { item . eventCount } }
< / template >
2020-04-30 12:09:45 +00:00
< template # cell ( title ) = " { item } " >
< div class = "gl-max-w-full text-truncate" > { { item . title } } < / div >
< / template >
2020-05-07 06:09:38 +00:00
2020-05-29 21:08:35 +00:00
< template # cell ( assignees ) = " { item } " >
< div class = "gl-max-w-full text-truncate" data -testid = " assigneesField " >
{ { getAssignees ( item . assignees ) } }
< / div >
< / template >
2020-05-04 06:10:10 +00:00
< template # cell ( status ) = " { item } " >
2020-05-28 00:08:37 +00:00
< gl -dropdown :text ="$options.statuses[item.status]" class = "w-100" right >
2020-05-13 18:08:47 +00:00
< gl -dropdown -item
v - for = "(label, field) in $options.statuses"
: key = "field"
@ click = "updateAlertStatus(label, item.iid)"
>
< span class = "d-flex" >
< gl -icon
class = "flex-shrink-0 append-right-4"
: class = "{ invisible: label.toUpperCase() !== item.status }"
name = "mobile-issue-close"
/ >
{ { label } }
< / span >
< / g l - d r o p d o w n - i t e m >
< / g l - d r o p d o w n >
2020-05-04 06:10:10 +00:00
< / template >
2020-04-30 12:09:45 +00:00
2020-04-24 09:09:44 +00:00
< template # empty >
2020-04-24 18:09:46 +00:00
{ { s _ _ ( 'AlertManagement|No alerts to display.' ) } }
< / template >
2020-04-30 12:09:45 +00:00
2020-04-24 18:09:46 +00:00
< template # table -busy >
< gl -loading -icon size = "lg" color = "dark" class = "mt-3" / >
2020-04-24 09:09:44 +00:00
< / template >
< / g l - t a b l e >
2020-06-01 15:08:16 +00:00
< gl -pagination
v - if = "showPaginationControls"
: value = "pagination.currentPage"
: prev - page = "prevPage"
: next - page = "nextPage"
align = "center"
class = "gl-pagination prepend-top-default"
@ input = "handlePageChange"
/ >
2020-04-21 15:21:10 +00:00
< / div >
2020-04-30 12:09:45 +00:00
< gl -empty -state
v - else
2020-05-07 12:09:46 +00:00
: title = "s__('AlertManagement|Surface alerts in GitLab')"
2020-04-30 12:09:45 +00:00
: svg - path = "emptyAlertSvgPath"
>
2020-04-27 18:09:41 +00:00
< template # description >
< div class = "d-block" >
< span > { {
s _ _ (
'AlertManagement|Display alerts from all your monitoring tools directly within GitLab. Streamline the investigation of your alerts and the escalation of alerts to incidents.' ,
)
} } < / span >
< a href = "/help/user/project/operations/alert_management.html" target = "_blank" >
{ { s _ _ ( 'AlertManagement|More information' ) } }
< / a >
< / div >
< div v-if ="userCanEnableAlertManagement" class="d-block center pt-4" >
< gl -deprecated -button
category = "primary"
variant = "success"
: href = "enableAlertManagementPath"
>
{ { s _ _ ( 'AlertManagement|Authorize external service' ) } }
< / g l - d e p r e c a t e d - b u t t o n >
< / div >
< / template >
< / g l - e m p t y - s t a t e >
2020-04-21 15:21:10 +00:00
< / div >
< / template >