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
0
Merge Requests
0
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
Léo-Paul Géneau
gitlab-ce
Commits
01c9488f
Commit
01c9488f
authored
7 years ago
by
Ryan Scott
Committed by
Sean McGivern
7 years ago
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Added slash command to close an issue as a duplicate. Closes #26372
parent
b6555693
Changes
10
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
219 additions
and
1 deletion
+219
-1
app/models/system_note_metadata.rb
app/models/system_note_metadata.rb
+1
-1
app/services/issuable_base_service.rb
app/services/issuable_base_service.rb
+22
-0
app/services/quick_actions/interpret_service.rb
app/services/quick_actions/interpret_service.rb
+14
-0
app/services/system_note_service.rb
app/services/system_note_service.rb
+19
-0
changelogs/unreleased/26372-duplicate-issue-slash-command.yml
...gelogs/unreleased/26372-duplicate-issue-slash-command.yml
+4
-0
doc/user/project/quick_actions.md
doc/user/project/quick_actions.md
+1
-0
spec/features/issues/user_uses_slash_commands_spec.rb
spec/features/issues/user_uses_slash_commands_spec.rb
+41
-0
spec/services/issues/update_service_spec.rb
spec/services/issues/update_service_spec.rb
+56
-0
spec/services/quick_actions/interpret_service_spec.rb
spec/services/quick_actions/interpret_service_spec.rb
+36
-0
spec/services/system_note_service_spec.rb
spec/services/system_note_service_spec.rb
+25
-0
No files found.
app/models/system_note_metadata.rb
View file @
01c9488f
...
...
@@ -2,7 +2,7 @@ class SystemNoteMetadata < ActiveRecord::Base
ICON_TYPES
=
%w[
commit description merge confidential visible label assignee cross_reference
title time_tracking branch milestone discussion task moved opened closed merged
outdated
outdated
duplicate
]
.
freeze
validates
:note
,
presence:
true
...
...
This diff is collapsed.
Click to expand it.
app/services/issuable_base_service.rb
View file @
01c9488f
...
...
@@ -46,6 +46,14 @@ class IssuableBaseService < BaseService
SystemNoteService
.
change_time_spent
(
issuable
,
issuable
.
project
,
current_user
)
end
def
create_issue_duplicate_note
(
issuable
,
original_issue
)
SystemNoteService
.
mark_duplicate_issue
(
issuable
,
issuable
.
project
,
current_user
,
original_issue
)
end
def
create_cross_reference_note
(
noteable
,
mentioner
)
SystemNoteService
.
cross_reference
(
noteable
,
mentioner
,
current_user
)
end
def
filter_params
(
issuable
)
ability_name
=
:"admin_
#{
issuable
.
to_ability_name
}
"
...
...
@@ -58,6 +66,7 @@ class IssuableBaseService < BaseService
params
.
delete
(
:assignee_ids
)
params
.
delete
(
:assignee_id
)
params
.
delete
(
:due_date
)
params
.
delete
(
:original_issue_id
)
end
filter_assignee
(
issuable
)
...
...
@@ -209,6 +218,7 @@ class IssuableBaseService < BaseService
change_state
(
issuable
)
change_subscription
(
issuable
)
change_todo
(
issuable
)
change_issue_duplicate
(
issuable
)
toggle_award
(
issuable
)
filter_params
(
issuable
)
old_labels
=
issuable
.
labels
.
to_a
...
...
@@ -291,6 +301,18 @@ class IssuableBaseService < BaseService
end
end
def
change_issue_duplicate
(
issuable
)
original_issue_id
=
params
.
delete
(
:original_issue_id
)
return
if
original_issue_id
.
nil?
original_issue
=
IssuesFinder
.
new
(
current_user
).
find
(
original_issue_id
)
if
original_issue
.
present?
create_issue_duplicate_note
(
issuable
,
original_issue
)
close_service
.
new
(
project
,
current_user
,
{}).
execute
(
issuable
)
create_cross_reference_note
(
original_issue
,
issuable
)
end
end
def
toggle_award
(
issuable
)
award
=
params
.
delete
(
:emoji_award
)
if
award
...
...
This diff is collapsed.
Click to expand it.
app/services/quick_actions/interpret_service.rb
View file @
01c9488f
...
...
@@ -471,6 +471,20 @@ module QuickActions
end
end
desc
'Mark this issue as a duplicate of another issue'
params
'#issue'
condition
do
issuable
.
is_a?
(
Issue
)
&&
issuable
.
persisted?
&&
current_user
.
can?
(
:"update_
#{
issuable
.
to_ability_name
}
"
,
issuable
)
end
command
:duplicate
do
|
duplicate_param
|
original_issue
=
extract_references
(
duplicate_param
,
:issue
).
first
if
original_issue
.
present?
&&
original_issue
!=
issuable
@updates
[
:original_issue_id
]
=
original_issue
.
id
end
end
def
extract_users
(
params
)
return
[]
if
params
.
nil?
...
...
This diff is collapsed.
Click to expand it.
app/services/system_note_service.rb
View file @
01c9488f
...
...
@@ -552,6 +552,25 @@ module SystemNoteService
create_note
(
NoteSummary
.
new
(
noteable
,
project
,
author
,
body
,
action:
'moved'
))
end
# Called when a Notable has been marked as a duplicate of another Issue
#
# noteable - Noteable object
# project - Project owning noteable
# author - User performing the change
# original_issue - Issue that this is a duplicate of
#
# Example Note text:
#
# "marked this issue as a duplicate of #1234"
#
# "marked this issue as a duplicate of other_project#5678"
#
# Returns the created Note object
def
mark_duplicate_issue
(
noteable
,
project
,
author
,
original_issue
)
body
=
"marked this issue as a duplicate of
#{
original_issue
.
to_reference
(
project
)
}
"
create_note
(
NoteSummary
.
new
(
noteable
,
project
,
author
,
body
,
action:
'duplicate'
))
end
private
def
notes_for_mentioner
(
mentioner
,
noteable
,
notes
)
...
...
This diff is collapsed.
Click to expand it.
changelogs/unreleased/26372-duplicate-issue-slash-command.yml
0 → 100644
View file @
01c9488f
---
title
:
Added /duplicate slash command to close a duplicate issue
merge_request
:
author
:
Ryan Scott
This diff is collapsed.
Click to expand it.
doc/user/project/quick_actions.md
View file @
01c9488f
...
...
@@ -37,3 +37,4 @@ do.
|
`/target_branch <Branch Name>`
| Set target branch for current merge request |
|
`/award :emoji:`
| Toggle award for :emoji: |
|
`/board_move ~column`
| Move issue to column on the board |
|
`/duplicate #issue`
| Closes this issue and marks it as a duplicate of another issue |
This diff is collapsed.
Click to expand it.
spec/features/issues/user_uses_slash_commands_spec.rb
View file @
01c9488f
...
...
@@ -134,5 +134,46 @@ feature 'Issues > User uses quick actions', feature: true, js: true do
expect
(
page
).
not_to
have_content
'/wip'
end
end
describe
'mark issue as duplicate'
do
let
(
:issue
)
{
create
(
:issue
,
project:
project
)
}
let
(
:original_issue
)
{
create
(
:issue
,
project:
project
)
}
context
'when the current user can update issues'
do
it
'does not create a note, and marks the issue as a duplicate'
do
write_note
(
"/duplicate #
#{
original_issue
.
to_reference
}
"
)
expect
(
page
).
not_to
have_content
"/duplicate
#{
original_issue
.
to_reference
}
"
expect
(
page
).
to
have_content
'Commands applied'
expect
(
page
).
to
have_content
"marked this issue as a duplicate of
#{
original_issue
.
to_reference
}
"
issue
.
reload
expect
(
issue
.
closed?
).
to
be_truthy
end
end
context
'when the current user cannot update the issue'
do
let
(
:guest
)
{
create
(
:user
)
}
before
do
project
.
team
<<
[
guest
,
:guest
]
logout
login_with
(
guest
)
visit
namespace_project_issue_path
(
project
.
namespace
,
project
,
issue
)
end
it
'does not create a note, and does not mark the issue as a duplicate'
do
write_note
(
"/duplicate #
#{
original_issue
.
to_reference
}
"
)
expect
(
page
).
to
have_content
"/duplicate #
#{
original_issue
.
to_reference
}
"
expect
(
page
).
not_to
have_content
'Commands applied'
expect
(
page
).
not_to
have_content
"marked this issue as a duplicate of
#{
original_issue
.
to_reference
}
"
issue
.
reload
expect
(
issue
.
closed?
).
to
be_falsey
end
end
end
end
end
This diff is collapsed.
Click to expand it.
spec/services/issues/update_service_spec.rb
View file @
01c9488f
...
...
@@ -491,6 +491,62 @@ describe Issues::UpdateService, services: true do
include_examples
'updating mentions'
,
Issues
::
UpdateService
end
context
'duplicate issue'
do
let
(
:issues_finder
)
{
spy
(
:issues_finder
)
}
let
(
:close_service
)
{
spy
(
:close_service
)
}
before
do
allow
(
IssuesFinder
).
to
receive
(
:new
).
and_return
(
issues_finder
)
allow
(
Issues
::
CloseService
).
to
receive
(
:new
).
and_return
(
close_service
)
allow
(
SystemNoteService
).
to
receive
(
:cross_reference
)
allow
(
SystemNoteService
).
to
receive
(
:mark_duplicate_issue
)
end
context
'invalid original_issue_id'
do
let
(
:original_issue_id
)
{
double
}
before
{
update_issue
({
original_issue_id:
original_issue_id
})
}
it
'finds the root issue'
do
expect
(
issues_finder
).
to
have_received
(
:find
).
with
(
original_issue_id
)
end
it
'does not close the issue'
do
expect
(
close_service
).
not_to
have_received
(
:execute
)
end
it
'does not create system notes'
do
expect
(
SystemNoteService
).
not_to
have_received
(
:cross_reference
)
expect
(
SystemNoteService
).
not_to
have_received
(
:mark_duplicate_issue
)
end
end
context
'valid original_issue_id'
do
let
(
:original_issue
)
{
create
(
:issue
,
project:
project
)
}
let
(
:original_issue_id
)
{
double
}
before
do
allow
(
issues_finder
).
to
receive
(
:find
).
and_return
(
original_issue
)
update_issue
({
original_issue_id:
original_issue_id
})
end
it
'finds the root issue'
do
expect
(
issues_finder
).
to
have_received
(
:find
).
with
(
original_issue_id
)
end
it
'closes the issue'
do
expect
(
close_service
).
to
have_received
(
:execute
).
with
(
issue
)
end
it
'creates a system note that this issue is a duplicate'
do
expect
(
SystemNoteService
).
to
have_received
(
:mark_duplicate_issue
).
with
(
issue
,
project
,
user
,
original_issue
)
end
it
'creates a cross reference system note in the other issue'
do
expect
(
SystemNoteService
).
to
have_received
(
:cross_reference
).
with
(
original_issue
,
issue
,
user
)
end
end
end
include_examples
'issuable update service'
do
let
(
:open_issuable
)
{
issue
}
let
(
:closed_issuable
)
{
create
(
:closed_issue
,
project:
project
)
}
...
...
This diff is collapsed.
Click to expand it.
spec/services/quick_actions/interpret_service_spec.rb
View file @
01c9488f
...
...
@@ -261,6 +261,17 @@ describe QuickActions::InterpretService, services: true do
end
end
shared_examples
'duplicate command'
do
let
(
:issue_duplicate
)
{
create
(
:issue
,
project:
project
)
}
it
'fetches issue and populates original_issue_id if content contains /duplicate issue_reference'
do
issue_duplicate
# populate the issue
_
,
updates
=
service
.
execute
(
content
,
issuable
)
expect
(
updates
).
to
eq
(
original_issue_id:
issue_duplicate
.
id
)
end
end
it_behaves_like
'reopen command'
do
let
(
:content
)
{
'/reopen'
}
let
(
:issuable
)
{
issue
}
...
...
@@ -644,6 +655,26 @@ describe QuickActions::InterpretService, services: true do
let
(
:issuable
)
{
issue
}
end
it_behaves_like
'duplicate command'
do
let
(
:content
)
{
"/duplicate
#{
issue_duplicate
.
to_reference
}
"
}
let
(
:issuable
)
{
issue
}
end
it_behaves_like
'empty command'
do
let
(
:content
)
{
'/duplicate #{issue.to_reference}'
}
let
(
:issuable
)
{
issue
}
end
it_behaves_like
'empty command'
do
let
(
:content
)
{
'/duplicate'
}
let
(
:issuable
)
{
issue
}
end
it_behaves_like
'empty command'
do
let
(
:content
)
{
'/duplicate imaginary#1234'
}
let
(
:issuable
)
{
issue
}
end
context
'when current_user cannot :admin_issue'
do
let
(
:visitor
)
{
create
(
:user
)
}
let
(
:issue
)
{
create
(
:issue
,
project:
project
,
author:
visitor
)
}
...
...
@@ -693,6 +724,11 @@ describe QuickActions::InterpretService, services: true do
let
(
:content
)
{
'/remove_due_date'
}
let
(
:issuable
)
{
issue
}
end
it_behaves_like
'empty command'
do
let
(
:content
)
{
'/duplicate #{issue.to_reference}'
}
let
(
:issuable
)
{
issue
}
end
end
context
'/award command'
do
...
...
This diff is collapsed.
Click to expand it.
spec/services/system_note_service_spec.rb
View file @
01c9488f
...
...
@@ -1101,4 +1101,29 @@ describe SystemNoteService, services: true do
expect
(
subject
.
note
).
to
include
(
diffs_project_merge_request_url
(
project
,
merge_request
,
diff_id:
diff_id
,
anchor:
line_code
))
end
end
describe
'.mark_duplicate_issue'
do
subject
{
described_class
.
mark_duplicate_issue
(
noteable
,
project
,
author
,
original_issue
)
}
context
'within the same project'
do
let
(
:original_issue
)
{
create
(
:issue
,
project:
project
)
}
it_behaves_like
'a system note'
do
let
(
:action
)
{
'duplicate'
}
end
it
{
expect
(
subject
.
note
).
to
eq
"marked this issue as a duplicate of
#{
original_issue
.
to_reference
}
"
}
end
context
'across different projects'
do
let
(
:other_project
)
{
create
(
:empty_project
)
}
let
(
:original_issue
)
{
create
(
:issue
,
project:
other_project
)
}
it_behaves_like
'a system note'
do
let
(
:action
)
{
'duplicate'
}
end
it
{
expect
(
subject
.
note
).
to
eq
"marked this issue as a duplicate of
#{
original_issue
.
to_reference
(
project
)
}
"
}
end
end
end
This diff is collapsed.
Click to expand it.
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