Commit 4ada1157 authored by Bob Van Landuyt's avatar Bob Van Landuyt

Merge branch '341562-fix-issue-card-position-on-create' into 'master'

Refactor issue move to not require group_id param

See merge request gitlab-org/gitlab!79309
parents 3208d059 f4364dd3
...@@ -321,7 +321,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -321,7 +321,7 @@ class Projects::IssuesController < Projects::ApplicationController
end end
def reorder_params def reorder_params
params.permit(:move_before_id, :move_after_id, :group_full_path) params.permit(:move_before_id, :move_after_id)
end end
def store_uri def store_uri
......
...@@ -39,7 +39,7 @@ module Boards ...@@ -39,7 +39,7 @@ module Boards
end end
def reposition_params(reposition_ids) def reposition_params(reposition_ids)
reposition_parent.merge(move_between_ids: reposition_ids) { move_between_ids: reposition_ids }
end end
def move_single_issuable(issuable, issuable_modification_params) def move_single_issuable(issuable, issuable_modification_params)
......
...@@ -54,10 +54,6 @@ module Boards ...@@ -54,10 +54,6 @@ module Boards
def update(issue, issue_modification_params) def update(issue, issue_modification_params)
::Issues::UpdateService.new(project: issue.project, current_user: current_user, params: issue_modification_params).execute(issue) ::Issues::UpdateService.new(project: issue.project, current_user: current_user, params: issue_modification_params).execute(issue)
end end
def reposition_parent
{ board_group_id: board.group&.id }
end
end end
end end
end end
......
...@@ -495,10 +495,11 @@ class IssuableBaseService < ::BaseProjectService ...@@ -495,10 +495,11 @@ class IssuableBaseService < ::BaseProjectService
return unless params[:move_between_ids] return unless params[:move_between_ids]
after_id, before_id = params.delete(:move_between_ids) after_id, before_id = params.delete(:move_between_ids)
positioning_scope_id = params.delete(positioning_scope_key)
issuable_before = issuable_for_positioning(before_id, positioning_scope_id) positioning_scope = issuable_position.class.relative_positioning_query_base(issuable_position)
issuable_after = issuable_for_positioning(after_id, positioning_scope_id)
issuable_before = issuable_for_positioning(before_id, positioning_scope)
issuable_after = issuable_for_positioning(after_id, positioning_scope)
raise ActiveRecord::RecordNotFound unless issuable_before || issuable_after raise ActiveRecord::RecordNotFound unless issuable_before || issuable_after
......
...@@ -2,47 +2,31 @@ ...@@ -2,47 +2,31 @@
module Issues module Issues
class ReorderService < Issues::BaseService class ReorderService < Issues::BaseService
include Gitlab::Utils::StrongMemoize
def execute(issue) def execute(issue)
return false unless can?(current_user, :update_issue, issue) return false unless can?(current_user, :update_issue, issue)
return false if group && !can?(current_user, :read_group, group) return false unless move_between_ids
attrs = issue_params(group)
return false if attrs.empty?
update(issue, attrs) update(issue, { move_between_ids: move_between_ids })
end end
private private
def group
return unless params[:group_full_path]
@group ||= Group.find_by_full_path(params[:group_full_path])
end
def update(issue, attrs) def update(issue, attrs)
::Issues::UpdateService.new(project: project, current_user: current_user, params: attrs).execute(issue) ::Issues::UpdateService.new(project: project, current_user: current_user, params: attrs).execute(issue)
rescue ActiveRecord::RecordNotFound rescue ActiveRecord::RecordNotFound
false false
end end
def issue_params(group)
attrs = {}
if move_between_ids
attrs[:move_between_ids] = move_between_ids
attrs[:board_group_id] = group&.id
end
attrs
end
def move_between_ids def move_between_ids
ids = [params[:move_after_id], params[:move_before_id]] strong_memoize(:move_between_ids) do
.map(&:to_i) ids = [params[:move_after_id], params[:move_before_id]]
.map { |m| m > 0 ? m : nil } .map(&:to_i)
.map { |m| m > 0 ? m : nil }
ids.any? ? ids : nil ids.any? ? ids : nil
end
end end
end end
end end
...@@ -100,10 +100,6 @@ module Issues ...@@ -100,10 +100,6 @@ module Issues
rebalance_if_needed(issue) rebalance_if_needed(issue)
end end
def positioning_scope_key
:board_group_id
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def change_issue_duplicate(issue) def change_issue_duplicate(issue)
canonical_issue_id = params.delete(:canonical_issue_id) canonical_issue_id = params.delete(:canonical_issue_id)
...@@ -221,20 +217,13 @@ module Issues ...@@ -221,20 +217,13 @@ module Issues
).execute ).execute
end end
# rubocop: disable CodeReuse/ActiveRecord def issuable_for_positioning(id, positioning_scope)
def issuable_for_positioning(id, board_group_id = nil)
return unless id return unless id
issue = issue = positioning_scope.find(id)
if board_group_id
IssuesFinder.new(current_user, group_id: board_group_id, include_subgroups: true).find_by(id: id)
else
project.issues.find(id)
end
issue if can?(current_user, :update_issue, issue) issue if can?(current_user, :update_issue, issue)
end end
# rubocop: enable CodeReuse/ActiveRecord
def create_confidentiality_note(issue) def create_confidentiality_note(issue)
SystemNoteService.change_issue_confidentiality(issue, issue.project, current_user) SystemNoteService.change_issue_confidentiality(issue, issue.project, current_user)
......
...@@ -22,11 +22,7 @@ module Boards ...@@ -22,11 +22,7 @@ module Boards
override :reposition_params override :reposition_params
def reposition_params(reposition_ids) def reposition_params(reposition_ids)
super.merge(list_id: params[:to_list_id], board_group: parent) super.merge(list_id: params[:to_list_id], board_id: board.id, board_group: parent)
end
def reposition_parent
{ board_id: board.id }
end end
end end
end end
......
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
module Epics module Epics
class UpdateService < Epics::BaseService class UpdateService < Epics::BaseService
include Gitlab::Utils::StrongMemoize
EPIC_DATE_FIELDS = %I[ EPIC_DATE_FIELDS = %I[
start_date_fixed start_date_fixed
start_date_is_fixed start_date_is_fixed
...@@ -9,6 +11,8 @@ module Epics ...@@ -9,6 +11,8 @@ module Epics
due_date_is_fixed due_date_is_fixed
].freeze ].freeze
attr_reader :epic_board_id
def execute(epic) def execute(epic)
reposition_on_board(epic) reposition_on_board(epic)
...@@ -104,27 +108,25 @@ module Epics ...@@ -104,27 +108,25 @@ module Epics
end end
def reposition_on_board(epic) def reposition_on_board(epic)
@epic_board_id = params.delete(:board_id)
return unless params[:move_between_ids] return unless params[:move_between_ids]
return unless epic_board_id return unless epic_board_id
fill_missing_positions_before fill_missing_positions_before
epic_board_position = issuable_for_positioning(epic.id, epic_board_id, create_missing: true) # we want to create missing only for the epic being moved
# other records are handled by PositionCreateService
epic_board_position = Boards::EpicBoardPosition.find_or_create_by!(epic_board_id: epic_board_id, epic_id: epic.id) # rubocop: disable CodeReuse/ActiveRecord
handle_move_between_ids(epic_board_position) handle_move_between_ids(epic_board_position)
epic_board_position.save! epic_board_position.save!
end end
# we want to create missing only for the epic being moved def issuable_for_positioning(id, positioning_scope)
# other records are handled by PositionCreateService
def issuable_for_positioning(id, board_id, create_missing: false)
return unless id return unless id
position = Boards::EpicBoardPosition.find_by_epic_id_and_epic_board_id(id, board_id) positioning_scope.find_by_epic_id(id)
return position if position
Boards::EpicBoardPosition.create!(epic_id: id, epic_board_id: board_id) if create_missing
end end
def fill_missing_positions_before def fill_missing_positions_before
...@@ -134,7 +136,7 @@ module Epics ...@@ -134,7 +136,7 @@ module Epics
return unless before_id return unless before_id
# if position for the epic above exists we don't need to create positioning records # if position for the epic above exists we don't need to create positioning records
return if issuable_for_positioning(before_id, epic_board_id) return if Boards::EpicBoardPosition.exists?(epic_board_id: epic_board_id, epic_id: before_id) # rubocop: disable CodeReuse/ActiveRecord
service_params = { service_params = {
board_id: epic_board_id, board_id: epic_board_id,
...@@ -145,14 +147,6 @@ module Epics ...@@ -145,14 +147,6 @@ module Epics
Boards::Epics::PositionCreateService.new(board_group, current_user, service_params).execute Boards::Epics::PositionCreateService.new(board_group, current_user, service_params).execute
end end
def epic_board_id
params[positioning_scope_key]
end
def positioning_scope_key
:board_id
end
def saved_change_to_epic_dates?(epic) def saved_change_to_epic_dates?(epic)
(epic.saved_changes.keys.map(&:to_sym) & EPIC_DATE_FIELDS).present? (epic.saved_changes.keys.map(&:to_sym) & EPIC_DATE_FIELDS).present?
end end
......
...@@ -502,10 +502,7 @@ RSpec.describe Projects::IssuesController do ...@@ -502,10 +502,7 @@ RSpec.describe Projects::IssuesController do
context 'with valid params' do context 'with valid params' do
it 'reorders issues and returns a successful 200 response' do it 'reorders issues and returns a successful 200 response' do
reorder_issue(issue1, reorder_issue(issue1, move_after_id: issue2.id, move_before_id: issue3.id)
move_after_id: issue2.id,
move_before_id: issue3.id,
group_full_path: group.full_path)
[issue1, issue2, issue3].map(&:reload) [issue1, issue2, issue3].map(&:reload)
...@@ -531,12 +528,10 @@ RSpec.describe Projects::IssuesController do ...@@ -531,12 +528,10 @@ RSpec.describe Projects::IssuesController do
end end
it 'returns a unprocessable entity 422 response for issues not in group' do it 'returns a unprocessable entity 422 response for issues not in group' do
another_group = create(:group) other_group_project = create(:project, group: create(:group))
other_group_issue = create(:issue, project: other_group_project)
reorder_issue(issue1, reorder_issue(issue1, move_after_id: issue2.id, move_before_id: other_group_issue.id)
move_after_id: issue2.id,
move_before_id: issue3.id,
group_full_path: another_group.full_path)
expect(response).to have_gitlab_http_status(:unprocessable_entity) expect(response).to have_gitlab_http_status(:unprocessable_entity)
end end
...@@ -555,15 +550,14 @@ RSpec.describe Projects::IssuesController do ...@@ -555,15 +550,14 @@ RSpec.describe Projects::IssuesController do
end end
end end
def reorder_issue(issue, move_after_id: nil, move_before_id: nil, group_full_path: nil) def reorder_issue(issue, move_after_id: nil, move_before_id: nil)
put :reorder, put :reorder,
params: { params: {
namespace_id: project.namespace.to_param, namespace_id: project.namespace.to_param,
project_id: project, project_id: project,
id: issue.iid, id: issue.iid,
move_after_id: move_after_id, move_after_id: move_after_id,
move_before_id: move_before_id, move_before_id: move_before_id
group_full_path: group_full_path
}, },
format: :json format: :json
end end
......
...@@ -1184,14 +1184,15 @@ RSpec.describe API::Issues do ...@@ -1184,14 +1184,15 @@ RSpec.describe API::Issues do
end end
describe 'PUT /projects/:id/issues/:issue_iid/reorder' do describe 'PUT /projects/:id/issues/:issue_iid/reorder' do
let_it_be(:project) { create(:project) } let_it_be(:group) { create(:group) }
let_it_be(:project) { create(:project, group: group) }
let_it_be(:issue1) { create(:issue, project: project, relative_position: 10) } let_it_be(:issue1) { create(:issue, project: project, relative_position: 10) }
let_it_be(:issue2) { create(:issue, project: project, relative_position: 20) } let_it_be(:issue2) { create(:issue, project: project, relative_position: 20) }
let_it_be(:issue3) { create(:issue, project: project, relative_position: 30) } let_it_be(:issue3) { create(:issue, project: project, relative_position: 30) }
context 'when user has access' do context 'when user has access' do
before do before_all do
project.add_developer(user) group.add_developer(user)
end end
context 'with valid params' do context 'with valid params' do
...@@ -1217,6 +1218,19 @@ RSpec.describe API::Issues do ...@@ -1217,6 +1218,19 @@ RSpec.describe API::Issues do
expect(response).to have_gitlab_http_status(:not_found) expect(response).to have_gitlab_http_status(:not_found)
end end
end end
context 'with issue in different project' do
let(:other_project) { create(:project, group: group) }
let(:other_issue) { create(:issue, project: other_project, relative_position: 80) }
it 'reorders issues and returns a successful 200 response' do
put api("/projects/#{other_project.id}/issues/#{other_issue.iid}/reorder", user), params: { move_after_id: issue2.id, move_before_id: issue3.id }
expect(response).to have_gitlab_http_status(:ok)
expect(other_issue.reload.relative_position)
.to be_between(issue2.reload.relative_position, issue3.reload.relative_position)
end
end
end end
context 'with unauthorized user' do context 'with unauthorized user' do
......
...@@ -67,20 +67,10 @@ RSpec.describe Issues::ReorderService do ...@@ -67,20 +67,10 @@ RSpec.describe Issues::ReorderService do
it_behaves_like 'issues reorder service' it_behaves_like 'issues reorder service'
context 'when ordering in a group issue list' do context 'when ordering in a group issue list' do
let(:params) { { move_after_id: issue2.id, move_before_id: issue3.id, group_full_path: group.full_path } } let(:params) { { move_after_id: issue2.id, move_before_id: issue3.id } }
subject { service(params) } subject { service(params) }
it 'sends the board_group_id parameter' do
match_params = { move_between_ids: [issue2.id, issue3.id], board_group_id: group.id }
expect(Issues::UpdateService)
.to receive(:new).with(project: project, current_user: user, params: match_params)
.and_return(double(execute: build(:issue)))
subject.execute(issue1)
end
it 'sorts issues' do it 'sorts issues' do
project2 = create(:project, namespace: group) project2 = create(:project, namespace: group)
issue4 = create(:issue, project: project2) issue4 = create(:issue, project: project2)
......
...@@ -385,7 +385,6 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -385,7 +385,6 @@ RSpec.describe Issues::UpdateService, :mailer do
[issue_1, issue_2, issue_3].map(&:save) [issue_1, issue_2, issue_3].map(&:save)
opts[:move_between_ids] = [issue_1.id, issue_2.id] opts[:move_between_ids] = [issue_1.id, issue_2.id]
opts[:board_group_id] = group.id
described_class.new(project: issue_3.project, current_user: user, params: opts).execute(issue_3) described_class.new(project: issue_3.project, current_user: user, params: opts).execute(issue_3)
expect(issue_2.relative_position).to be_between(issue_1.relative_position, issue_2.relative_position) expect(issue_2.relative_position).to be_between(issue_1.relative_position, issue_2.relative_position)
...@@ -1309,19 +1308,12 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -1309,19 +1308,12 @@ RSpec.describe Issues::UpdateService, :mailer do
end end
context 'when moving an issue ' do context 'when moving an issue ' do
it 'raises an error for invalid move ids within a project' do it 'raises an error for invalid move ids' do
opts = { move_between_ids: [9000, non_existing_record_id] } opts = { move_between_ids: [9000, non_existing_record_id] }
expect { described_class.new(project: issue.project, current_user: user, params: opts).execute(issue) } expect { described_class.new(project: issue.project, current_user: user, params: opts).execute(issue) }
.to raise_error(ActiveRecord::RecordNotFound) .to raise_error(ActiveRecord::RecordNotFound)
end end
it 'raises an error for invalid move ids within a group' do
opts = { move_between_ids: [9000, non_existing_record_id], board_group_id: create(:group).id }
expect { described_class.new(project: issue.project, current_user: user, params: opts).execute(issue) }
.to raise_error(ActiveRecord::RecordNotFound)
end
end end
include_examples 'issuable update service' do include_examples 'issuable update service' do
......
...@@ -140,19 +140,6 @@ RSpec.shared_examples 'issues move service' do |group| ...@@ -140,19 +140,6 @@ RSpec.shared_examples 'issues move service' do |group|
expect(issue2.reload.updated_at.change(usec: 0)).to eq updated_at2.change(usec: 0) expect(issue2.reload.updated_at.change(usec: 0)).to eq updated_at2.change(usec: 0)
end end
if group
context 'when on a group board' do
it 'sends the board_group_id parameter' do
params.merge!(move_after_id: issue1.id, move_before_id: issue2.id)
match_params = { move_between_ids: [issue1.id, issue2.id], board_group_id: parent.id }
expect(Issues::UpdateService).to receive(:new).with(project: issue.project, current_user: user, params: match_params).and_return(double(execute: build(:issue)))
described_class.new(parent, user, params).execute(issue)
end
end
end
def reorder_issues(params, issues: []) def reorder_issues(params, issues: [])
issues.each do |issue| issues.each do |issue|
issue.move_to_end && issue.save! issue.move_to_end && issue.save!
......
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