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
ce5c8c67
Commit
ce5c8c67
authored
Jul 06, 2020
by
Jan Provaznik
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'bulk-edit-health-status' into 'master'
Bulk edit health status See merge request gitlab-org/gitlab!33065
parents
e99e3f2d
6143f05f
Changes
16
Show whitespace changes
Inline
Side-by-side
Showing
16 changed files
with
469 additions
and
68 deletions
+469
-68
app/assets/javascripts/issuable_bulk_update_actions.js
app/assets/javascripts/issuable_bulk_update_actions.js
+1
-0
app/assets/javascripts/issuable_bulk_update_sidebar.js
app/assets/javascripts/issuable_bulk_update_sidebar.js
+6
-0
app/controllers/concerns/issuable_actions.rb
app/controllers/concerns/issuable_actions.rb
+6
-2
app/services/issuable/bulk_update_service.rb
app/services/issuable/bulk_update_service.rb
+32
-21
app/views/shared/issuable/_bulk_update_sidebar.html.haml
app/views/shared/issuable/_bulk_update_sidebar.html.haml
+8
-0
ee/app/assets/javascripts/sidebar/components/status/health_status_dropdown.vue
...ipts/sidebar/components/status/health_status_dropdown.vue
+131
-0
ee/app/assets/javascripts/sidebar/constants.js
ee/app/assets/javascripts/sidebar/constants.js
+7
-0
ee/app/assets/javascripts/vue_shared/components/sidebar/health_status_select/health_status_bundle.js
...ents/sidebar/health_status_select/health_status_bundle.js
+48
-0
ee/app/controllers/concerns/ee/issuable_actions.rb
ee/app/controllers/concerns/ee/issuable_actions.rb
+1
-0
ee/app/services/ee/issuable/bulk_update_service.rb
ee/app/services/ee/issuable/bulk_update_service.rb
+16
-3
ee/app/views/shared/issuable/_group_bulk_update_sidebar.html.haml
...iews/shared/issuable/_group_bulk_update_sidebar.html.haml
+8
-0
ee/changelogs/unreleased/bulk-edit-health-status.yml
ee/changelogs/unreleased/bulk-edit-health-status.yml
+5
-0
ee/spec/features/issues/bulk_assignment_health_status_spec.rb
...pec/features/issues/bulk_assignment_health_status_spec.rb
+153
-0
ee/spec/services/ee/issuable/bulk_update_service_spec.rb
ee/spec/services/ee/issuable/bulk_update_service_spec.rb
+27
-28
locale/gitlab.pot
locale/gitlab.pot
+6
-0
spec/services/issuable/bulk_update_service_spec.rb
spec/services/issuable/bulk_update_service_spec.rb
+14
-14
No files found.
app/assets/javascripts/issuable_bulk_update_actions.js
View file @
ce5c8c67
...
...
@@ -86,6 +86,7 @@ export default {
milestone_id
:
this
.
form
.
find
(
'
input[name="update[milestone_id]"]
'
).
val
(),
issuable_ids
:
this
.
form
.
find
(
'
input[name="update[issuable_ids]"]
'
).
val
(),
subscription_event
:
this
.
form
.
find
(
'
input[name="update[subscription_event]"]
'
).
val
(),
health_status
:
this
.
form
.
find
(
'
input[name="update[health_status]"]
'
).
val
(),
add_label_ids
:
[],
remove_label_ids
:
[],
},
...
...
app/assets/javascripts/issuable_bulk_update_sidebar.js
View file @
ce5c8c67
...
...
@@ -7,6 +7,8 @@ import MilestoneSelect from './milestone_select';
import
issueStatusSelect
from
'
./issue_status_select
'
;
import
subscriptionSelect
from
'
./subscription_select
'
;
import
LabelsSelect
from
'
./labels_select
'
;
import
HealthStatusSelect
from
'
ee_else_ce/vue_shared/components/sidebar/health_status_select/health_status_bundle
'
;
import
issueableEventHub
from
'
./issuables_list/eventhub
'
;
const
HIDDEN_CLASS
=
'
hidden
'
;
...
...
@@ -63,6 +65,10 @@ export default class IssuableBulkUpdateSidebar {
new
MilestoneSelect
();
issueStatusSelect
();
subscriptionSelect
();
if
(
HealthStatusSelect
)
{
HealthStatusSelect
();
}
}
setupBulkUpdateActions
()
{
...
...
app/controllers/concerns/issuable_actions.rb
View file @
ce5c8c67
...
...
@@ -110,9 +110,13 @@ module IssuableActions
def
bulk_update
result
=
Issuable
::
BulkUpdateService
.
new
(
parent
,
current_user
,
bulk_update_params
).
execute
(
resource_name
)
quantity
=
result
[
:count
]
if
result
.
success?
quantity
=
result
.
payload
[
:count
]
render
json:
{
notice:
"
#{
quantity
}
#{
resource_name
.
pluralize
(
quantity
)
}
updated"
}
elsif
result
.
error?
render
json:
{
errors:
result
.
message
},
status:
result
.
http_status
end
end
# rubocop:disable CodeReuse/ActiveRecord
...
...
app/services/issuable/bulk_update_service.rb
View file @
ce5c8c67
...
...
@@ -11,40 +11,29 @@ module Issuable
end
def
execute
(
type
)
model_class
=
type
.
classify
.
constantize
update_class
=
type
.
classify
.
pluralize
.
constantize
::
UpdateService
ids
=
params
.
delete
(
:issuable_ids
).
split
(
","
)
items
=
find_issuables
(
parent
,
model_class
,
ids
)
set_update_params
(
type
)
items
=
update_issuables
(
type
,
ids
)
response_success
(
payload:
{
count:
items
.
count
})
rescue
ArgumentError
=>
e
response_error
(
e
.
message
,
422
)
end
private
def
set_update_params
(
type
)
params
.
slice!
(
*
permitted_attrs
(
type
))
params
.
delete_if
{
|
k
,
v
|
v
.
blank?
}
if
params
[
:assignee_ids
]
==
[
IssuableFinder
::
Params
::
NONE
.
to_s
]
params
[
:assignee_ids
]
=
[]
end
items
.
each
do
|
issuable
|
next
unless
can?
(
current_user
,
:"update_
#{
type
}
"
,
issuable
)
update_class
.
new
(
issuable
.
issuing_parent
,
current_user
,
params
).
execute
(
issuable
)
end
{
count:
items
.
count
,
success:
!
items
.
count
.
zero?
}
end
private
def
permitted_attrs
(
type
)
attrs
=
%i(state_event milestone_id add_label_ids remove_label_ids subscription_event)
issuable_specific_attrs
(
type
,
attrs
)
end
def
issuable_specific_attrs
(
type
,
attrs
)
if
type
==
'issue'
||
type
==
'merge_request'
attrs
.
push
(
:assignee_ids
)
else
...
...
@@ -52,6 +41,20 @@ module Issuable
end
end
def
update_issuables
(
type
,
ids
)
model_class
=
type
.
classify
.
constantize
update_class
=
type
.
classify
.
pluralize
.
constantize
::
UpdateService
items
=
find_issuables
(
parent
,
model_class
,
ids
)
items
.
each
do
|
issuable
|
next
unless
can?
(
current_user
,
:"update_
#{
type
}
"
,
issuable
)
update_class
.
new
(
issuable
.
issuing_parent
,
current_user
,
params
).
execute
(
issuable
)
end
items
end
def
find_issuables
(
parent
,
model_class
,
ids
)
if
parent
.
is_a?
(
Project
)
model_class
.
id_in
(
ids
).
of_projects
(
parent
)
...
...
@@ -59,6 +62,14 @@ module Issuable
model_class
.
id_in
(
ids
).
of_projects
(
parent
.
all_projects
)
end
end
def
response_success
(
message:
nil
,
payload:
nil
)
ServiceResponse
.
success
(
message:
message
,
payload:
payload
)
end
def
response_error
(
message
,
http_status
)
ServiceResponse
.
error
(
message:
message
,
http_status:
http_status
)
end
end
end
...
...
app/views/shared/issuable/_bulk_update_sidebar.html.haml
View file @
ce5c8c67
-
type
=
local_assigns
.
fetch
(
:type
)
-
bulk_issue_health_status_flag
=
Feature
.
enabled?
(
:bulk_update_health_status
,
@project
&
.
group
)
&&
type
==
:issues
&&
@project
&
.
group
&
.
feature_available?
(
:issuable_health_status
)
%aside
.issues-bulk-update.js-right-sidebar.right-sidebar
{
"aria-live"
=>
"polite"
,
data:
{
'signed-in'
:
current_user
.
present?
}
}
.issuable-sidebar.hidden
...
...
@@ -36,6 +37,13 @@
=
_
(
'Labels'
)
.filter-item.labels-filter
=
render
"shared/issuable/label_dropdown"
,
classes:
[
"js-filter-bulk-update"
,
"js-multiselect"
],
dropdown_title:
_
(
"Apply a label"
),
show_create:
false
,
show_footer:
false
,
extra_options:
false
,
filter_submit:
false
,
data_options:
{
persist_when_hide:
"true"
,
field_name:
"update[label_ids][]"
,
show_no:
false
,
show_any:
false
,
use_id:
true
,
default_label:
_
(
"Labels"
)
},
label_name:
_
(
"Select labels"
),
no_default_styles:
true
-
if
bulk_issue_health_status_flag
.block
.title
=
_
(
'Health status'
)
.filter-item.health-status.health-status-filter
#js-bulk-update-health-status-root
%input
{
id:
'issue_health_status_value'
,
type:
'hidden'
,
name:
'update[health_status]'
}
.block
.title
=
_
(
'Subscriptions'
)
...
...
ee/app/assets/javascripts/sidebar/components/status/health_status_dropdown.vue
0 → 100644
View file @
ce5c8c67
<
script
>
import
{
GlButton
,
GlDropdownItem
,
GlDropdown
,
GlDropdownDivider
}
from
'
@gitlab/ui
'
;
import
{
s__
}
from
'
~/locale
'
;
import
{
healthStatusTextMap
}
from
'
../../constants
'
;
export
default
{
components
:
{
GlButton
,
GlDropdown
,
GlDropdownItem
,
GlDropdownDivider
,
},
props
:
{
isEditable
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
isFetching
:
{
type
:
Boolean
,
required
:
false
,
default
:
false
,
},
status
:
{
type
:
String
,
required
:
false
,
default
:
''
,
},
},
data
()
{
return
{
isDropdownShowing
:
false
,
selectedStatus
:
this
.
status
,
statusOptions
:
Object
.
keys
(
healthStatusTextMap
).
map
(
key
=>
({
key
,
value
:
healthStatusTextMap
[
key
],
})),
};
},
computed
:
{
statusText
()
{
return
this
.
status
?
healthStatusTextMap
[
this
.
status
]
:
s__
(
'
Sidebar|None
'
);
},
dropdownText
()
{
if
(
this
.
status
===
null
)
{
return
s__
(
'
No status
'
);
}
return
this
.
status
?
healthStatusTextMap
[
this
.
status
]
:
s__
(
'
Select health status
'
);
},
tooltipText
()
{
let
tooltipText
=
s__
(
'
Sidebar|Health status
'
);
if
(
this
.
status
)
{
tooltipText
+=
`:
${
this
.
statusText
}
`
;
}
return
tooltipText
;
},
},
watch
:
{
status
(
status
)
{
this
.
selectedStatus
=
status
;
},
},
methods
:
{
handleDropdownClick
(
status
)
{
this
.
selectedStatus
=
status
;
this
.
$emit
(
'
onDropdownClick
'
,
status
);
this
.
hideDropdown
();
},
hideDropdown
()
{
this
.
isDropdownShowing
=
false
;
},
isSelected
(
status
)
{
return
this
.
status
===
status
;
},
},
};
</
script
>
<
template
>
<div
class=
"dropdown dropdown-menu-selectable"
>
<gl-dropdown
ref=
"dropdown"
class=
"w-100"
:text=
"dropdownText"
@
keydown.esc.native=
"hideDropdown"
@
hide=
"hideDropdown"
>
<div
class=
"dropdown-title"
>
<span
class=
"health-title"
>
{{
s__
(
'
Sidebar|Assign health status
'
)
}}
</span>
<gl-button
:aria-label=
"__('Close')"
variant=
"link"
class=
"dropdown-title-button dropdown-menu-close"
icon=
"close"
@
click=
"hideDropdown"
/>
</div>
<div
class=
"dropdown-content dropdown-body"
>
<gl-dropdown-item
@
click=
"handleDropdownClick(null)"
>
<gl-button
variant=
"link"
class=
"dropdown-item health-dropdown-item"
:class=
"
{ 'is-active': isSelected(null) }"
>
{{
s__
(
'
Sidebar|No status
'
)
}}
</gl-button>
</gl-dropdown-item>
<gl-dropdown-divider
class=
"divider health-divider"
/>
<gl-dropdown-item
v-for=
"option in statusOptions"
:key=
"option.key"
@
click=
"handleDropdownClick(option.key)"
>
<gl-button
variant=
"link"
class=
"dropdown-item health-dropdown-item"
:class=
"
{ 'is-active': isSelected(option.key) }"
>
{{
option
.
value
}}
</gl-button>
</gl-dropdown-item>
</div>
</gl-dropdown>
</div>
</
template
>
ee/app/assets/javascripts/sidebar/constants.js
View file @
ce5c8c67
...
...
@@ -18,3 +18,10 @@ export const iterationSelectTextMap = {
noIterationItem
:
[{
title
:
__
(
'
No iteration
'
),
id
:
null
}],
iterationSelectFail
:
__
(
'
Failed to set iteration on this issue. Please try again.
'
),
};
export
const
healthStatusForRestApi
=
{
NO_STATUS
:
'
0
'
,
[
healthStatus
.
ON_TRACK
]:
'
on_track
'
,
[
healthStatus
.
NEEDS_ATTENTION
]:
'
needs_attention
'
,
[
healthStatus
.
AT_RISK
]:
'
at_risk
'
,
};
ee/app/assets/javascripts/vue_shared/components/sidebar/health_status_select/health_status_bundle.js
0 → 100644
View file @
ce5c8c67
import
Vue
from
'
vue
'
;
import
HealthStatusSelect
from
'
ee/sidebar/components/status/health_status_dropdown.vue
'
;
import
{
healthStatusForRestApi
}
from
'
ee/sidebar/constants
'
;
export
default
()
=>
{
const
el
=
document
.
getElementById
(
'
js-bulk-update-health-status-root
'
);
const
healthStatusFormFieldEl
=
document
.
getElementById
(
'
issue_health_status_value
'
);
if
(
!
el
&&
!
healthStatusFormFieldEl
)
{
return
false
;
}
return
new
Vue
({
el
,
components
:
{
HealthStatusSelect
,
},
data
()
{
return
{
selectedStatus
:
undefined
,
};
},
methods
:
{
handleHealthStatusSelect
(
selectedStatus
)
{
this
.
selectedStatus
=
selectedStatus
;
healthStatusFormFieldEl
.
setAttribute
(
'
value
'
,
healthStatusForRestApi
[
selectedStatus
||
'
NO_STATUS
'
],
);
},
},
render
(
createElement
)
{
return
createElement
(
'
health-status-select
'
,
{
props
:
{
isFetching
:
false
,
isEditable
:
true
,
showDropdown
:
true
,
status
:
this
.
selectedStatus
,
},
on
:
{
onDropdownClick
:
this
.
handleHealthStatusSelect
.
bind
(
this
),
},
});
},
});
};
ee/app/controllers/concerns/ee/issuable_actions.rb
View file @
ce5c8c67
...
...
@@ -7,6 +7,7 @@ module EE
EE_PERMITTED_KEYS
=
%w[
weight
health_status
]
.
freeze
private
...
...
ee/app/services/ee/issuable/bulk_update_service.rb
View file @
ce5c8c67
...
...
@@ -14,11 +14,24 @@ module EE
super
end
override
:
issuable_specific
_attrs
def
issuable_specific_attrs
(
type
,
attrs
)
override
:
permitted
_attrs
def
permitted_attrs
(
type
)
return
super
unless
type
==
'issue'
super
.
push
(
:health_status
,
:epic
)
super
.
push
(
:health_status
,
:epic_id
)
end
override
:set_update_params
def
set_update_params
(
type
)
super
set_health_status
end
def
set_health_status
return
unless
params
[
:health_status
].
present?
params
[
:health_status
]
=
nil
if
params
[
:health_status
]
==
IssuableFinder
::
Params
::
NONE
.
to_s
end
end
end
...
...
ee/app/views/shared/issuable/_group_bulk_update_sidebar.html.haml
View file @
ce5c8c67
-
group
=
local_assigns
.
fetch
(
:group
)
-
type
=
local_assigns
.
fetch
(
:type
)
-
bulk_issue_health_status_flag
=
type
==
:issues
&&
Feature
.
enabled?
(
:bulk_update_health_status
,
group
)
&&
group
&
.
feature_available?
(
:issuable_health_status
)
%aside
.issues-bulk-update.js-right-sidebar.right-sidebar
{
'aria-live'
=>
'polite'
,
data:
{
'signed-in'
:
current_user
.
present?
}
}
.issuable-sidebar.hidden
...
...
@@ -19,6 +20,13 @@
=
_
(
'Labels'
)
.filter-item.labels-filter
=
render
"shared/issuable/label_dropdown"
,
classes:
[
"js-filter-bulk-update"
,
"js-multiselect"
],
dropdown_title:
_
(
'Apply a label'
),
show_create:
false
,
show_footer:
false
,
extra_options:
false
,
filter_submit:
false
,
data_options:
{
persist_when_hide:
"true"
,
field_name:
"update[label_ids][]"
,
show_no:
false
,
show_any:
false
,
use_id:
true
,
default_label:
"Labels"
},
label_name:
_
(
'Select labels'
),
no_default_styles:
true
,
edit_context:
group
-
if
bulk_issue_health_status_flag
.block
.title
=
_
(
'Health status'
)
.filter-item.health-status.health-status-filter
#js-bulk-update-health-status-root
%input
{
id:
'issue_health_status_value'
,
type:
'hidden'
,
name:
'update[health_status]'
}
=
hidden_field_tag
'update[issuable_ids]'
,
[]
=
hidden_field_tag
:state_event
,
params
[
:state_event
]
ee/changelogs/unreleased/bulk-edit-health-status.yml
0 → 100644
View file @
ce5c8c67
---
title
:
Bulk edit health status
merge_request
:
33065
author
:
type
:
added
ee/spec/features/issues/bulk_assignment_health_status_spec.rb
0 → 100644
View file @
ce5c8c67
# frozen_string_literal: true
require
'spec_helper'
RSpec
.
describe
'Issues > Health status bulk assignment'
do
let_it_be
(
:user
)
{
create
(
:user
)
}
let_it_be
(
:group
)
{
create
(
:group
,
:public
)
}
let_it_be
(
:project
)
{
create
(
:project
,
:public
,
group:
group
)
}
let_it_be
(
:issue1
)
{
create
(
:issue
,
project:
project
,
title:
"Issue 1"
)
}
let_it_be
(
:issue2
)
{
create
(
:issue
,
project:
project
,
title:
"Issue 2"
)
}
shared_examples
'bulk edit option in sidebar'
do
|
context
|
it
'is present when bulk edit is enabled'
do
enable_bulk_update
(
context
)
expect
(
page
).
to
have_css
(
'.issuable-sidebar'
)
end
it
'is not present when bulk edit is disabled'
do
expect
(
page
).
not_to
have_css
(
'.issuable-sidebar'
)
end
end
shared_examples
'bulk edit health status'
do
|
context
|
before
do
enable_bulk_update
(
context
)
end
context
'health_status'
,
:js
do
context
'to all issues'
do
before
do
check
'check-all-issues'
open_health_status_dropdown
[
'On track'
]
update_issues
end
it
'updates the health statuses'
do
expect
(
issue1
.
reload
.
health_status
).
to
eq
'on_track'
expect
(
issue2
.
reload
.
health_status
).
to
eq
'on_track'
end
end
context
'to an issue'
do
before
do
check
"selected_issue_
#{
issue1
.
id
}
"
open_health_status_dropdown
[
'At risk'
]
update_issues
end
it
'updates the checked issue\'s status'
do
expect
(
issue1
.
reload
.
health_status
).
to
eq
'at_risk'
expect
(
issue2
.
reload
.
health_status
).
to
eq
nil
end
end
end
end
shared_examples
'bulk edit health_status with insufficient permissions'
do
it
'cannot bulk assign health_status'
do
expect
(
page
).
not_to
have_button
'Edit issues'
expect
(
page
).
not_to
have_css
'.check-all-issues'
expect
(
page
).
not_to
have_css
'.issue-check'
end
end
before
do
stub_feature_flags
(
vue_issuables_list:
false
)
end
context
'as an allowed user'
,
:js
do
before
do
allow
(
group
).
to
receive
(
:feature_enabled?
).
and_return
(
true
)
stub_licensed_features
(
group_bulk_edit:
true
,
issuable_health_status:
true
)
group
.
add_maintainer
(
user
)
sign_in
user
end
context
'at group level'
do
it_behaves_like
'bulk edit option in sidebar'
,
:group
it_behaves_like
'bulk edit health status'
,
:group
end
context
'at project level'
do
it_behaves_like
'bulk edit option in sidebar'
,
:project
it_behaves_like
'bulk edit health status'
,
:project
end
end
context
'as a guest'
,
:js
do
before
do
sign_in
user
allow
(
group
).
to
receive
(
:feature_enabled?
).
and_return
(
true
)
stub_licensed_features
(
issuable_health_status:
true
)
end
context
'at group level'
do
before
do
visit
issues_group_path
(
group
)
end
it_behaves_like
'bulk edit health_status with insufficient permissions'
end
context
'at project level'
do
before
do
visit
project_issues_path
(
project
)
end
it_behaves_like
'bulk edit health_status with insufficient permissions'
end
end
def
open_health_status_dropdown
(
items
=
[])
page
.
within
(
'.issues-bulk-update'
)
do
click_button
'Select health status'
items
.
map
do
|
item
|
find
(
'.gl-button-text'
,
{
text:
item
}).
click
end
end
end
def
toggle_issue
(
issue
,
uncheck
=
false
)
page
.
within
(
'.issues-list'
)
do
if
uncheck
uncheck
"selected_issue_
#{
issue
.
id
}
"
else
check
"selected_issue_
#{
issue
.
id
}
"
end
end
end
def
uncheck_issue
(
issue
)
toggle_issue
(
issue
,
uncheck:
true
)
end
def
update_issues
find
(
'.update-selected-issues'
).
click
wait_for_requests
end
def
enable_bulk_update
(
context
)
if
context
==
:project
visit
project_issues_path
(
project
)
else
visit
issues_group_path
(
group
)
end
click_button
'Edit issues'
end
end
ee/spec/services/ee/issuable/bulk_update_service_spec.rb
View file @
ce5c8c67
...
...
@@ -12,8 +12,8 @@ RSpec.describe Issuable::BulkUpdateService do
shared_examples
'updates issuables attribute'
do
|
attribute
|
it
'succeeds and returns the correct number of issuables updated'
do
expect
(
subject
[
:success
]
).
to
be_truthy
expect
(
subject
[
:count
]).
to
eq
(
issuables
.
count
)
expect
(
subject
.
success?
).
to
be_truthy
expect
(
subject
.
payload
[
:count
]).
to
eq
(
issuables
.
count
)
issuables
.
each
do
|
issuable
|
expect
(
issuable
.
reload
.
send
(
attribute
)).
to
eq
(
new_value
)
end
...
...
@@ -31,48 +31,55 @@ RSpec.describe Issuable::BulkUpdateService do
context
'with issues'
do
let_it_be
(
:type
)
{
'issue'
}
let_it_be
(
:parent
)
{
group
}
let
(
:issue1
)
{
create
(
:issue
,
project:
project1
,
health_status: :at_risk
,
epic:
epic
)
}
let
(
:issue2
)
{
create
(
:issue
,
project:
project2
,
health_status: :at_risk
,
epic:
epic
)
}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let
(
:epic2
)
{
create
(
:epic
,
group:
group
)
}
let
(
:issue1
)
{
create
(
:issue
,
project:
project1
,
health_status: :at_risk
)
}
let
(
:issue2
)
{
create
(
:issue
,
project:
project2
,
health_status: :at_risk
)
}
let
(
:issuables
)
{
[
issue1
,
issue2
]
}
before
do
group
.
add_reporter
(
user
)
end
context
'updating health status
and epic
'
do
context
'updating health status'
do
let
(
:params
)
do
{
issuable_ids:
issuables
.
map
(
&
:id
),
health_status: :on_track
,
epic:
epic2
health_status: :on_track
}
end
context
'when features are enabled'
do
before
do
stub_licensed_features
(
epics:
true
,
issuable_health_status:
true
)
stub_licensed_features
(
issuable_health_status:
true
)
end
it
'succeeds and returns the correct number of issuables updated'
do
expect
(
subject
[
:success
]).
to
be_truthy
expect
(
subject
[
:count
]).
to
eq
(
issuables
.
count
)
expect
(
subject
.
success?
).
to
be_truthy
expect
(
subject
.
payload
[
:count
]).
to
eq
(
issuables
.
count
)
issuables
.
each
do
|
issuable
|
expect
(
issuable
.
reload
.
health_status
).
to
eq
(
'on_track'
)
end
end
context
"when params value is '0'"
do
let
(
:params
)
{
{
issuable_ids:
issuables
.
map
(
&
:id
),
health_status:
'0'
}
}
it
'succeeds and remove values'
do
expect
(
subject
.
success?
).
to
be_truthy
expect
(
subject
.
payload
[
:count
]).
to
eq
(
issuables
.
count
)
issuables
.
each
do
|
issuable
|
issuable
.
reload
expect
(
issuable
.
epic
).
to
eq
(
epic2
)
e
xpect
(
issuable
.
health_status
).
to
eq
(
'on_track'
)
expect
(
issuable
.
health_status
).
to
be_nil
e
nd
end
end
end
context
'when feature
s are
disabled'
do
context
'when feature
issuable_health_status is
disabled'
do
before
do
stub_licensed_features
(
epics:
false
,
issuable_health_status:
false
)
stub_licensed_features
(
issuable_health_status:
false
)
end
it_behaves_like
'does not update issuables attribute'
,
:health_status
it_behaves_like
'does not update issuables attribute'
,
:epic
end
context
'when user can not update issues'
do
...
...
@@ -81,14 +88,6 @@ RSpec.describe Issuable::BulkUpdateService do
end
it_behaves_like
'does not update issuables attribute'
,
:health_status
it_behaves_like
'does not update issuables attribute'
,
:epic
end
context
'when user can not admin epic'
do
let
(
:epic3
)
{
create
(
:epic
,
group:
create
(
:group
))
}
let
(
:params
)
{
{
issuable_ids:
issuables
.
map
(
&
:id
),
epic:
epic3
}
}
it_behaves_like
'does not update issuables attribute'
,
:epic
end
end
end
...
...
@@ -140,8 +139,8 @@ RSpec.describe Issuable::BulkUpdateService do
let
(
:params
)
{
{
issuable_ids:
[
epic1
.
id
,
epic3
.
id
,
outer_epic
.
id
],
add_label_ids:
[
label3
.
id
]
}
}
it
'updates epics that belong to the parent group or descendants'
do
expect
(
subject
[
:success
]
).
to
be_truthy
expect
(
subject
[
:count
]).
to
eq
(
2
)
expect
(
subject
.
success?
).
to
be_truthy
expect
(
subject
.
payload
[
:count
]).
to
eq
(
2
)
expect
(
epic1
.
reload
.
labels
).
to
eq
([
label1
,
label3
])
expect
(
epic3
.
reload
.
labels
).
to
eq
([
label1
,
label3
])
...
...
locale/gitlab.pot
View file @
ce5c8c67
...
...
@@ -11749,6 +11749,9 @@ msgstr ""
msgid "Health information can be retrieved from the following endpoints. More information is available"
msgstr ""
msgid "Health status"
msgstr ""
msgid "HealthCheck|Access token is"
msgstr ""
...
...
@@ -15391,6 +15394,9 @@ msgstr ""
msgid "No start date"
msgstr ""
msgid "No status"
msgstr ""
msgid "No template"
msgstr ""
...
...
spec/services/issuable/bulk_update_service_spec.rb
View file @
ce5c8c67
...
...
@@ -18,8 +18,8 @@ RSpec.describe Issuable::BulkUpdateService do
it
'succeeds'
do
result
=
bulk_update
(
issuables
,
milestone_id:
milestone
.
id
)
expect
(
result
[
:success
]
).
to
be_truthy
expect
(
result
[
:count
]).
to
eq
(
issuables
.
count
)
expect
(
result
.
success?
).
to
be_truthy
expect
(
result
.
payload
[
:count
]).
to
eq
(
issuables
.
count
)
end
it
'updates the issuables milestone'
do
...
...
@@ -121,8 +121,8 @@ RSpec.describe Issuable::BulkUpdateService do
it
'succeeds and returns the correct number of issues updated'
do
result
=
bulk_update
(
issues
,
state_event:
'close'
)
expect
(
result
[
:success
]
).
to
be_truthy
expect
(
result
[
:count
]).
to
eq
(
issues
.
count
)
expect
(
result
.
success?
).
to
be_truthy
expect
(
result
.
payload
[
:count
]).
to
eq
(
issues
.
count
)
end
it
'closes all the issues passed'
do
...
...
@@ -139,8 +139,8 @@ RSpec.describe Issuable::BulkUpdateService do
it
'succeeds and returns the correct number of issues updated'
do
result
=
bulk_update
(
issues
,
state_event:
'reopen'
)
expect
(
result
[
:success
]
).
to
be_truthy
expect
(
result
[
:count
]).
to
eq
(
issues
.
count
)
expect
(
result
.
success?
).
to
be_truthy
expect
(
result
.
payload
[
:count
]).
to
eq
(
issues
.
count
)
end
it
'reopens all the issues passed'
do
...
...
@@ -161,8 +161,8 @@ RSpec.describe Issuable::BulkUpdateService do
result
=
bulk_update
(
merge_request
,
assignee_ids:
[
user
.
id
,
new_assignee
.
id
])
expect
(
result
[
:success
]
).
to
be_truthy
expect
(
result
[
:count
]).
to
eq
(
1
)
expect
(
result
.
success?
).
to
be_truthy
expect
(
result
.
payload
[
:count
]).
to
eq
(
1
)
end
it
'updates the assignee to the user ID passed'
do
...
...
@@ -199,8 +199,8 @@ RSpec.describe Issuable::BulkUpdateService do
result
=
bulk_update
(
issue
,
assignee_ids:
[
new_assignee
.
id
])
expect
(
result
[
:success
]
).
to
be_truthy
expect
(
result
[
:count
]).
to
eq
(
1
)
expect
(
result
.
success?
).
to
be_truthy
expect
(
result
.
payload
[
:count
]).
to
eq
(
1
)
end
it
'updates the assignee to the user ID passed'
do
...
...
@@ -273,8 +273,8 @@ RSpec.describe Issuable::BulkUpdateService do
issue2
=
create
(
:issue
,
project:
create
(
:project
))
result
=
bulk_update
([
issue1
,
issue2
],
assignee_ids:
[
user
.
id
])
expect
(
result
[
:success
]
).
to
be_truthy
expect
(
result
[
:count
]).
to
eq
(
1
)
expect
(
result
.
success?
).
to
be_truthy
expect
(
result
.
payload
[
:count
]).
to
eq
(
1
)
expect
(
issue1
.
reload
.
assignees
).
to
eq
([
user
])
expect
(
issue2
.
reload
.
assignees
).
to
be_empty
...
...
@@ -332,8 +332,8 @@ RSpec.describe Issuable::BulkUpdateService do
milestone
=
create
(
:milestone
,
group:
group
)
result
=
bulk_update
([
issue1
,
issue2
,
issue3
],
milestone_id:
milestone
.
id
)
expect
(
result
[
:success
]
).
to
be_truthy
expect
(
result
[
:count
]).
to
eq
(
2
)
expect
(
result
.
success?
).
to
be_truthy
expect
(
result
.
payload
[
:count
]).
to
eq
(
2
)
expect
(
issue1
.
reload
.
milestone
).
to
eq
(
milestone
)
expect
(
issue2
.
reload
.
milestone
).
to
be_nil
...
...
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