Commit 55566628 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch '323194-epic-board-reposition' into 'master'

Reposition epics on the board using graphQL

See merge request gitlab-org/gitlab!56236
parents 9c91a94d ad398c13
...@@ -22,6 +22,12 @@ module Boards ...@@ -22,6 +22,12 @@ module Boards
) )
end end
reposition_ids = move_between_ids(params)
if reposition_ids
attrs[:move_between_ids] = reposition_ids
attrs.merge!(reposition_parent)
end
attrs attrs
end end
...@@ -68,5 +74,13 @@ module Boards ...@@ -68,5 +74,13 @@ module Boards
Array(label_ids).compact Array(label_ids).compact
end end
def move_between_ids(move_params)
ids = [move_params[:move_after_id], move_params[:move_before_id]]
.map(&:to_i)
.map { |m| m > 0 ? m : nil }
ids.any? ? ids : nil
end
end end
end end
...@@ -3,8 +3,6 @@ ...@@ -3,8 +3,6 @@
module Boards module Boards
module Issues module Issues
class MoveService < Boards::BaseItemMoveService class MoveService < Boards::BaseItemMoveService
extend ::Gitlab::Utils::Override
def execute_multiple(issues) def execute_multiple(issues)
return execute_multiple_empty_result if issues.empty? return execute_multiple_empty_result if issues.empty?
...@@ -57,25 +55,8 @@ module Boards ...@@ -57,25 +55,8 @@ module Boards
::Issues::UpdateService.new(issue.project, current_user, issue_modification_params).execute(issue) ::Issues::UpdateService.new(issue.project, current_user, issue_modification_params).execute(issue)
end end
override :issuable_params def reposition_parent
def issuable_params(issuable) { board_group_id: board.group&.id }
attrs = super
move_between_ids = move_between_ids(params)
if move_between_ids
attrs[:move_between_ids] = move_between_ids
attrs[:board_group_id] = board.group&.id
end
attrs
end
def move_between_ids(move_params)
ids = [move_params[:move_after_id], move_params[:move_before_id]]
.map(&:to_i)
.map { |m| m > 0 ? m : nil }
ids.any? ? ids : nil
end end
end end
end end
......
...@@ -424,6 +424,20 @@ class IssuableBaseService < BaseService ...@@ -424,6 +424,20 @@ class IssuableBaseService < BaseService
associations associations
end end
def handle_move_between_ids(issuable_position)
return unless params[: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)
issuable_after = issuable_for_positioning(after_id, positioning_scope_id)
raise ActiveRecord::RecordNotFound unless issuable_before || issuable_after
issuable_position.move_between(issuable_before, issuable_after)
end
def has_changes?(issuable, old_labels: [], old_assignees: [], old_reviewers: []) def has_changes?(issuable, old_labels: [], old_assignees: [], old_reviewers: [])
valid_attrs = [:title, :description, :assignee_ids, :reviewer_ids, :milestone_id, :target_branch] valid_attrs = [:title, :description, :assignee_ids, :reviewer_ids, :milestone_id, :target_branch]
......
...@@ -96,19 +96,15 @@ module Issues ...@@ -96,19 +96,15 @@ module Issues
end end
def handle_move_between_ids(issue) def handle_move_between_ids(issue)
return unless params[:move_between_ids] super
after_id, before_id = params.delete(:move_between_ids)
board_group_id = params.delete(:board_group_id)
issue_before = get_issue_if_allowed(before_id, board_group_id)
issue_after = get_issue_if_allowed(after_id, board_group_id)
raise ActiveRecord::RecordNotFound unless issue_before || issue_after
issue.move_between(issue_before, issue_after)
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)
...@@ -185,7 +181,7 @@ module Issues ...@@ -185,7 +181,7 @@ module Issues
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def get_issue_if_allowed(id, board_group_id = nil) def issuable_for_positioning(id, board_group_id = nil)
return unless id return unless id
issue = issue =
......
...@@ -12,6 +12,10 @@ module Boards ...@@ -12,6 +12,10 @@ module Boards
def board def board
@board ||= parent.epic_boards.find(params[:board_id]) @board ||= parent.epic_boards.find(params[:board_id])
end end
def reposition_parent
{ board_id: board.id }
end
end end
end end
end end
...@@ -10,6 +10,8 @@ module Epics ...@@ -10,6 +10,8 @@ module Epics
].freeze ].freeze
def execute(epic) def execute(epic)
reposition_on_board(epic)
# start_date and end_date columns are no longer writable by users because those # start_date and end_date columns are no longer writable by users because those
# are composite fields managed by the system. # are composite fields managed by the system.
params.extract!(:start_date, :end_date) params.extract!(:start_date, :end_date)
...@@ -64,6 +66,27 @@ module Epics ...@@ -64,6 +66,27 @@ module Epics
end end
end end
def reposition_on_board(epic)
return unless params[:move_between_ids]
return unless params[positioning_scope_key]
epic_board_position = issuable_for_positioning(epic.id, params[positioning_scope_key])
handle_move_between_ids(epic_board_position)
epic_board_position.save
end
def issuable_for_positioning(id, board_id)
return unless id
Boards::EpicBoardPosition.find_by_epic_id_and_epic_board_id(id, board_id)
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
......
...@@ -41,6 +41,37 @@ RSpec.describe Boards::Epics::MoveService do ...@@ -41,6 +41,37 @@ RSpec.describe Boards::Epics::MoveService do
group.add_maintainer(user) group.add_maintainer(user)
end end
context 'when repositioning epics' do
let_it_be(:epic1) { create(:epic, group: group) }
let_it_be(:epic2) { create(:epic, group: group) }
let!(:epic_position) { create(:epic_board_position, epic: epic, epic_board: board, relative_position: 10) }
let!(:epic1_position) { create(:epic_board_position, epic: epic1, epic_board: board, relative_position: 20) }
let!(:epic2_position) { create(:epic_board_position, epic: epic2, epic_board: board, relative_position: 30) }
let(:params) { { board_id: board.id, move_before_id: epic1.id } }
context 'with valid params' do
it 'moves the epic' do
subject
expect(epic_position.reload.relative_position).to be > epic1_position.relative_position
expect(epic_position.relative_position).to be < epic2_position.relative_position
end
end
context 'with invalid params' do
context 'with board from another group' do
let(:board) { create(:epic_board) }
it 'raises an error' do
expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end
context 'when moving an epic between lists' do
context 'when moving the epic from backlog' do context 'when moving the epic from backlog' do
context 'to a labeled list' do context 'to a labeled list' do
let(:to_list) { list1 } let(:to_list) { list1 }
...@@ -92,4 +123,5 @@ RSpec.describe Boards::Epics::MoveService do ...@@ -92,4 +123,5 @@ RSpec.describe Boards::Epics::MoveService do
end end
end end
end end
end
end end
...@@ -84,6 +84,46 @@ RSpec.describe Epics::UpdateService do ...@@ -84,6 +84,46 @@ RSpec.describe Epics::UpdateService do
end end
end end
context 'when repositioning an epic on a board' do
let(:epic1) { create(:epic, group: group) }
let(:epic2) { create(:epic, group: group) }
let!(:epic_position) { create(:epic_board_position, epic: epic, epic_board: board, relative_position: 10) }
let!(:epic1_position) { create(:epic_board_position, epic: epic1, epic_board: board, relative_position: 20) }
let!(:epic2_position) { create(:epic_board_position, epic: epic2, epic_board: board, relative_position: 30) }
let(:board) { create(:epic_board, group: group) }
context 'when moving beetween 2 epics on the board' do
it 'moves the epic correctly' do
update_epic(move_between_ids: [epic1.id, epic2.id], board_id: board.id)
expect(epic_position.reload.relative_position)
.to be_between(epic1_position.relative_position, epic2_position.relative_position)
end
end
context 'when moving the epic to the end' do
it 'moves the epic correctly' do
update_epic(move_between_ids: [nil, epic2.id], board_id: board.id)
expect(epic_position.reload.relative_position).to be > epic2_position.relative_position
end
end
context 'when moving the epic to the beginning' do
before do
epic_position.update_column(:relative_position, 25)
end
it 'moves the epic correctly' do
update_epic(move_between_ids: [epic1.id, nil], board_id: board.id)
expect(epic_position.reload.relative_position).to be < epic1_position.relative_position
end
end
end
context 'after_save callback to store_mentions' do context 'after_save callback to store_mentions' do
let(:user2) { create(:user) } let(:user2) { create(:user) }
let(:epic) { create(:epic, group: group, description: "simple description") } let(:epic) { create(:epic, group: group, description: "simple description") }
......
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