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
ff7c8182
Commit
ff7c8182
authored
May 15, 2019
by
Alexandru Croitor
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Quick actions for adding/removing epic child relations
https://gitlab.com/gitlab-org/gitlab-ee/issues/7330
parent
221f0060
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
417 additions
and
5 deletions
+417
-5
ee/app/models/ee/epic.rb
ee/app/models/ee/epic.rb
+4
-0
ee/app/services/ee/quick_actions/interpret_service.rb
ee/app/services/ee/quick_actions/interpret_service.rb
+1
-0
ee/changelogs/unreleased/7330-child-epics-quick-actions.yml
ee/changelogs/unreleased/7330-child-epics-quick-actions.yml
+5
-0
ee/lib/ee/gitlab/quick_actions/epic_actions.rb
ee/lib/ee/gitlab/quick_actions/epic_actions.rb
+61
-0
ee/spec/services/groups/autocomplete_service_spec.rb
ee/spec/services/groups/autocomplete_service_spec.rb
+1
-1
ee/spec/services/quick_actions/interpret_service_spec.rb
ee/spec/services/quick_actions/interpret_service_spec.rb
+333
-4
locale/gitlab.pot
locale/gitlab.pot
+12
-0
No files found.
ee/app/models/ee/epic.rb
View file @
ff7c8182
...
...
@@ -283,6 +283,10 @@ module EE
issues
.
any?
||
descendants
.
any?
end
def
child?
(
id
)
children
.
where
(
id:
id
).
exists?
end
def
hierarchy
::
Gitlab
::
ObjectHierarchy
.
new
(
self
.
class
.
where
(
id:
id
))
end
...
...
ee/app/services/ee/quick_actions/interpret_service.rb
View file @
ff7c8182
...
...
@@ -8,6 +8,7 @@ module EE
# as doing so would clear any existing command definitions.
prepended
do
# rubocop: disable Cop/InjectEnterpriseEditionModule
include
EE
::
Gitlab
::
QuickActions
::
EpicActions
include
EE
::
Gitlab
::
QuickActions
::
IssueActions
include
EE
::
Gitlab
::
QuickActions
::
MergeRequestActions
include
EE
::
Gitlab
::
QuickActions
::
IssueAndMergeRequestActions
...
...
ee/changelogs/unreleased/7330-child-epics-quick-actions.yml
0 → 100644
View file @
ff7c8182
---
title
:
Add quick actions for adding and removing child epic relations to epic
merge_request
:
12772
author
:
type
:
added
ee/lib/ee/gitlab/quick_actions/epic_actions.rb
0 → 100644
View file @
ff7c8182
# frozen_string_literal: true
module
EE
module
Gitlab
module
QuickActions
module
EpicActions
extend
ActiveSupport
::
Concern
include
::
Gitlab
::
QuickActions
::
Dsl
included
do
desc
_
(
'Add child epic to an epic'
)
explanation
do
|
epic_param
|
child_epic
=
extract_epic
(
epic_param
)
_
(
"Adds %{epic_ref} as child epic."
)
%
{
epic_ref:
child_epic
.
to_reference
(
quick_action_target
)
}
if
child_epic
end
types
Epic
condition
{
action_allowed?
}
params
'<&epic | group&epic | Epic URL>'
command
:child_epic
do
|
epic_param
|
child_epic
=
extract_epic
(
epic_param
)
if
child_epic
&&
!
quick_action_target
.
child?
(
child_epic
.
id
)
EpicLinks
::
CreateService
.
new
(
quick_action_target
,
current_user
,
{
target_issuable:
child_epic
}).
execute
end
end
desc
_
(
'Remove child epic from an epic'
)
explanation
do
|
epic_param
|
child_epic
=
extract_epic
(
epic_param
)
_
(
"Removes %{epic_ref} from child epics."
)
%
{
epic_ref:
child_epic
.
to_reference
(
quick_action_target
)
}
if
child_epic
end
types
Epic
condition
{
action_allowed?
}
params
'<&epic | group&epic | Epic URL>'
command
:remove_child_epic
do
|
epic_param
|
child_epic
=
extract_epic
(
epic_param
)
if
child_epic
&&
quick_action_target
.
child?
(
child_epic
.
id
)
EpicLinks
::
DestroyService
.
new
(
child_epic
,
current_user
).
execute
end
end
private
def
extract_epic
(
params
)
return
if
params
.
nil?
extract_references
(
params
,
:epic
).
first
end
def
action_allowed?
quick_action_target
.
group
&
.
feature_available?
(
:epics
)
&&
current_user
.
can?
(
:"admin_
#{
quick_action_target
.
to_ability_name
}
"
,
quick_action_target
)
end
end
end
end
end
end
ee/spec/services/groups/autocomplete_service_spec.rb
View file @
ff7c8182
...
...
@@ -103,7 +103,7 @@ describe Groups::AutocompleteService do
it
'returns available commands'
do
expect
(
subject
.
commands
(
epic
).
map
{
|
c
|
c
[
:name
]
})
.
to
match_array
(
[
:todo
,
:unsubscribe
,
:award
,
:shrug
,
:tableflip
,
:cc
,
:title
,
:close
]
[
:todo
,
:unsubscribe
,
:award
,
:shrug
,
:tableflip
,
:cc
,
:title
,
:close
,
:child_epic
,
:remove_child_epic
]
)
end
end
...
...
ee/spec/services/quick_actions/interpret_service_spec.rb
View file @
ff7c8182
...
...
@@ -17,6 +17,18 @@ describe QuickActions::InterpretService do
project
.
add_developer
(
current_user
)
end
shared_examples
'quick action is unavailable'
do
|
action
|
it
'does not recognize action'
do
expect
(
service
.
available_commands
(
target
).
map
{
|
command
|
command
[
:name
]
}).
not_to
include
(
action
)
end
end
shared_examples
'quick action is available'
do
|
action
|
it
'does recognize action'
do
expect
(
service
.
available_commands
(
target
).
map
{
|
command
|
command
[
:name
]
}).
to
include
(
action
)
end
end
describe
'#execute'
do
let
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
)
}
...
...
@@ -197,7 +209,7 @@ describe QuickActions::InterpretService do
end
context
'epic command'
do
let
(
:epic
)
{
create
(
:epic
,
group:
group
)}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let
(
:content
)
{
"/epic
#{
epic
.
to_reference
(
project
)
}
"
}
context
'when epics are enabled'
do
...
...
@@ -286,8 +298,275 @@ describe QuickActions::InterpretService do
end
end
context
'child_epic command'
do
let
(
:subgroup
)
{
create
(
:group
,
parent:
group
)
}
let
(
:another_group
)
{
create
(
:group
)
}
let
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
)
}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let
(
:child_epic
)
{
create
(
:epic
,
group:
group
)
}
let
(
:content
)
{
"/child_epic
#{
child_epic
&
.
to_reference
(
epic
)
}
"
}
shared_examples
'epic relation is not added'
do
it
'does not add child epic to epic'
do
service
.
execute
(
content
,
epic
)
child_epic
.
reload
expect
(
child_epic
.
parent
).
to
be_nil
end
end
shared_examples
'epic relation is added'
do
it
'adds child epic relation to the epic'
do
service
.
execute
(
content
,
epic
)
child_epic
.
reload
expect
(
child_epic
.
parent
).
to
eq
(
epic
)
end
end
context
'when epics are enabled'
do
before
do
stub_licensed_features
(
epics:
true
)
end
context
'when a user does not have permissions to add epic relations'
do
it_behaves_like
'epic relation is not added'
it_behaves_like
'quick action is unavailable'
,
:child_epic
do
let
(
:target
)
{
epic
}
end
end
context
'when a user has permissions to add epic relations'
do
before
do
group
.
add_developer
(
current_user
)
another_group
.
add_developer
(
current_user
)
end
it_behaves_like
'epic relation is added'
it_behaves_like
'quick action is available'
,
:child_epic
do
let
(
:target
)
{
epic
}
end
it_behaves_like
'quick action is unavailable'
,
:child_epic
do
let
(
:target
)
{
issue
}
end
it_behaves_like
'quick action is unavailable'
,
:child_epic
do
let
(
:target
)
{
merge_request
}
end
context
'when passed child epic is nil'
do
let
(
:child_epic
)
{
nil
}
it
'does not add child epic to epic'
do
expect
{
service
.
execute
(
content
,
epic
)
}.
not_to
change
{
epic
.
children
.
count
}
expect
{
service
.
execute
(
content
,
epic
)
}.
not_to
raise_error
end
it
'does not raise error'
do
expect
{
service
.
execute
(
content
,
epic
)
}.
not_to
raise_error
end
end
context
'when child_epic is already linked to an epic'
do
let
(
:another_epic
)
{
create
(
:epic
,
group:
group
)
}
before
do
child_epic
.
update!
(
parent:
another_epic
)
end
it_behaves_like
'epic relation is added'
it_behaves_like
'quick action is available'
,
:child_epic
do
let
(
:target
)
{
epic
}
end
end
context
'when child epic is in a subgroup of parent epic'
do
let
(
:child_epic
)
{
create
(
:epic
,
group:
subgroup
)
}
it_behaves_like
'epic relation is added'
it_behaves_like
'quick action is available'
,
:child_epic
do
let
(
:target
)
{
epic
}
end
end
context
'when child epic is in a parent group of the parent epic'
do
let
(
:child_epic
)
{
create
(
:epic
,
group:
group
)
}
before
do
epic
.
update!
(
group:
subgroup
)
end
it_behaves_like
'epic relation is not added'
it_behaves_like
'quick action is available'
,
:child_epic
do
let
(
:target
)
{
epic
}
end
end
context
'when child epic is in a different group than parent epic'
do
let
(
:child_epic
)
{
create
(
:epic
,
group:
another_group
)
}
it_behaves_like
'epic relation is not added'
it_behaves_like
'quick action is available'
,
:child_epic
do
let
(
:target
)
{
epic
}
end
end
end
end
context
'when epics are disabled'
do
before
do
group
.
add_developer
(
current_user
)
end
it_behaves_like
'epic relation is not added'
it_behaves_like
'quick action is unavailable'
,
:child_epic
do
let
(
:target
)
{
epic
}
end
end
end
context
'remove_child_epic command'
do
let
(
:subgroup
)
{
create
(
:group
,
parent:
group
)
}
let
(
:another_group
)
{
create
(
:group
)
}
let
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
)
}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let!
(
:child_epic
)
{
create
(
:epic
,
group:
group
,
parent:
epic
)
}
let
(
:content
)
{
"/remove_child_epic
#{
child_epic
.
to_reference
(
epic
)
}
"
}
shared_examples
'epic relation is not removed'
do
it
'does not remove child_epic from epic'
do
expect
(
child_epic
.
parent
).
to
eq
(
epic
)
service
.
execute
(
content
,
target
)
child_epic
.
reload
expect
(
child_epic
.
parent
).
to
eq
(
epic
)
end
end
shared_examples
'epic relation is removed'
do
it
'does not remove child_epic from epic'
do
expect
(
child_epic
.
parent
).
to
eq
(
epic
)
service
.
execute
(
content
,
epic
)
child_epic
.
reload
expect
(
child_epic
.
parent
).
to
be_nil
end
end
context
'when epics are enabled'
do
before
do
stub_licensed_features
(
epics:
true
)
epic
.
reload
end
context
'when a user does not have permissions to remove epic relations'
do
it
'does not remove child_epic from epic'
do
expect
(
child_epic
.
parent
).
to
eq
(
epic
)
service
.
execute
(
content
,
epic
)
child_epic
.
reload
expect
(
child_epic
.
parent
).
to
eq
(
epic
)
end
it_behaves_like
'epic relation is not removed'
do
let
(
:target
)
{
epic
}
end
it_behaves_like
'quick action is unavailable'
,
:remove_child_epic
do
let
(
:target
)
{
epic
}
end
end
context
'when a user has permissions to remove epic relations'
do
before
do
group
.
add_developer
(
current_user
)
another_group
.
add_developer
(
current_user
)
end
it_behaves_like
'quick action is available'
,
:remove_child_epic
do
let
(
:target
)
{
epic
}
end
it_behaves_like
'quick action is unavailable'
,
:remove_child_epic
do
let
(
:target
)
{
issue
}
end
it_behaves_like
'quick action is unavailable'
,
:remove_child_epic
do
let
(
:target
)
{
merge_request
}
end
it_behaves_like
'epic relation is removed'
context
'when trying to remove child epic from a different epic'
do
let
(
:another_epic
)
{
create
(
:epic
,
group:
group
)
}
it_behaves_like
'epic relation is not removed'
do
let
(
:target
)
{
another_epic
}
end
end
context
'when child epic is in a subgroup of parent epic'
do
let
(
:child_epic
)
{
create
(
:epic
,
group:
subgroup
,
parent:
epic
)
}
it_behaves_like
'epic relation is removed'
it_behaves_like
'quick action is available'
,
:remove_child_epic
do
let
(
:target
)
{
epic
}
end
end
context
'when child and paretn epics are in different groups'
do
let
(
:child_epic
)
{
create
(
:epic
,
group:
group
,
parent:
epic
)
}
context
'when child epic is in a parent group of the parent epic'
do
before
do
epic
.
update!
(
group:
subgroup
)
end
it_behaves_like
'epic relation is removed'
do
let
(
:target
)
{
epic
}
end
it_behaves_like
'quick action is available'
,
:remove_child_epic
do
let
(
:target
)
{
epic
}
end
end
context
'when child epic is in a different group than parent epic'
do
before
do
epic
.
update!
(
group:
another_group
)
end
it_behaves_like
'epic relation is removed'
do
let
(
:target
)
{
epic
}
end
it_behaves_like
'quick action is available'
,
:remove_child_epic
do
let
(
:target
)
{
epic
}
end
end
end
end
end
context
'when epics are disabled'
do
before
do
group
.
add_developer
(
current_user
)
end
it_behaves_like
'epic relation is not removed'
do
let
(
:target
)
{
epic
}
end
it_behaves_like
'quick action is unavailable'
,
:remove_child_epic
do
let
(
:target
)
{
epic
}
end
end
end
context
'label command for epics'
do
let
(
:epic
)
{
create
(
:epic
,
group:
group
)}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let
(
:label
)
{
create
(
:group_label
,
title:
'bug'
,
group:
group
)
}
let
(
:project_label
)
{
create
(
:label
,
title:
'project_label'
)
}
let
(
:content
)
{
"/label ~
#{
label
.
title
}
~
#{
project_label
.
title
}
"
}
...
...
@@ -332,7 +611,7 @@ describe QuickActions::InterpretService do
end
context
'remove_epic command'
do
let
(
:epic
)
{
create
(
:epic
,
group:
group
)}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let
(
:content
)
{
"/remove_epic
#{
epic
.
to_reference
(
project
)
}
"
}
before
do
...
...
@@ -409,7 +688,7 @@ describe QuickActions::InterpretService do
it_behaves_like
'weight command'
do
let
(
:weight
)
{
5
}
let
(
:content
)
{
"/weight
#{
weight
}
"
}
let
(
:content
)
{
"/weight
#{
weight
}
"
}
let
(
:issuable
)
{
issue
}
end
...
...
@@ -518,5 +797,55 @@ describe QuickActions::InterpretService do
expect
(
explanations
).
to
eq
([
'Sets weight to 4.'
])
end
end
context
'epic commands'
do
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let
(
:child_epic
)
{
create
(
:epic
,
group:
group
)
}
before
do
stub_licensed_features
(
epics:
true
)
group
.
add_developer
(
current_user
)
end
context
'child_epic command'
do
context
'when correct epic reference'
do
let
(
:content
)
{
"/child_epic
#{
child_epic
&
.
to_reference
(
epic
)
}
"
}
it
'returns message with epic reference'
do
_
,
explanations
=
service
.
explain
(
content
,
epic
)
expect
(
explanations
).
to
eq
([
"Adds
#{
child_epic
.
group
.
name
}
&
#{
child_epic
.
iid
}
as child epic."
])
end
end
context
'when epic reference is wrong'
do
let
(
:content
)
{
"/child_epic qwe"
}
it
'returns empty explain message'
do
_
,
explanations
=
service
.
explain
(
content
,
epic
)
expect
(
explanations
).
to
eq
([])
end
end
end
context
'remove_child_epic command'
do
context
'when correct epic reference'
do
let
(
:content
)
{
"/remove_child_epic
#{
child_epic
&
.
to_reference
(
epic
)
}
"
}
it
'returns message with epic reference'
do
_
,
explanations
=
service
.
explain
(
content
,
epic
)
expect
(
explanations
).
to
eq
([
"Removes
#{
child_epic
.
group
.
name
}
&
#{
child_epic
.
iid
}
from child epics."
])
end
end
context
'when epic reference is wrong'
do
let
(
:content
)
{
"/child_epic qwe"
}
it
'returns empty explain message'
do
_
,
explanations
=
service
.
explain
(
content
,
epic
)
expect
(
explanations
).
to
eq
([])
end
end
end
end
end
end
locale/gitlab.pot
View file @
ff7c8182
...
...
@@ -663,6 +663,9 @@ msgstr ""
msgid "Add bold text"
msgstr ""
msgid "Add child epic to an epic"
msgstr ""
msgid "Add comment now"
msgstr ""
...
...
@@ -735,6 +738,9 @@ msgstr ""
msgid "Adds"
msgstr ""
msgid "Adds %{epic_ref} as child epic."
msgstr ""
msgid "Adds a todo."
msgstr ""
...
...
@@ -10403,6 +10409,9 @@ msgstr ""
msgid "Remove avatar"
msgstr ""
msgid "Remove child epic from an epic"
msgstr ""
msgid "Remove due date"
msgstr ""
...
...
@@ -10445,6 +10454,9 @@ msgstr ""
msgid "Removed projects cannot be restored!"
msgstr ""
msgid "Removes %{epic_ref} from child epics."
msgstr ""
msgid "Removes %{milestone_reference} milestone."
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