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
9b13ce0b
Commit
9b13ce0b
authored
Mar 18, 2016
by
Grzegorz Bizon
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improvements in issue move feaure (refactoring)
According to endbosses' suggestions.
parent
fcf10689
Changes
11
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
105 additions
and
142 deletions
+105
-142
app/controllers/projects/issues_controller.rb
app/controllers/projects/issues_controller.rb
+4
-4
app/models/concerns/issuable.rb
app/models/concerns/issuable.rb
+9
-0
app/services/issues/create_service.rb
app/services/issues/create_service.rb
+2
-2
app/services/issues/move_service.rb
app/services/issues/move_service.rb
+37
-50
app/views/shared/issuable/_form.html.haml
app/views/shared/issuable/_form.html.haml
+12
-12
db/migrate/20160317092222_add_moved_to_to_issue.rb
db/migrate/20160317092222_add_moved_to_to_issue.rb
+1
-1
db/schema.rb
db/schema.rb
+0
-1
lib/gitlab/gfm/reference_unfolder.rb
lib/gitlab/gfm/reference_unfolder.rb
+6
-19
lib/gitlab/reference_extractor.rb
lib/gitlab/reference_extractor.rb
+10
-0
spec/lib/gitlab/reference_extractor_spec.rb
spec/lib/gitlab/reference_extractor_spec.rb
+5
-0
spec/services/issues/move_service_spec.rb
spec/services/issues/move_service_spec.rb
+19
-53
No files found.
app/controllers/projects/issues_controller.rb
View file @
9b13ce0b
...
@@ -88,11 +88,11 @@ class Projects::IssuesController < Projects::ApplicationController
...
@@ -88,11 +88,11 @@ class Projects::IssuesController < Projects::ApplicationController
def
update
def
update
@issue
=
Issues
::
UpdateService
.
new
(
project
,
current_user
,
issue_params
).
execute
(
issue
)
@issue
=
Issues
::
UpdateService
.
new
(
project
,
current_user
,
issue_params
).
execute
(
issue
)
move_service
=
Issues
::
MoveService
.
new
(
project
,
current_user
,
issue_params
,
@issue
,
params
[
'move_to_project_id'
])
if
move_service
.
move?
if
params
[
:move_to_project_id
].
to_i
>
0
@issue
=
move_service
.
execute
new_project
=
Project
.
find
(
params
[
:move_to_project_id
])
move_service
=
Issues
::
MoveService
.
new
(
project
,
current_user
,
issue_params
)
@issue
=
move_service
.
execute
(
@issue
,
new_project
)
end
end
respond_to
do
|
format
|
respond_to
do
|
format
|
...
...
app/models/concerns/issuable.rb
View file @
9b13ce0b
...
@@ -209,4 +209,13 @@ module Issuable
...
@@ -209,4 +209,13 @@ module Issuable
Taskable
.
get_updated_tasks
(
old_content:
previous_changes
[
'description'
].
first
,
Taskable
.
get_updated_tasks
(
old_content:
previous_changes
[
'description'
].
first
,
new_content:
description
)
new_content:
description
)
end
end
##
# Method that checks if issuable can be moved to another project.
#
# Should be overridden if issuable can be moved.
#
def
can_move?
(
*
)
false
end
end
end
app/services/issues/create_service.rb
View file @
9b13ce0b
module
Issues
module
Issues
class
CreateService
<
Issues
::
BaseService
class
CreateService
<
Issues
::
BaseService
def
execute
(
set_author:
true
)
def
execute
filter_params
filter_params
label_params
=
params
[
:label_ids
]
label_params
=
params
[
:label_ids
]
issue
=
project
.
issues
.
new
(
params
.
except
(
:label_ids
))
issue
=
project
.
issues
.
new
(
params
.
except
(
:label_ids
))
issue
.
author
=
current_user
if
set_autho
r
issue
.
author
=
params
[
:author
]
||
current_use
r
if
issue
.
save
if
issue
.
save
issue
.
update_attributes
(
label_ids:
label_params
)
issue
.
update_attributes
(
label_ids:
label_params
)
...
...
app/services/issues/move_service.rb
View file @
9b13ce0b
module
Issues
module
Issues
class
MoveService
<
Issues
::
BaseService
class
MoveService
<
Issues
::
BaseService
def
initialize
(
project
,
current_user
,
params
,
issue
,
new_project_id
)
class
MoveError
<
StandardError
;
end
super
(
project
,
current_user
,
params
)
@issue_old
=
issue
def
execute
(
issue
,
new_project
)
@issue_new
=
nil
@old_issue
=
issue
@project_old
=
@project
@old_project
=
@project
@new_project
=
new_project
if
new_project_id
.
to_i
>
0
unless
issue
.
can_move?
(
current_user
,
new_project
)
@project_new
=
Project
.
find
(
new_project_id
)
raise
MoveError
,
'Cannot move issue due to insufficient permissions!'
end
end
if
@project
_new
==
@project_old
if
@project
==
new_project
raise
Standard
Error
,
'Cannot move issue to project it originates from!'
raise
Move
Error
,
'Cannot move issue to project it originates from!'
end
end
end
def
execute
return
unless
move?
# Using transaction because of a high resources footprint
# Using transaction because of a high resources footprint
# on rewriting notes (unfolding references)
# on rewriting notes (unfolding references)
...
@@ -25,81 +21,72 @@ module Issues
...
@@ -25,81 +21,72 @@ module Issues
ActiveRecord
::
Base
.
transaction
do
ActiveRecord
::
Base
.
transaction
do
# New issue tasks
# New issue tasks
#
#
create_new_issue
@new_issue
=
create_new_issue
rewrite_notes
rewrite_notes
add_
moved_from_note
add_
note_moved_from
# Old issue tasks
# Old issue tasks
#
#
add_
moved_to_note
add_
note_moved_to
close_
old_
issue
close_issue
mark_as_moved
mark_as_moved
end
end
notify_participants
notify_participants
@issue_new
@new_issue
end
def
move?
@project_new
&&
can_move?
end
end
private
private
def
can_move?
@issue_old
.
can_move?
(
@current_user
)
&&
@issue_old
.
can_move?
(
@current_user
,
@project_new
)
end
def
create_new_issue
def
create_new_issue
new_params
=
{
id:
nil
,
iid:
nil
,
milestone:
nil
,
label_ids:
[],
new_params
=
{
id:
nil
,
iid:
nil
,
label_ids:
[],
milestone:
nil
,
project:
@project_new
,
author:
@issue_old
.
author
,
project:
@new_project
,
author:
@old_issue
.
author
,
description:
unfold_references
(
@issue_old
.
description
)
}
description:
unfold_references
(
@old_issue
.
description
)
}
new_params
=
@issue_old
.
serializable_hash
.
merge
(
new_params
)
create_service
=
CreateService
.
new
(
@project_new
,
@current_user
,
new_params
)
@issue_new
=
create_service
.
execute
(
set_author:
false
)
new_params
=
@old_issue
.
serializable_hash
.
merge
(
new_params
)
CreateService
.
new
(
@new_project
,
@current_user
,
new_params
).
execute
end
end
def
rewrite_notes
def
rewrite_notes
@
issue_old
.
notes
.
find_each
do
|
note
|
@
old_issue
.
notes
.
find_each
do
|
note
|
new_note
=
note
.
dup
new_note
=
note
.
dup
new_params
=
{
project:
@
project_new
,
noteable:
@issue_new
,
new_params
=
{
project:
@
new_project
,
noteable:
@new_issue
,
note:
unfold_references
(
new_note
.
note
)
}
note:
unfold_references
(
new_note
.
note
)
}
new_note
.
update
(
new_params
)
new_note
.
update
(
new_params
)
end
end
end
end
def
close_
old_
issue
def
close_issue
close_service
=
CloseService
.
new
(
@
project_new
,
@current_user
)
close_service
=
CloseService
.
new
(
@
old_project
,
@current_user
)
close_service
.
execute
(
@
issue_old
,
notifications:
false
,
system_note:
false
)
close_service
.
execute
(
@
old_issue
,
notifications:
false
,
system_note:
false
)
end
end
def
add_moved_from_note
def
add_note_moved_from
SystemNoteService
.
noteable_moved
(
@issue_new
,
@project_new
,
SystemNoteService
.
noteable_moved
(
@new_issue
,
@new_project
,
@issue_old
,
@current_user
,
direction: :from
)
@old_issue
,
@current_user
,
direction: :from
)
end
end
def
add_moved_to_note
def
add_note_moved_to
SystemNoteService
.
noteable_moved
(
@issue_old
,
@project_old
,
SystemNoteService
.
noteable_moved
(
@old_issue
,
@old_project
,
@issue_new
,
@current_user
,
direction: :to
)
@new_issue
,
@current_user
,
direction: :to
)
end
end
def
unfold_references
(
content
)
def
unfold_references
(
content
)
unfolder
=
Gitlab
::
Gfm
::
ReferenceUnfolder
.
new
(
content
,
@
project_old
)
unfolder
=
Gitlab
::
Gfm
::
ReferenceUnfolder
.
new
(
content
,
@
old_project
)
unfolder
.
unfold
(
@
project_new
)
unfolder
.
unfold
(
@
new_project
)
end
end
def
notify_participants
def
notify_participants
notification_service
.
issue_moved
(
@
issue_old
,
@issue_new
,
@current_user
)
notification_service
.
issue_moved
(
@
old_issue
,
@new_issue
,
@current_user
)
end
end
def
mark_as_moved
def
mark_as_moved
@
issue_old
.
update
(
moved_to:
@issue_new
)
@
old_issue
.
update
(
moved_to:
@new_issue
)
end
end
end
end
end
end
app/views/shared/issuable/_form.html.haml
View file @
9b13ce0b
...
@@ -67,22 +67,22 @@
...
@@ -67,22 +67,22 @@
-
if
can?
current_user
,
:admin_label
,
issuable
.
project
-
if
can?
current_user
,
:admin_label
,
issuable
.
project
=
link_to
'Create new label'
,
new_namespace_project_label_path
(
issuable
.
project
.
namespace
,
issuable
.
project
),
target: :blank
=
link_to
'Create new label'
,
new_namespace_project_label_path
(
issuable
.
project
.
namespace
,
issuable
.
project
),
target: :blank
-
if
issuable
.
is_a?
(
Issue
)
&&
issuable
.
can_move?
(
current_user
)
-
if
issuable
.
can_move?
(
current_user
)
%hr
%hr
.form-group
.form-group
=
label_tag
:move_to_project_id
,
'Move'
,
class:
'control-label'
=
label_tag
:move_to_project_id
,
'Move'
,
class:
'control-label'
.col-sm-10
.col-sm-10
-
projects
=
project_options
(
issuable
,
current_user
,
ability: :admin_issue
)
-
projects
=
project_options
(
issuable
,
current_user
,
ability: :admin_issue
)
=
select_tag
(
:move_to_project_id
,
projects
,
include_blank:
true
,
=
select_tag
(
:move_to_project_id
,
projects
,
include_blank:
true
,
class:
'select2'
,
data:
{
placeholder:
'Select project'
})
class:
'select2'
,
data:
{
placeholder:
'Select project'
})
-
if
issuable
.
is_a?
(
MergeRequest
)
-
if
issuable
.
is_a?
(
MergeRequest
)
%hr
%hr
-
if
@merge_request
.
new_record?
-
if
@merge_request
.
new_record?
.form-group
.form-group
=
f
.
label
:source_branch
,
class:
'control-label'
=
f
.
label
:source_branch
,
class:
'control-label'
.col-sm-10
.col-sm-10
=
f
.
select
(
:source_branch
,
[
@merge_request
.
source_branch
],
{
},
{
class:
'source_branch select2 span2'
,
disabled:
true
})
=
f
.
select
(
:source_branch
,
[
@merge_request
.
source_branch
],
{
},
{
class:
'source_branch select2 span2'
,
disabled:
true
})
.form-group
.form-group
=
f
.
label
:target_branch
,
class:
'control-label'
=
f
.
label
:target_branch
,
class:
'control-label'
.col-sm-10
.col-sm-10
...
...
db/migrate/20160317092222_add_moved_to_to_issue.rb
View file @
9b13ce0b
class
AddMovedToToIssue
<
ActiveRecord
::
Migration
class
AddMovedToToIssue
<
ActiveRecord
::
Migration
def
change
def
change
add_reference
:issues
,
:moved_to
,
references: :issues
,
index:
true
add_reference
:issues
,
:moved_to
,
references: :issues
end
end
end
end
db/schema.rb
View file @
9b13ce0b
...
@@ -425,7 +425,6 @@ ActiveRecord::Schema.define(version: 20160317092222) do
...
@@ -425,7 +425,6 @@ ActiveRecord::Schema.define(version: 20160317092222) do
add_index
"issues"
,
[
"created_at"
],
name:
"index_issues_on_created_at"
,
using: :btree
add_index
"issues"
,
[
"created_at"
],
name:
"index_issues_on_created_at"
,
using: :btree
add_index
"issues"
,
[
"description"
],
name:
"index_issues_on_description_trigram"
,
using: :gin
,
opclasses:
{
"description"
=>
"gin_trgm_ops"
}
add_index
"issues"
,
[
"description"
],
name:
"index_issues_on_description_trigram"
,
using: :gin
,
opclasses:
{
"description"
=>
"gin_trgm_ops"
}
add_index
"issues"
,
[
"milestone_id"
],
name:
"index_issues_on_milestone_id"
,
using: :btree
add_index
"issues"
,
[
"milestone_id"
],
name:
"index_issues_on_milestone_id"
,
using: :btree
add_index
"issues"
,
[
"moved_to_id"
],
name:
"index_issues_on_moved_to_id"
,
using: :btree
add_index
"issues"
,
[
"project_id"
,
"iid"
],
name:
"index_issues_on_project_id_and_iid"
,
unique:
true
,
using: :btree
add_index
"issues"
,
[
"project_id"
,
"iid"
],
name:
"index_issues_on_project_id_and_iid"
,
unique:
true
,
using: :btree
add_index
"issues"
,
[
"project_id"
],
name:
"index_issues_on_project_id"
,
using: :btree
add_index
"issues"
,
[
"project_id"
],
name:
"index_issues_on_project_id"
,
using: :btree
add_index
"issues"
,
[
"state"
],
name:
"index_issues_on_state"
,
using: :btree
add_index
"issues"
,
[
"state"
],
name:
"index_issues_on_state"
,
using: :btree
...
...
lib/gitlab/gfm/reference_unfolder.rb
View file @
9b13ce0b
...
@@ -7,7 +7,9 @@ module Gitlab
...
@@ -7,7 +7,9 @@ module Gitlab
# in context of.
# in context of.
#
#
# `unfold` method tries to find all local references and unfold each of
# `unfold` method tries to find all local references and unfold each of
# those local references to cross reference format.
# those local references to cross reference format, assuming that the
# argument passed to this method is a project that references will be
# viewed from (see `Referable#to_reference method).
#
#
# Examples:
# Examples:
#
#
...
@@ -34,17 +36,12 @@ module Gitlab
...
@@ -34,17 +36,12 @@ module Gitlab
end
end
def
unfold
(
from_project
)
def
unfold
(
from_project
)
return
@text
unless
@text
=~
references_pattern
pattern
=
Gitlab
::
ReferenceExtractor
.
references_pattern
return
@text
unless
@text
=~
pattern
unfolded
=
@text
.
gsub
(
references_
pattern
)
do
|
reference
|
@text
.
gsub
(
pattern
)
do
|
reference
|
unfold_reference
(
reference
,
Regexp
.
last_match
,
from_project
)
unfold_reference
(
reference
,
Regexp
.
last_match
,
from_project
)
end
end
unless
substitution_valid?
(
unfolded
)
raise
StandardError
,
'Invalid references unfolding!'
end
unfolded
end
end
private
private
...
@@ -61,16 +58,6 @@ module Gitlab
...
@@ -61,16 +58,6 @@ module Gitlab
substitution_valid?
(
new_text
)
?
cross_reference
:
reference
substitution_valid?
(
new_text
)
?
cross_reference
:
reference
end
end
def
references_pattern
return
@pattern
if
@pattern
patterns
=
Gitlab
::
ReferenceExtractor
::
REFERABLES
.
map
do
|
ref
|
ref
.
to_s
.
classify
.
constantize
.
try
(
:reference_pattern
)
end
@pattern
=
Regexp
.
union
(
patterns
.
compact
)
end
def
referables
def
referables
return
@referables
if
@referables
return
@referables
if
@referables
...
...
lib/gitlab/reference_extractor.rb
View file @
9b13ce0b
...
@@ -37,6 +37,16 @@ module Gitlab
...
@@ -37,6 +37,16 @@ module Gitlab
@references
.
values
.
flatten
@references
.
values
.
flatten
end
end
def
self
.
references_pattern
return
@pattern
if
@pattern
patterns
=
REFERABLES
.
map
do
|
ref
|
ref
.
to_s
.
classify
.
constantize
.
try
(
:reference_pattern
)
end
@pattern
=
Regexp
.
union
(
patterns
.
compact
)
end
private
private
def
reference_context
def
reference_context
...
...
spec/lib/gitlab/reference_extractor_spec.rb
View file @
9b13ce0b
...
@@ -134,4 +134,9 @@ describe Gitlab::ReferenceExtractor, lib: true do
...
@@ -134,4 +134,9 @@ describe Gitlab::ReferenceExtractor, lib: true do
expect
(
subject
.
all
).
to
match_array
([
issue
,
label
])
expect
(
subject
.
all
).
to
match_array
([
issue
,
label
])
end
end
end
end
describe
'.references_pattern'
do
subject
{
described_class
.
references_pattern
}
it
{
is_expected
.
to
be_kind_of
Regexp
}
end
end
end
spec/services/issues/move_service_spec.rb
View file @
9b13ce0b
...
@@ -15,11 +15,7 @@ describe Issues::MoveService, services: true do
...
@@ -15,11 +15,7 @@ describe Issues::MoveService, services: true do
end
end
let
(
:move_service
)
do
let
(
:move_service
)
do
described_class
.
new
(
old_project
,
user
,
issue_params
,
old_issue
,
new_project_id
)
described_class
.
new
(
old_project
,
user
,
issue_params
)
end
shared_context
'issue move requested'
do
let
(
:new_project_id
)
{
new_project
.
id
}
end
end
shared_context
'user can move issue'
do
shared_context
'user can move issue'
do
...
@@ -29,19 +25,13 @@ describe Issues::MoveService, services: true do
...
@@ -29,19 +25,13 @@ describe Issues::MoveService, services: true do
end
end
end
end
context
'issue movable'
do
describe
'#execute'
do
include_context
'issue move requested'
shared_context
'issue move executed'
do
include_context
'user can move issue'
let!
(
:new_issue
)
{
move_service
.
execute
(
old_issue
,
new_project
)
}
describe
'#move?'
do
subject
{
move_service
.
move?
}
it
{
is_expected
.
to
be_truthy
}
end
end
describe
'#execute'
do
context
'issue movable'
do
shared_context
'issue move executed'
do
include_context
'user can move issue'
let!
(
:new_issue
)
{
move_service
.
execute
}
end
context
'generic issue'
do
context
'generic issue'
do
include_context
'issue move executed'
include_context
'issue move executed'
...
@@ -164,57 +154,33 @@ describe Issues::MoveService, services: true do
...
@@ -164,57 +154,33 @@ describe Issues::MoveService, services: true do
end
end
end
end
end
end
end
end
context
'moving to same project'
do
let
(
:new_project
)
{
old_project
}
include_context
'issue move requested'
include_context
'user can move issue'
it
'raises error'
do
expect
{
move_service
}
.
to
raise_error
(
StandardError
,
/Cannot move issue/
)
end
end
context
'issue move not requested'
do
let
(
:new_project_id
)
{
nil
}
describe
'#move?'
do
context
'moving to same project'
do
subject
{
move_service
.
move?
}
let
(
:new_project
)
{
old_project
}
context
'user do not have permissions to move issue'
do
it
{
is_expected
.
to
be_falsey
}
end
context
'user has permissions to move issue'
do
it
'raises error'
do
include_context
'user can move issue'
expect
{
move_service
.
execute
(
old_issue
,
new_project
)
}
it
{
is_expected
.
to
be_falsey
}
.
to
raise_error
(
StandardError
,
/Cannot move issue/
)
end
end
end
end
end
end
describe
'move permissions'
do
include_context
'issue move requested'
describe
'
#move?
'
do
describe
'
move permissions
'
do
subject
{
move_service
.
move?
}
let
(
:move
)
{
move_service
.
execute
(
old_issue
,
new_project
)
}
context
'user is reporter in both projects'
do
context
'user is reporter in both projects'
do
include_context
'user can move issue'
include_context
'user can move issue'
it
{
is_expected
.
to
be_truthy
}
it
{
expect
{
move
}.
to_not
raise_error
}
end
end
context
'user is reporter only in new project'
do
context
'user is reporter only in new project'
do
before
{
new_project
.
team
<<
[
user
,
:reporter
]
}
before
{
new_project
.
team
<<
[
user
,
:reporter
]
}
it
{
is_expected
.
to
be_falsey
}
it
{
expect
{
move
}.
to
raise_error
(
StandardError
,
/permissions/
)
}
end
end
context
'user is reporter only in old project'
do
context
'user is reporter only in old project'
do
before
{
old_project
.
team
<<
[
user
,
:reporter
]
}
before
{
old_project
.
team
<<
[
user
,
:reporter
]
}
it
{
is_expected
.
to
be_falsey
}
it
{
expect
{
move
}.
to
raise_error
(
StandardError
,
/permissions/
)
}
end
end
context
'user is reporter in one project and guest in another'
do
context
'user is reporter in one project and guest in another'
do
...
@@ -223,7 +189,7 @@ describe Issues::MoveService, services: true do
...
@@ -223,7 +189,7 @@ describe Issues::MoveService, services: true do
old_project
.
team
<<
[
user
,
:reporter
]
old_project
.
team
<<
[
user
,
:reporter
]
end
end
it
{
is_expected
.
to
be_falsey
}
it
{
expect
{
move
}.
to
raise_error
(
StandardError
,
/permissions/
)
}
end
end
context
'issue has already been moved'
do
context
'issue has already been moved'
do
...
@@ -236,7 +202,7 @@ describe Issues::MoveService, services: true do
...
@@ -236,7 +202,7 @@ describe Issues::MoveService, services: true do
moved_to:
moved_to_issue
)
moved_to:
moved_to_issue
)
end
end
it
{
is_expected
.
to
be_falsey
}
it
{
expect
{
move
}.
to
raise_error
(
StandardError
,
/permissions/
)
}
end
end
end
end
end
end
...
...
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