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
...
@@ -283,6 +283,10 @@ module EE
issues
.
any?
||
descendants
.
any?
issues
.
any?
||
descendants
.
any?
end
end
def
child?
(
id
)
children
.
where
(
id:
id
).
exists?
end
def
hierarchy
def
hierarchy
::
Gitlab
::
ObjectHierarchy
.
new
(
self
.
class
.
where
(
id:
id
))
::
Gitlab
::
ObjectHierarchy
.
new
(
self
.
class
.
where
(
id:
id
))
end
end
...
...
ee/app/services/ee/quick_actions/interpret_service.rb
View file @
ff7c8182
...
@@ -8,6 +8,7 @@ module EE
...
@@ -8,6 +8,7 @@ module EE
# as doing so would clear any existing command definitions.
# as doing so would clear any existing command definitions.
prepended
do
prepended
do
# rubocop: disable Cop/InjectEnterpriseEditionModule
# rubocop: disable Cop/InjectEnterpriseEditionModule
include
EE
::
Gitlab
::
QuickActions
::
EpicActions
include
EE
::
Gitlab
::
QuickActions
::
IssueActions
include
EE
::
Gitlab
::
QuickActions
::
IssueActions
include
EE
::
Gitlab
::
QuickActions
::
MergeRequestActions
include
EE
::
Gitlab
::
QuickActions
::
MergeRequestActions
include
EE
::
Gitlab
::
QuickActions
::
IssueAndMergeRequestActions
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
...
@@ -103,7 +103,7 @@ describe Groups::AutocompleteService do
it
'returns available commands'
do
it
'returns available commands'
do
expect
(
subject
.
commands
(
epic
).
map
{
|
c
|
c
[
:name
]
})
expect
(
subject
.
commands
(
epic
).
map
{
|
c
|
c
[
:name
]
})
.
to
match_array
(
.
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
end
end
...
...
ee/spec/services/quick_actions/interpret_service_spec.rb
View file @
ff7c8182
...
@@ -17,6 +17,18 @@ describe QuickActions::InterpretService do
...
@@ -17,6 +17,18 @@ describe QuickActions::InterpretService do
project
.
add_developer
(
current_user
)
project
.
add_developer
(
current_user
)
end
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
describe
'#execute'
do
let
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
)
}
let
(
:merge_request
)
{
create
(
:merge_request
,
source_project:
project
)
}
...
@@ -197,7 +209,7 @@ describe QuickActions::InterpretService do
...
@@ -197,7 +209,7 @@ describe QuickActions::InterpretService do
end
end
context
'epic command'
do
context
'epic command'
do
let
(
:epic
)
{
create
(
:epic
,
group:
group
)}
let
(
:epic
)
{
create
(
:epic
,
group:
group
)
}
let
(
:content
)
{
"/epic
#{
epic
.
to_reference
(
project
)
}
"
}
let
(
:content
)
{
"/epic
#{
epic
.
to_reference
(
project
)
}
"
}
context
'when epics are enabled'
do
context
'when epics are enabled'
do
...
@@ -286,8 +298,275 @@ describe QuickActions::InterpretService do
...
@@ -286,8 +298,275 @@ describe QuickActions::InterpretService do
end
end
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
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
(
:label
)
{
create
(
:group_label
,
title:
'bug'
,
group:
group
)
}
let
(
:project_label
)
{
create
(
:label
,
title:
'project_label'
)
}
let
(
:project_label
)
{
create
(
:label
,
title:
'project_label'
)
}
let
(
:content
)
{
"/label ~
#{
label
.
title
}
~
#{
project_label
.
title
}
"
}
let
(
:content
)
{
"/label ~
#{
label
.
title
}
~
#{
project_label
.
title
}
"
}
...
@@ -332,7 +611,7 @@ describe QuickActions::InterpretService do
...
@@ -332,7 +611,7 @@ describe QuickActions::InterpretService do
end
end
context
'remove_epic command'
do
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
)
}
"
}
let
(
:content
)
{
"/remove_epic
#{
epic
.
to_reference
(
project
)
}
"
}
before
do
before
do
...
@@ -409,7 +688,7 @@ describe QuickActions::InterpretService do
...
@@ -409,7 +688,7 @@ describe QuickActions::InterpretService do
it_behaves_like
'weight command'
do
it_behaves_like
'weight command'
do
let
(
:weight
)
{
5
}
let
(
:weight
)
{
5
}
let
(
:content
)
{
"/weight
#{
weight
}
"
}
let
(
:content
)
{
"/weight
#{
weight
}
"
}
let
(
:issuable
)
{
issue
}
let
(
:issuable
)
{
issue
}
end
end
...
@@ -518,5 +797,55 @@ describe QuickActions::InterpretService do
...
@@ -518,5 +797,55 @@ describe QuickActions::InterpretService do
expect
(
explanations
).
to
eq
([
'Sets weight to 4.'
])
expect
(
explanations
).
to
eq
([
'Sets weight to 4.'
])
end
end
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
end
end
locale/gitlab.pot
View file @
ff7c8182
...
@@ -663,6 +663,9 @@ msgstr ""
...
@@ -663,6 +663,9 @@ msgstr ""
msgid "Add bold text"
msgid "Add bold text"
msgstr ""
msgstr ""
msgid "Add child epic to an epic"
msgstr ""
msgid "Add comment now"
msgid "Add comment now"
msgstr ""
msgstr ""
...
@@ -735,6 +738,9 @@ msgstr ""
...
@@ -735,6 +738,9 @@ msgstr ""
msgid "Adds"
msgid "Adds"
msgstr ""
msgstr ""
msgid "Adds %{epic_ref} as child epic."
msgstr ""
msgid "Adds a todo."
msgid "Adds a todo."
msgstr ""
msgstr ""
...
@@ -10403,6 +10409,9 @@ msgstr ""
...
@@ -10403,6 +10409,9 @@ msgstr ""
msgid "Remove avatar"
msgid "Remove avatar"
msgstr ""
msgstr ""
msgid "Remove child epic from an epic"
msgstr ""
msgid "Remove due date"
msgid "Remove due date"
msgstr ""
msgstr ""
...
@@ -10445,6 +10454,9 @@ msgstr ""
...
@@ -10445,6 +10454,9 @@ msgstr ""
msgid "Removed projects cannot be restored!"
msgid "Removed projects cannot be restored!"
msgstr ""
msgstr ""
msgid "Removes %{epic_ref} from child epics."
msgstr ""
msgid "Removes %{milestone_reference} milestone."
msgid "Removes %{milestone_reference} milestone."
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