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
c58680e2
Commit
c58680e2
authored
Jun 10, 2021
by
Olena Horal-Koretska
Committed by
Kushal Pandya
Jun 10, 2021
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Display Escalation Policy
parent
22fedb37
Changes
21
Show whitespace changes
Inline
Side-by-side
Showing
21 changed files
with
631 additions
and
86 deletions
+631
-86
app/assets/stylesheets/page_bundles/escalation_policies.scss
app/assets/stylesheets/page_bundles/escalation_policies.scss
+26
-0
ee/app/assets/javascripts/escalation_policies/components/add_edit_escalation_policy_form.vue
...n_policies/components/add_edit_escalation_policy_form.vue
+3
-3
ee/app/assets/javascripts/escalation_policies/components/add_edit_escalation_policy_modal.vue
..._policies/components/add_edit_escalation_policy_modal.vue
+7
-0
ee/app/assets/javascripts/escalation_policies/components/escalation_policies_wrapper.vue
...ation_policies/components/escalation_policies_wrapper.vue
+63
-3
ee/app/assets/javascripts/escalation_policies/components/escalation_policy.vue
...ipts/escalation_policies/components/escalation_policy.vue
+153
-0
ee/app/assets/javascripts/escalation_policies/constants.js
ee/app/assets/javascripts/escalation_policies/constants.js
+5
-2
ee/app/assets/javascripts/escalation_policies/graphql/cache_updates.js
.../javascripts/escalation_policies/graphql/cache_updates.js
+31
-0
ee/app/assets/javascripts/escalation_policies/graphql/fragments/escalation_policy.fragment.graphql
...cies/graphql/fragments/escalation_policy.fragment.graphql
+14
-0
ee/app/assets/javascripts/escalation_policies/graphql/mutations/create_escalation_policy.mutation.graphql
...aphql/mutations/create_escalation_policy.mutation.graphql
+3
-11
ee/app/assets/javascripts/escalation_policies/graphql/queries/get_escalation_policies.query.graphql
...ies/graphql/queries/get_escalation_policies.query.graphql
+11
-0
ee/app/assets/javascripts/escalation_policies/index.js
ee/app/assets/javascripts/escalation_policies/index.js
+1
-0
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedule.vue
...vascripts/oncall_schedules/components/oncall_schedule.vue
+1
-1
ee/spec/frontend/escalation_policies/__snapshots__/escalation_policy_spec.js.snap
...ion_policies/__snapshots__/escalation_policy_spec.js.snap
+114
-0
ee/spec/frontend/escalation_policies/add_edit_escalation_policy_form_spec.js
...calation_policies/add_edit_escalation_policy_form_spec.js
+23
-25
ee/spec/frontend/escalation_policies/add_edit_escalation_policy_modal_spec.js
...alation_policies/add_edit_escalation_policy_modal_spec.js
+12
-1
ee/spec/frontend/escalation_policies/escalation_policy_spec.js
...ec/frontend/escalation_policies/escalation_policy_spec.js
+33
-0
ee/spec/frontend/escalation_policies/escalation_policy_wrapper_spec.js
...end/escalation_policies/escalation_policy_wrapper_spec.js
+57
-15
ee/spec/frontend/escalation_policies/escalation_rule_spec.js
ee/spec/frontend/escalation_policies/escalation_rule_spec.js
+16
-19
ee/spec/frontend/escalation_policies/mocks/mockPolicies.json
ee/spec/frontend/escalation_policies/mocks/mockPolicies.json
+43
-0
ee/spec/frontend/escalation_policies/mocks/mockPolicy.json
ee/spec/frontend/escalation_policies/mocks/mockPolicy.json
+0
-6
locale/gitlab.pot
locale/gitlab.pot
+15
-0
No files found.
app/assets/stylesheets/page_bundles/escalation_policies.scss
View file @
c58680e2
@import
'mixins_and_variables_and_functions'
;
.escalation-policy-modal
{
.escalation-policy-modal
{
width
:
640px
;
width
:
640px
;
}
}
...
@@ -9,3 +11,27 @@
...
@@ -9,3 +11,27 @@
.rule-close-icon
{
.rule-close-icon
{
right
:
1rem
;
right
:
1rem
;
}
}
$stroke-size
:
1px
;
.right-arrow
{
@include
gl-relative
;
@include
gl-mx-5
;
@include
gl-display-inline-block
;
@include
gl-vertical-align-middle
;
height
:
$stroke-size
;
background-color
:
var
(
--
gray-900
,
$gray-900
);
min-width
:
$gl-spacing-scale-7
;
&
-head
{
@include
gl-absolute
;
top
:
-2
*
$stroke-size
;
left
:
calc
(
100%
-
#{
5
*
$stroke-size
}
);
@include
gl-display-inline-block
;
@include
gl-p-1
;
@include
gl-border-solid
;
border-width
:
0
$stroke-size
$stroke-size
0
;
border-color
:
var
(
--
gray-900
,
$gray-900
);
transform
:
rotate
(
-45deg
);
}
}
ee/app/assets/javascripts/escalation_policies/components/add_edit_escalation_policy_form.vue
View file @
c58680e2
...
@@ -3,7 +3,7 @@ import { GlLink, GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
...
@@ -3,7 +3,7 @@ import { GlLink, GlForm, GlFormGroup, GlFormInput } from '@gitlab/ui';
import
{
cloneDeep
}
from
'
lodash
'
;
import
{
cloneDeep
}
from
'
lodash
'
;
import
createFlash
from
'
~/flash
'
;
import
createFlash
from
'
~/flash
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
defaultEscalationRule
}
from
'
../constants
'
;
import
{
DEFAULT_ESCALATION_RULE
}
from
'
../constants
'
;
import
getOncallSchedulesQuery
from
'
../graphql/queries/get_oncall_schedules.query.graphql
'
;
import
getOncallSchedulesQuery
from
'
../graphql/queries/get_oncall_schedules.query.graphql
'
;
import
EscalationRule
from
'
./escalation_rule.vue
'
;
import
EscalationRule
from
'
./escalation_rule.vue
'
;
...
@@ -74,11 +74,11 @@ export default {
...
@@ -74,11 +74,11 @@ export default {
},
},
},
},
mounted
()
{
mounted
()
{
this
.
rules
.
push
({
...
cloneDeep
(
defaultEscalationRule
),
key
:
this
.
getUid
()
}
);
this
.
addRule
(
);
},
},
methods
:
{
methods
:
{
addRule
()
{
addRule
()
{
this
.
rules
.
push
({
...
cloneDeep
(
defaultEscalationRule
),
key
:
this
.
getUid
()
});
this
.
rules
.
push
({
...
cloneDeep
(
DEFAULT_ESCALATION_RULE
),
key
:
this
.
getUid
()
});
},
},
updateEscalationRules
(
index
,
rule
)
{
updateEscalationRules
(
index
,
rule
)
{
this
.
rules
[
index
]
=
{
...
this
.
rules
[
index
],
...
rule
};
this
.
rules
[
index
]
=
{
...
this
.
rules
[
index
],
...
rule
};
...
...
ee/app/assets/javascripts/escalation_policies/components/add_edit_escalation_policy_modal.vue
View file @
c58680e2
...
@@ -3,7 +3,9 @@ import { GlModal, GlAlert } from '@gitlab/ui';
...
@@ -3,7 +3,9 @@ import { GlModal, GlAlert } from '@gitlab/ui';
import
{
set
}
from
'
lodash
'
;
import
{
set
}
from
'
lodash
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
addEscalationPolicyModalId
}
from
'
../constants
'
;
import
{
addEscalationPolicyModalId
}
from
'
../constants
'
;
import
{
updateStoreOnEscalationPolicyCreate
}
from
'
../graphql/cache_updates
'
;
import
createEscalationPolicyMutation
from
'
../graphql/mutations/create_escalation_policy.mutation.graphql
'
;
import
createEscalationPolicyMutation
from
'
../graphql/mutations/create_escalation_policy.mutation.graphql
'
;
import
getEscalationPoliciesQuery
from
'
../graphql/queries/get_escalation_policies.query.graphql
'
;
import
{
isNameFieldValid
,
getRulesValidationState
}
from
'
../utils
'
;
import
{
isNameFieldValid
,
getRulesValidationState
}
from
'
../utils
'
;
import
AddEditEscalationPolicyForm
from
'
./add_edit_escalation_policy_form.vue
'
;
import
AddEditEscalationPolicyForm
from
'
./add_edit_escalation_policy_form.vue
'
;
...
@@ -86,6 +88,11 @@ export default {
...
@@ -86,6 +88,11 @@ export default {
...
this
.
getRequestParams
(),
...
this
.
getRequestParams
(),
},
},
},
},
update
(
store
,
{
data
})
{
updateStoreOnEscalationPolicyCreate
(
store
,
getEscalationPoliciesQuery
,
data
,
{
projectPath
,
});
},
})
})
.
then
(
.
then
(
({
({
...
...
ee/app/assets/javascripts/escalation_policies/components/escalation_policies_wrapper.vue
View file @
c58680e2
<
script
>
<
script
>
import
{
GlEmptyState
,
GlButton
,
GlModalDirective
}
from
'
@gitlab/ui
'
;
import
{
GlEmptyState
,
GlButton
,
GlModalDirective
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
*
as
Sentry
from
'
@sentry/browser
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
addEscalationPolicyModalId
}
from
'
../constants
'
;
import
{
addEscalationPolicyModalId
}
from
'
../constants
'
;
import
getEscalationPoliciesQuery
from
'
../graphql/queries/get_escalation_policies.query.graphql
'
;
import
AddEscalationPolicyModal
from
'
./add_edit_escalation_policy_modal.vue
'
;
import
AddEscalationPolicyModal
from
'
./add_edit_escalation_policy_modal.vue
'
;
import
EscalationPolicy
from
'
./escalation_policy.vue
'
;
export
const
i18n
=
{
export
const
i18n
=
{
title
:
s__
(
'
EscalationPolicies|Escalation policies
'
),
addPolicy
:
s__
(
'
EscalationPolicies|Add policy
'
),
emptyState
:
{
emptyState
:
{
title
:
s__
(
'
EscalationPolicies|Create an escalation policy in GitLab
'
),
title
:
s__
(
'
EscalationPolicies|Create an escalation policy in GitLab
'
),
description
:
s__
(
description
:
s__
(
...
@@ -20,18 +25,73 @@ export default {
...
@@ -20,18 +25,73 @@ export default {
components
:
{
components
:
{
GlEmptyState
,
GlEmptyState
,
GlButton
,
GlButton
,
GlLoadingIcon
,
AddEscalationPolicyModal
,
AddEscalationPolicyModal
,
EscalationPolicy
,
},
},
directives
:
{
directives
:
{
GlModal
:
GlModalDirective
,
GlModal
:
GlModalDirective
,
},
},
inject
:
[
'
emptyEscalationPoliciesSvgPath
'
],
inject
:
[
'
projectPath
'
,
'
emptyEscalationPoliciesSvgPath
'
],
data
()
{
return
{
escalationPolicies
:
[],
};
},
apollo
:
{
escalationPolicies
:
{
query
:
getEscalationPoliciesQuery
,
variables
()
{
return
{
projectPath
:
this
.
projectPath
,
};
},
update
({
project
})
{
return
project
?.
incidentManagementEscalationPolicies
?.
nodes
??
[];
},
error
(
error
)
{
Sentry
.
captureException
(
error
);
},
},
},
computed
:
{
isLoading
()
{
return
this
.
$apollo
.
queries
.
escalationPolicies
.
loading
;
},
hasPolicies
()
{
return
this
.
escalationPolicies
.
length
;
},
},
};
};
</
script
>
</
script
>
<
template
>
<
template
>
<div>
<div>
<gl-loading-icon
v-if=
"isLoading"
size=
"lg"
class=
"gl-mt-3"
/>
<template
v-else-if=
"hasPolicies"
>
<div
class=
"gl-display-flex gl-justify-content-space-between gl-align-items-center"
>
<h2>
{{
$options
.
i18n
.
title
}}
</h2>
<gl-button
v-gl-modal=
"$options.addEscalationPolicyModalId"
:title=
"$options.i18n.addPolicy"
category=
"secondary"
variant=
"confirm"
class=
"gl-mt-5"
>
{{
$options
.
i18n
.
addPolicy
}}
</gl-button>
</div>
<escalation-policy
v-for=
"(policy, index) in escalationPolicies"
:key=
"policy.id"
:policy=
"policy"
:index=
"index"
/>
</
template
>
<gl-empty-state
<gl-empty-state
v-else
:title=
"$options.i18n.emptyState.title"
:title=
"$options.i18n.emptyState.title"
:description=
"$options.i18n.emptyState.description"
:description=
"$options.i18n.emptyState.description"
:svg-path=
"emptyEscalationPoliciesSvgPath"
:svg-path=
"emptyEscalationPoliciesSvgPath"
...
@@ -42,6 +102,6 @@ export default {
...
@@ -42,6 +102,6 @@ export default {
</gl-button>
</gl-button>
</
template
>
</
template
>
</gl-empty-state>
</gl-empty-state>
<add-escalation-policy-modal
/>
<add-escalation-policy-modal
:modal-id=
"$options.addEscalationPolicyModalId"
/>
</div>
</div>
</template>
</template>
ee/app/assets/javascripts/escalation_policies/components/escalation_policy.vue
0 → 100644
View file @
c58680e2
<
script
>
import
{
GlModalDirective
,
GlTooltipDirective
,
GlButton
,
GlButtonGroup
,
GlCard
,
GlSprintf
,
GlIcon
,
GlCollapse
,
}
from
'
@gitlab/ui
'
;
import
{
s__
,
__
}
from
'
~/locale
'
;
import
{
ACTIONS
,
ALERT_STATUSES
,
DEFAULT_ACTION
}
from
'
../constants
'
;
export
const
i18n
=
{
editPolicy
:
s__
(
'
EscalationPolicies|Edit escalation policy
'
),
deletePolicy
:
s__
(
'
EscalationPolicies|Delete escalation policy
'
),
escalationRule
:
s__
(
'
EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} %{then} THEN %{doAction} %{schedule}
'
,
),
minutes
:
s__
(
'
EscalationPolicies|mins
'
),
};
const
isRuleValid
=
({
status
,
elapsedTimeSeconds
,
oncallSchedule
:
{
name
}
})
=>
Object
.
keys
(
ALERT_STATUSES
).
includes
(
status
)
&&
typeof
elapsedTimeSeconds
===
'
number
'
&&
typeof
name
===
'
string
'
;
export
default
{
i18n
,
ACTIONS
,
ALERT_STATUSES
,
DEFAULT_ACTION
,
components
:
{
GlButton
,
GlButtonGroup
,
GlCard
,
GlSprintf
,
GlIcon
,
GlCollapse
,
},
directives
:
{
GlModal
:
GlModalDirective
,
GlTooltip
:
GlTooltipDirective
,
},
props
:
{
policy
:
{
type
:
Object
,
required
:
true
,
validator
:
({
name
,
rules
})
=>
{
return
typeof
name
===
'
string
'
&&
Array
.
isArray
(
rules
)
&&
rules
.
every
(
isRuleValid
);
},
},
index
:
{
type
:
Number
,
required
:
true
,
},
},
data
()
{
return
{
isPolicyVisible
:
this
.
index
===
0
,
};
},
computed
:
{
policyVisibleAngleIcon
()
{
return
this
.
isPolicyVisible
?
'
angle-down
'
:
'
angle-right
'
;
},
policyVisibleAngleIconLabel
()
{
return
this
.
isPolicyVisible
?
__
(
'
Collapse
'
)
:
__
(
'
Expand
'
);
},
},
};
</
script
>
<
template
>
<gl-card
class=
"gl-mt-5"
:class=
"
{ 'gl-border-bottom-0': !isPolicyVisible }"
:body-class="{ 'gl-p-0': !isPolicyVisible }"
:header-class="{ 'gl-py-3': true, 'gl-rounded-base': !isPolicyVisible }"
>
<template
#header
>
<div
class=
"gl-display-flex gl-align-items-center"
>
<gl-button
v-gl-tooltip
class=
"gl-mr-2 gl-p-0!"
:title=
"policyVisibleAngleIconLabel"
:aria-label=
"policyVisibleAngleIconLabel"
category=
"tertiary"
@
click=
"isPolicyVisible = !isPolicyVisible"
>
<gl-icon
:size=
"12"
:name=
"policyVisibleAngleIcon"
/>
</gl-button>
<h3
class=
"gl-font-weight-bold gl-font-lg gl-m-0"
>
{{
policy
.
name
}}
</h3>
<gl-button-group
class=
"gl-ml-auto"
>
<gl-button
v-gl-tooltip
:title=
"$options.i18n.editPolicy"
icon=
"pencil"
:aria-label=
"$options.i18n.editPolicy"
disabled
/>
<gl-button
v-gl-tooltip
:title=
"$options.i18n.deletePolicy"
icon=
"remove"
:aria-label=
"$options.i18n.deletePolicy"
disabled
/>
</gl-button-group>
</div>
</
template
>
<gl-collapse
:visible=
"isPolicyVisible"
>
<p
v-if=
"policy.description"
class=
"gl-text-gray-500 gl-mb-5"
>
{{ policy.description }}
</p>
<div
class=
"gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-5"
>
<div
v-for=
"(rule, ruleIndex) in policy.rules"
:key=
"rule.id"
:class=
"{ 'gl-mb-5': ruleIndex !== policy.rules.length - 1 }"
>
<gl-icon
name=
"clock"
class=
"gl-mr-3"
/>
<gl-sprintf
:message=
"$options.i18n.escalationRule"
>
<
template
#alertStatus
>
{{
$options
.
ALERT_STATUSES
[
rule
.
status
].
toLowerCase
()
}}
</
template
>
<
template
#minutes
>
<span
class=
"gl-font-weight-bold"
>
{{
rule
.
elapsedTimeSeconds
}}
{{
$options
.
i18n
.
minutes
}}
</span>
</
template
>
<
template
#then
>
<span
class=
"right-arrow"
>
<i
class=
"right-arrow-head"
></i>
</span>
<gl-icon
name=
"notifications"
class=
"gl-mr-3"
/>
</
template
>
<
template
#doAction
>
{{
$options
.
ACTIONS
[
$options
.
DEFAULT_ACTION
].
toLowerCase
()
}}
</
template
>
<
template
#schedule
>
<span
class=
"gl-font-weight-bold"
>
{{
rule
.
oncallSchedule
.
name
}}
</span>
</
template
>
</gl-sprintf>
</div>
</div>
</gl-collapse>
</gl-card>
</template>
ee/app/assets/javascripts/escalation_policies/constants.js
View file @
c58680e2
...
@@ -5,11 +5,13 @@ export const ALERT_STATUSES = {
...
@@ -5,11 +5,13 @@ export const ALERT_STATUSES = {
RESOLVED
:
s__
(
'
AlertManagement|Resolved
'
),
RESOLVED
:
s__
(
'
AlertManagement|Resolved
'
),
};
};
export
const
DEFAULT_ACTION
=
'
EMAIL_ONCALL_SCHEDULE_USER
'
;
export
const
ACTIONS
=
{
export
const
ACTIONS
=
{
EMAIL_ONCALL_SCHEDULE_USER
:
s__
(
'
EscalationPolicies|Email on-call user in schedule
'
),
[
DEFAULT_ACTION
]
:
s__
(
'
EscalationPolicies|Email on-call user in schedule
'
),
};
};
export
const
defaultEscalationRule
=
{
export
const
DEFAULT_ESCALATION_RULE
=
{
status
:
'
ACKNOWLEDGED
'
,
status
:
'
ACKNOWLEDGED
'
,
elapsedTimeSeconds
:
0
,
elapsedTimeSeconds
:
0
,
action
:
'
EMAIL_ONCALL_SCHEDULE_USER
'
,
action
:
'
EMAIL_ONCALL_SCHEDULE_USER
'
,
...
@@ -17,3 +19,4 @@ export const defaultEscalationRule = {
...
@@ -17,3 +19,4 @@ export const defaultEscalationRule = {
};
};
export
const
addEscalationPolicyModalId
=
'
addEscalationPolicyModal
'
;
export
const
addEscalationPolicyModalId
=
'
addEscalationPolicyModal
'
;
export
const
editEscalationPolicyModalId
=
'
editEscalationPolicyModal
'
;
ee/app/assets/javascripts/escalation_policies/graphql/cache_updates.js
0 → 100644
View file @
c58680e2
import
produce
from
'
immer
'
;
const
addEscalationPolicyToStore
=
(
store
,
query
,
{
escalationPolicyCreate
},
variables
)
=>
{
const
policy
=
escalationPolicyCreate
?.
escalationPolicy
;
if
(
!
policy
)
{
return
;
}
const
sourceData
=
store
.
readQuery
({
query
,
variables
,
});
const
data
=
produce
(
sourceData
,
(
draftData
)
=>
{
draftData
.
project
.
incidentManagementEscalationPolicies
.
nodes
.
push
(
policy
);
});
store
.
writeQuery
({
query
,
variables
,
data
,
});
};
export
const
hasErrors
=
({
errors
=
[]
})
=>
errors
?.
length
;
export
const
updateStoreOnEscalationPolicyCreate
=
(
store
,
query
,
data
,
variables
)
=>
{
if
(
!
hasErrors
(
data
))
{
addEscalationPolicyToStore
(
store
,
query
,
data
,
variables
);
}
};
ee/app/assets/javascripts/escalation_policies/graphql/fragments/escalation_policy.fragment.graphql
0 → 100644
View file @
c58680e2
fragment
EscalationPolicy
on
EscalationPolicyType
{
id
name
description
rules
{
id
status
elapsedTimeSeconds
oncallSchedule
{
iid
name
}
}
}
ee/app/assets/javascripts/escalation_policies/graphql/mutations/create_escalation_policy.mutation.graphql
View file @
c58680e2
#import "../fragments/escalation_policy.fragment.graphql"
mutation
escalationPolicyCreate
(
$input
:
EscalationPolicyCreateInput
!)
{
mutation
escalationPolicyCreate
(
$input
:
EscalationPolicyCreateInput
!)
{
escalationPolicyCreate
(
input
:
$input
)
{
escalationPolicyCreate
(
input
:
$input
)
{
escalationPolicy
{
escalationPolicy
{
id
...
EscalationPolicy
name
description
rules
{
status
elapsedTimeSeconds
oncallSchedule
{
iid
name
}
}
}
}
errors
errors
}
}
...
...
ee/app/assets/javascripts/escalation_policies/graphql/queries/get_escalation_policies.query.graphql
0 → 100644
View file @
c58680e2
#import "../fragments/escalation_policy.fragment.graphql"
query
getEscalationPolicies
(
$projectPath
:
ID
!)
{
project
(
fullPath
:
$projectPath
)
{
incidentManagementEscalationPolicies
{
nodes
{
...
EscalationPolicy
}
}
}
}
ee/app/assets/javascripts/escalation_policies/index.js
View file @
c58680e2
...
@@ -19,6 +19,7 @@ const apolloProvider = new VueApollo({
...
@@ -19,6 +19,7 @@ const apolloProvider = new VueApollo({
return
defaultDataIdFromObject
(
object
);
return
defaultDataIdFromObject
(
object
);
},
},
},
},
assumeImmutableResults
:
true
,
},
},
),
),
});
});
...
...
ee/app/assets/javascripts/oncall_schedules/components/oncall_schedule.vue
View file @
c58680e2
...
@@ -237,7 +237,7 @@ export default {
...
@@ -237,7 +237,7 @@ export default {
class=
"gl-mt-5"
class=
"gl-mt-5"
:class=
"
{ 'gl-border-bottom-0': !scheduleVisible }"
:class=
"
{ 'gl-border-bottom-0': !scheduleVisible }"
:body-class="{ 'gl-p-0': !scheduleVisible }"
:body-class="{ 'gl-p-0': !scheduleVisible }"
header-class="gl-py-3
"
:header-class="{ 'gl-py-3': true, 'gl-rounded-small': !scheduleVisible }
"
>
>
<template
#header
>
<template
#header
>
<div
<div
...
...
ee/spec/frontend/escalation_policies/__snapshots__/escalation_policy_spec.js.snap
0 → 100644
View file @
c58680e2
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`EscalationPolicy renders policy with rules 1`] = `
<gl-card-stub
bodyclass="[object Object]"
class="gl-mt-5"
footerclass=""
headerclass="[object Object]"
>
<gl-collapse-stub
visible="true"
>
<p
class="gl-text-gray-500 gl-mb-5"
>
Description 1 lives here
</p>
<div
class="gl-border-solid gl-border-1 gl-border-gray-100 gl-rounded-base gl-p-5"
>
<div
class="gl-mb-5"
>
<gl-icon-stub
class="gl-mr-3"
name="clock"
size="16"
/>
IF alert is not
acknowledged
in
<span
class="gl-font-weight-bold"
>
10 mins
</span>
<span
class="right-arrow"
>
<i
class="right-arrow-head"
/>
</span>
<gl-icon-stub
class="gl-mr-3"
name="notifications"
size="16"
/>
THEN
email on-call user in schedule
<span
class="gl-font-weight-bold"
>
Schedule to fill in
</span>
</div>
<div
class=""
>
<gl-icon-stub
class="gl-mr-3"
name="clock"
size="16"
/>
IF alert is not
resolved
in
<span
class="gl-font-weight-bold"
>
20 mins
</span>
<span
class="right-arrow"
>
<i
class="right-arrow-head"
/>
</span>
<gl-icon-stub
class="gl-mr-3"
name="notifications"
size="16"
/>
THEN
email on-call user in schedule
<span
class="gl-font-weight-bold"
>
Monitor schedule
</span>
</div>
</div>
</gl-collapse-stub>
</gl-card-stub>
`;
ee/spec/frontend/escalation_policies/add_edit_escalation_policy_form_spec.js
View file @
c58680e2
import
{
GlLink
}
from
'
@gitlab/ui
'
;
import
{
GlLink
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
AddEscalationPolicyForm
,
{
import
AddEscalationPolicyForm
,
{
i18n
,
i18n
,
}
from
'
ee/escalation_policies/components/add_edit_escalation_policy_form.vue
'
;
}
from
'
ee/escalation_policies/components/add_edit_escalation_policy_form.vue
'
;
import
EscalationRule
from
'
ee/escalation_policies/components/escalation_rule.vue
'
;
import
EscalationRule
from
'
ee/escalation_policies/components/escalation_rule.vue
'
;
import
{
defaultEscalationRule
}
from
'
ee/escalation_policies/constants
'
;
import
{
DEFAULT_ESCALATION_RULE
}
from
'
ee/escalation_policies/constants
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
import
mockPolicy
from
'
./mocks/mockPolicy.json
'
;
import
mockPolicies
from
'
./mocks/mockPolicies.json
'
;
describe
(
'
AddEscalationPolicyForm
'
,
()
=>
{
describe
(
'
AddEscalationPolicyForm
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
const
projectPath
=
'
group/project
'
;
const
projectPath
=
'
group/project
'
;
const
createComponent
=
({
props
=
{}
}
=
{})
=>
{
const
createComponent
=
({
props
=
{}
}
=
{})
=>
{
wrapper
=
extendedWrapper
(
wrapper
=
shallowMountExtended
(
AddEscalationPolicyForm
,
{
shallowMount
(
AddEscalationPolicyForm
,
{
propsData
:
{
propsData
:
{
form
:
{
form
:
{
name
:
mockPolicy
.
name
,
name
:
mockPolicies
[
1
]
.
name
,
description
:
mockPolicy
.
description
,
description
:
mockPolicies
[
1
]
.
description
,
},
},
validationState
:
{
validationState
:
{
name
:
true
,
name
:
true
,
...
@@ -34,8 +33,7 @@ describe('AddEscalationPolicyForm', () => {
...
@@ -34,8 +33,7 @@ describe('AddEscalationPolicyForm', () => {
queries
:
{
schedules
:
{
loading
:
false
}
},
queries
:
{
schedules
:
{
loading
:
false
}
},
},
},
},
},
}),
});
);
};
};
beforeEach
(()
=>
{
beforeEach
(()
=>
{
...
@@ -65,7 +63,7 @@ describe('AddEscalationPolicyForm', () => {
...
@@ -65,7 +63,7 @@ describe('AddEscalationPolicyForm', () => {
await
wrapper
.
vm
.
$nextTick
();
await
wrapper
.
vm
.
$nextTick
();
const
rules
=
findRules
();
const
rules
=
findRules
();
expect
(
rules
.
length
).
toBe
(
2
);
expect
(
rules
.
length
).
toBe
(
2
);
expect
(
rules
.
at
(
1
).
props
(
'
rule
'
)).
toMatchObject
(
defaultEscalationRule
);
expect
(
rules
.
at
(
1
).
props
(
'
rule
'
)).
toMatchObject
(
DEFAULT_ESCALATION_RULE
);
});
});
it
(
'
should NOT emit updates when rule is added
'
,
async
()
=>
{
it
(
'
should NOT emit updates when rule is added
'
,
async
()
=>
{
...
...
ee/spec/frontend/escalation_policies/add_edit_escalation_policy_modal_spec.js
View file @
c58680e2
import
{
GlModal
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
GlModal
,
GlAlert
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
cloneDeep
}
from
'
lodash
'
;
import
AddEscalationPolicyForm
from
'
ee/escalation_policies/components/add_edit_escalation_policy_form.vue
'
;
import
AddEscalationPolicyForm
from
'
ee/escalation_policies/components/add_edit_escalation_policy_form.vue
'
;
import
AddEscalationPolicyModal
,
{
import
AddEscalationPolicyModal
,
{
i18n
,
i18n
,
}
from
'
ee/escalation_policies/components/add_edit_escalation_policy_modal.vue
'
;
}
from
'
ee/escalation_policies/components/add_edit_escalation_policy_modal.vue
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
waitForPromises
from
'
helpers/wait_for_promises
'
;
import
mockPolic
y
from
'
./mocks/mockPolicy
.json
'
;
import
mockPolic
ies
from
'
./mocks/mockPolicies
.json
'
;
describe
(
'
AddEscalationPolicyModal
'
,
()
=>
{
describe
(
'
AddEscalationPolicyModal
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
const
projectPath
=
'
group/project
'
;
const
projectPath
=
'
group/project
'
;
const
mockHideModal
=
jest
.
fn
();
const
mockHideModal
=
jest
.
fn
();
const
mutate
=
jest
.
fn
();
const
mutate
=
jest
.
fn
();
const
mockPolicy
=
cloneDeep
(
mockPolicies
[
0
]);
const
createComponent
=
({
escalationPolicy
,
data
}
=
{})
=>
{
const
createComponent
=
({
escalationPolicy
,
data
}
=
{})
=>
{
wrapper
=
shallowMount
(
AddEscalationPolicyModal
,
{
wrapper
=
shallowMount
(
AddEscalationPolicyModal
,
{
...
@@ -60,14 +62,23 @@ describe('AddEscalationPolicyModal', () => {
...
@@ -60,14 +62,23 @@ describe('AddEscalationPolicyModal', () => {
it
(
'
makes a request with form data to create an escalation policy
'
,
()
=>
{
it
(
'
makes a request with form data to create an escalation policy
'
,
()
=>
{
mutate
.
mockResolvedValueOnce
({});
mutate
.
mockResolvedValueOnce
({});
findModal
().
vm
.
$emit
(
'
primary
'
,
{
preventDefault
:
jest
.
fn
()
});
findModal
().
vm
.
$emit
(
'
primary
'
,
{
preventDefault
:
jest
.
fn
()
});
const
rules
=
mockPolicy
.
rules
.
map
(
({
status
,
elapsedTimeSeconds
,
oncallSchedule
:
{
id
}
})
=>
({
status
,
elapsedTimeSeconds
,
oncallScheduleIid
:
id
,
}),
);
expect
(
mutate
).
toHaveBeenCalledWith
(
expect
(
mutate
).
toHaveBeenCalledWith
(
expect
.
objectContaining
({
expect
.
objectContaining
({
variables
:
{
variables
:
{
input
:
{
input
:
{
projectPath
,
projectPath
,
...
mockPolicy
,
...
mockPolicy
,
rules
,
},
},
},
},
update
:
expect
.
any
(
Function
),
}),
}),
);
);
});
});
...
...
ee/spec/frontend/escalation_policies/escalation_policy_spec.js
0 → 100644
View file @
c58680e2
import
{
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
cloneDeep
}
from
'
lodash
'
;
import
EscalationPolicy
from
'
ee/escalation_policies/components/escalation_policy.vue
'
;
import
mockPolicies
from
'
./mocks/mockPolicies.json
'
;
describe
(
'
EscalationPolicy
'
,
()
=>
{
let
wrapper
;
const
createComponent
=
()
=>
{
wrapper
=
shallowMount
(
EscalationPolicy
,
{
propsData
:
{
policy
:
cloneDeep
(
mockPolicies
[
0
]),
index
:
0
,
},
stubs
:
{
GlSprintf
,
},
});
};
beforeEach
(()
=>
{
createComponent
();
});
afterEach
(()
=>
{
wrapper
.
destroy
();
});
it
(
'
renders policy with rules
'
,
()
=>
{
expect
(
wrapper
.
element
).
toMatchSnapshot
();
});
});
ee/spec/frontend/escalation_policies/escalation_policy_wrapper_spec.js
View file @
c58680e2
import
{
GlEmptyState
}
from
'
@gitlab/ui
'
;
import
{
GlEmptyState
,
GlLoadingIcon
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
EscalationPoliciesWrapper
from
'
ee/escalation_policies/components/escalation_policies_wrapper.vue
'
;
import
EscalationPolic
iesWrapper
,
{
import
EscalationPolic
y
from
'
ee/escalation_policies/components/escalation_policy.vue
'
;
i18n
,
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
}
from
'
ee/escalation_policies/components/escalation_policies_wrapper.vue
'
;
import
mockEscalationPolicies
from
'
./mocks/mockPolicies.json
'
;
describe
(
'
AlertManagementEmptyState
'
,
()
=>
{
describe
(
'
Escalation Policies Wrapper
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
const
emptyEscalationPoliciesSvgPath
=
'
illustration/path.svg
'
;
const
emptyEscalationPoliciesSvgPath
=
'
illustration/path.svg
'
;
const
projectPath
=
'
group/project
'
;
function
mountComponent
()
{
function
mountComponent
({
loading
=
false
,
escalationPolicies
=
[]
}
=
{})
{
wrapper
=
shallowMount
(
EscalationPoliciesWrapper
,
{
const
$apollo
=
{
queries
:
{
escalationPolicies
:
{
loading
,
},
},
};
wrapper
=
shallowMountExtended
(
EscalationPoliciesWrapper
,
{
provide
:
{
provide
:
{
emptyEscalationPoliciesSvgPath
,
emptyEscalationPoliciesSvgPath
,
projectPath
,
},
mocks
:
{
$apollo
,
},
data
()
{
return
{
escalationPolicies
,
};
},
},
});
});
}
}
...
@@ -24,15 +41,40 @@ describe('AlertManagementEmptyState', () => {
...
@@ -24,15 +41,40 @@ describe('AlertManagementEmptyState', () => {
wrapper
.
destroy
();
wrapper
.
destroy
();
});
});
const
findLoader
=
()
=>
wrapper
.
findComponent
(
GlLoadingIcon
);
const
findEmptyState
=
()
=>
wrapper
.
findComponent
(
GlEmptyState
);
const
findEmptyState
=
()
=>
wrapper
.
findComponent
(
GlEmptyState
);
const
findEscalationPolicies
=
()
=>
wrapper
.
findAllComponents
(
EscalationPolicy
);
const
findAddPolicyBtn
=
()
=>
wrapper
.
findByRole
(
'
button
'
,
{
name
:
EscalationPoliciesWrapper
.
i18n
.
addPolicy
});
describe
.
each
`
state | loading | escalationPolicies | showsEmptyState | showsLoader
${
'
is loading
'
}
|
${
true
}
|
${[]}
|
${
false
}
|
${
true
}
${
'
is empty
'
}
|
${
false
}
|
${[]}
|
${
true
}
|
${
false
}
${
'
has policies
'
}
|
${
false
}
|
${
mockEscalationPolicies
}
|
${
false
}
|
${
false
}
`
(
``
,
({
state
,
loading
,
escalationPolicies
,
showsEmptyState
,
showsLoader
})
=>
{
describe
(
`When
${
state
}
`
,
()
=>
{
beforeEach
(()
=>
{
mountComponent
({
loading
,
escalationPolicies
,
});
});
it
(
`does
${
loading
?
'
show
'
:
'
not show
'
}
a loader`
,
()
=>
{
expect
(
findLoader
().
exists
()).
toBe
(
showsLoader
);
});
it
(
`does
${
showsEmptyState
?
'
show
'
:
'
not show
'
}
an empty state`
,
()
=>
{
expect
(
findEmptyState
().
exists
()).
toBe
(
showsEmptyState
);
});
it
(
`does
${
escalationPolicies
.
length
?
'
show
'
:
'
not show
'
}
escalation policies`
,
()
=>
{
expect
(
findEscalationPolicies
()).
toHaveLength
(
escalationPolicies
.
length
);
});
describe
(
'
Empty state
'
,
()
=>
{
it
(
`does
${
escalationPolicies
.
length
?
'
show
'
:
'
not show
'
}
"Add policy" button`
,
()
=>
{
it
(
'
shows empty state and passed correct attributes to it
'
,
()
=>
{
expect
(
findAddPolicyBtn
().
exists
()).
toBe
(
Boolean
(
escalationPolicies
.
length
));
expect
(
findEmptyState
().
exists
()).
toBe
(
true
);
expect
(
findEmptyState
().
attributes
()).
toEqual
({
title
:
i18n
.
emptyState
.
title
,
description
:
i18n
.
emptyState
.
description
,
svgpath
:
emptyEscalationPoliciesSvgPath
,
});
});
});
});
});
});
...
...
ee/spec/frontend/escalation_policies/escalation_rule_spec.js
View file @
c58680e2
import
{
GlDropdownItem
,
GlFormGroup
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
GlDropdownItem
,
GlFormGroup
,
GlSprintf
}
from
'
@gitlab/ui
'
;
import
{
shallowMount
}
from
'
@vue/test-utils
'
;
import
{
cloneDeep
}
from
'
lodash
'
;
import
{
cloneDeep
}
from
'
lodash
'
;
import
EscalationRule
,
{
i18n
}
from
'
ee/escalation_policies/components/escalation_rule.vue
'
;
import
EscalationRule
,
{
i18n
}
from
'
ee/escalation_policies/components/escalation_rule.vue
'
;
import
{
defaultEscalationRule
,
ACTIONS
,
ALERT_STATUSES
}
from
'
ee/escalation_policies/constants
'
;
import
{
DEFAULT_ESCALATION_RULE
,
ACTIONS
,
ALERT_STATUSES
}
from
'
ee/escalation_policies/constants
'
;
import
{
extendedWrapper
}
from
'
helpers/vue_test_utils_helper
'
;
import
{
shallowMountExtended
}
from
'
helpers/vue_test_utils_helper
'
;
const
mockSchedules
=
[
const
mockSchedules
=
[
{
id
:
1
,
name
:
'
schedule1
'
},
{
id
:
1
,
name
:
'
schedule1
'
},
...
@@ -17,10 +16,9 @@ const invalidTimeMsg = i18n.fields.rules.invalidTimeValidationMsg;
...
@@ -17,10 +16,9 @@ const invalidTimeMsg = i18n.fields.rules.invalidTimeValidationMsg;
describe
(
'
EscalationRule
'
,
()
=>
{
describe
(
'
EscalationRule
'
,
()
=>
{
let
wrapper
;
let
wrapper
;
const
createComponent
=
({
props
=
{}
}
=
{})
=>
{
const
createComponent
=
({
props
=
{}
}
=
{})
=>
{
wrapper
=
extendedWrapper
(
wrapper
=
shallowMountExtended
(
EscalationRule
,
{
shallowMount
(
EscalationRule
,
{
propsData
:
{
propsData
:
{
rule
:
cloneDeep
(
defaultEscalationRule
),
rule
:
cloneDeep
(
DEFAULT_ESCALATION_RULE
),
schedules
:
mockSchedules
,
schedules
:
mockSchedules
,
schedulesLoading
:
false
,
schedulesLoading
:
false
,
index
:
0
,
index
:
0
,
...
@@ -31,8 +29,7 @@ describe('EscalationRule', () => {
...
@@ -31,8 +29,7 @@ describe('EscalationRule', () => {
GlFormGroup
,
GlFormGroup
,
GlSprintf
,
GlSprintf
,
},
},
}),
});
);
};
};
beforeEach
(()
=>
{
beforeEach
(()
=>
{
...
...
ee/spec/frontend/escalation_policies/mocks/mockPolicies.json
0 → 100644
View file @
c58680e2
[
{
"id"
:
"37"
,
"name"
:
"Escalation policy"
,
"description"
:
"Description 1 lives here"
,
"rules"
:
[
{
"id"
:
"gid://gitlab/IncidentManagement::EscalationRule/22"
,
"status"
:
"ACKNOWLEDGED"
,
"elapsedTimeSeconds"
:
10
,
"oncallSchedule"
:
{
"iid"
:
"3"
,
"name"
:
"Schedule to fill in"
}
},
{
"id"
:
"gid://gitlab/IncidentManagement::EscalationRule/23"
,
"status"
:
"RESOLVED"
,
"elapsedTimeSeconds"
:
20
,
"oncallSchedule"
:
{
"iid"
:
"4"
,
"name"
:
"Monitor schedule"
}
}
]
},
{
"id"
:
"39"
,
"name"
:
"Another escalation policy"
,
"description"
:
"Description 1 lives here"
,
"rules"
:
[
{
"id"
:
"gid://gitlab/IncidentManagement::EscalationRule/48"
,
"status"
:
"ACKNOWLEDGED"
,
"elapsedTimeSeconds"
:
30
,
"oncallSchedule"
:
{
"iid"
:
"3"
,
"name"
:
"Schedule to fill in"
}
}
]
}
]
ee/spec/frontend/escalation_policies/mocks/mockPolicy.json
deleted
100644 → 0
View file @
22fedb37
{
"iid"
:
"37"
,
"name"
:
"Test ecsaltion policy"
,
"description"
:
"Description 1 lives here"
,
"rules"
:
[]
}
locale/gitlab.pot
View file @
c58680e2
...
@@ -13063,9 +13063,15 @@ msgstr ""
...
@@ -13063,9 +13063,15 @@ msgstr ""
msgid "EscalationPolicies|Add escalation policy"
msgid "EscalationPolicies|Add escalation policy"
msgstr ""
msgstr ""
msgid "EscalationPolicies|Add policy"
msgstr ""
msgid "EscalationPolicies|Create an escalation policy in GitLab"
msgid "EscalationPolicies|Create an escalation policy in GitLab"
msgstr ""
msgstr ""
msgid "EscalationPolicies|Delete escalation policy"
msgstr ""
msgid "EscalationPolicies|Edit escalation policy"
msgid "EscalationPolicies|Edit escalation policy"
msgstr ""
msgstr ""
...
@@ -13075,12 +13081,18 @@ msgstr ""
...
@@ -13075,12 +13081,18 @@ msgstr ""
msgid "EscalationPolicies|Email on-call user in schedule"
msgid "EscalationPolicies|Email on-call user in schedule"
msgstr ""
msgstr ""
msgid "EscalationPolicies|Escalation policies"
msgstr ""
msgid "EscalationPolicies|Escalation rules"
msgid "EscalationPolicies|Escalation rules"
msgstr ""
msgstr ""
msgid "EscalationPolicies|Failed to load oncall-schedules"
msgid "EscalationPolicies|Failed to load oncall-schedules"
msgstr ""
msgstr ""
msgid "EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} %{then} THEN %{doAction} %{schedule}"
msgstr ""
msgid "EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} minutes"
msgid "EscalationPolicies|IF alert is not %{alertStatus} in %{minutes} minutes"
msgstr ""
msgstr ""
...
@@ -13096,6 +13108,9 @@ msgstr ""
...
@@ -13096,6 +13108,9 @@ msgstr ""
msgid "EscalationPolicies|THEN %{doAction} %{schedule}"
msgid "EscalationPolicies|THEN %{doAction} %{schedule}"
msgstr ""
msgstr ""
msgid "EscalationPolicies|mins"
msgstr ""
msgid "Estimate"
msgid "Estimate"
msgstr ""
msgstr ""
...
...
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