Commit 231497bb authored by Heinrich Lee Yu's avatar Heinrich Lee Yu

Merge branch 'issue_337189-block_move_and_clone_of_requirement_issues' into 'master'

Block move and clone of requirement issues

See merge request gitlab-org/gitlab!70439
parents a0e8e0c7 3c18b544
...@@ -12,7 +12,8 @@ module IssueAvailableFeatures ...@@ -12,7 +12,8 @@ module IssueAvailableFeatures
{ {
assignee: %w(issue incident), assignee: %w(issue incident),
confidentiality: %w(issue incident), confidentiality: %w(issue incident),
time_tracking: %w(issue incident) time_tracking: %w(issue incident),
move_and_clone: %w(issue incident)
}.with_indifferent_access }.with_indifferent_access
end end
end end
......
...@@ -542,6 +542,10 @@ class Issue < ApplicationRecord ...@@ -542,6 +542,10 @@ class Issue < ApplicationRecord
issue_type_supports?(:time_tracking) issue_type_supports?(:time_tracking)
end end
def supports_move_and_clone?
issue_type_supports?(:move_and_clone)
end
def email_participants_emails def email_participants_emails
issue_email_participants.pluck(:email) issue_email_participants.pluck(:email)
end end
......
...@@ -8,13 +8,7 @@ module Issues ...@@ -8,13 +8,7 @@ module Issues
@target_project = target_project @target_project = target_project
@with_notes = with_notes @with_notes = with_notes
unless issue.can_clone?(current_user, target_project) verify_can_clone_issue!(issue, target_project)
raise CloneError, s_('CloneIssue|Cannot clone issue due to insufficient permissions!')
end
if target_project.pending_delete?
raise CloneError, s_('CloneIssue|Cannot clone issue to target project as it is pending deletion.')
end
super(issue, target_project) super(issue, target_project)
...@@ -30,6 +24,20 @@ module Issues ...@@ -30,6 +24,20 @@ module Issues
attr_reader :target_project attr_reader :target_project
attr_reader :with_notes attr_reader :with_notes
def verify_can_clone_issue!(issue, target_project)
unless issue.supports_move_and_clone?
raise CloneError, s_('CloneIssue|Cannot clone issues of \'%{issue_type}\' type.') % { issue_type: issue.issue_type }
end
unless issue.can_clone?(current_user, target_project)
raise CloneError, s_('CloneIssue|Cannot clone issue due to insufficient permissions!')
end
if target_project.pending_delete?
raise CloneError, s_('CloneIssue|Cannot clone issue to target project as it is pending deletion.')
end
end
def update_new_entity def update_new_entity
# we don't call `super` because we want to be able to decide whether or not to copy all comments over. # we don't call `super` because we want to be able to decide whether or not to copy all comments over.
update_new_entity_description update_new_entity_description
......
...@@ -7,13 +7,7 @@ module Issues ...@@ -7,13 +7,7 @@ module Issues
def execute(issue, target_project) def execute(issue, target_project)
@target_project = target_project @target_project = target_project
unless issue.can_move?(current_user, @target_project) verify_can_move_issue!(issue, target_project)
raise MoveError, s_('MoveIssue|Cannot move issue due to insufficient permissions!')
end
if @project == @target_project
raise MoveError, s_('MoveIssue|Cannot move issue to project it originates from!')
end
super super
...@@ -32,6 +26,20 @@ module Issues ...@@ -32,6 +26,20 @@ module Issues
attr_reader :target_project attr_reader :target_project
def verify_can_move_issue!(issue, target_project)
unless issue.supports_move_and_clone?
raise MoveError, s_('MoveIssue|Cannot move issues of \'%{issue_type}\' type.') % { issue_type: issue.issue_type }
end
unless issue.can_move?(current_user, @target_project)
raise MoveError, s_('MoveIssue|Cannot move issue due to insufficient permissions!')
end
if @project == @target_project
raise MoveError, s_('MoveIssue|Cannot move issue to project it originates from!')
end
end
def update_service_desk_sent_notifications def update_service_desk_sent_notifications
return unless original_entity.from_service_desk? return unless original_entity.from_service_desk?
......
...@@ -15,6 +15,7 @@ module EE ...@@ -15,6 +15,7 @@ module EE
available_features[:epics] = %w(issue) available_features[:epics] = %w(issue)
available_features[:sla] = %w(incident) available_features[:sla] = %w(incident)
available_features[:confidentiality] += %w(test_case) available_features[:confidentiality] += %w(test_case)
available_features[:move_and_clone] += %w(test_case)
end end
end end
end end
......
...@@ -1048,6 +1048,26 @@ RSpec.describe Issue do ...@@ -1048,6 +1048,26 @@ RSpec.describe Issue do
end end
end end
describe '#supports_move_and_clone?' do
let_it_be(:project) { create(:project) }
let_it_be_with_refind(:issue) { create(:incident, project: project) }
where(:issue_type, :supports_move_and_clone) do
:requirement | false
:test_case | true
end
with_them do
before do
issue.update!(issue_type: issue_type)
end
it do
expect(issue.supports_move_and_clone?).to eq(supports_move_and_clone)
end
end
end
describe '#related_feature_flags' do describe '#related_feature_flags' do
let_it_be(:user) { create(:user) } let_it_be(:user) { create(:user) }
......
...@@ -48,6 +48,15 @@ RSpec.describe Issues::CloneService do ...@@ -48,6 +48,15 @@ RSpec.describe Issues::CloneService do
end end
end end
context 'when it is not allowed to clone issues of given type' do
it 'throws error' do
requirement_issue = create(:issue, issue_type: :requirement, project: old_project)
expect { clone_service.execute(requirement_issue, new_project) }
.to raise_error(StandardError, 'Cannot clone issues of \'requirement\' type.')
end
end
context 'epics' do context 'epics' do
context 'issue assigned to epic' do context 'issue assigned to epic' do
let_it_be(:epic) { create(:epic, group: group) } let_it_be(:epic) { create(:epic, group: group) }
......
...@@ -46,6 +46,15 @@ RSpec.describe Issues::MoveService do ...@@ -46,6 +46,15 @@ RSpec.describe Issues::MoveService do
move_service.execute(old_issue, new_project) move_service.execute(old_issue, new_project)
end end
end end
context 'when it is not allowed to move issues of given type' do
it 'throws error' do
requirement_issue = create(:issue, issue_type: :requirement, project: old_project)
expect { move_service.execute(requirement_issue, new_project) }
.to raise_error(StandardError, 'Cannot move issues of \'requirement\' type.')
end
end
end end
context 'resource weight events' do context 'resource weight events' do
......
...@@ -7106,6 +7106,9 @@ msgstr "" ...@@ -7106,6 +7106,9 @@ msgstr ""
msgid "CloneIssue|Cannot clone issue to target project as it is pending deletion." msgid "CloneIssue|Cannot clone issue to target project as it is pending deletion."
msgstr "" msgstr ""
msgid "CloneIssue|Cannot clone issues of '%{issue_type}' type."
msgstr ""
msgid "Cloned this issue to %{path_to_project}." msgid "Cloned this issue to %{path_to_project}."
msgstr "" msgstr ""
...@@ -22168,6 +22171,9 @@ msgstr "" ...@@ -22168,6 +22171,9 @@ msgstr ""
msgid "MoveIssue|Cannot move issue to project it originates from!" msgid "MoveIssue|Cannot move issue to project it originates from!"
msgstr "" msgstr ""
msgid "MoveIssue|Cannot move issues of '%{issue_type}' type."
msgstr ""
msgid "Moved issue to %{label} column in the board." msgid "Moved issue to %{label} column in the board."
msgstr "" msgstr ""
......
...@@ -1505,6 +1505,26 @@ RSpec.describe Issue do ...@@ -1505,6 +1505,26 @@ RSpec.describe Issue do
end end
end end
describe '#supports_move_and_clone?' do
let_it_be(:project) { create(:project) }
let_it_be_with_refind(:issue) { create(:incident, project: project) }
where(:issue_type, :supports_move_and_clone) do
:issue | true
:incident | true
end
with_them do
before do
issue.update!(issue_type: issue_type)
end
it do
expect(issue.supports_move_and_clone?).to eq(supports_move_and_clone)
end
end
end
describe '#email_participants_emails' do describe '#email_participants_emails' do
let_it_be(:issue) { create(:issue) } let_it_be(:issue) { create(:issue) }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment