Commit 263f4ce0 authored by Alex Kalderimis's avatar Alex Kalderimis

Rebalance issues if needed on create and update

This adds a new method in the issues base_service that rebalances the
issue set when the issue is placed in the extreme 0.0001 fringes of the
relative positioning range.
parent ce699b25
...@@ -19,6 +19,16 @@ module Issues ...@@ -19,6 +19,16 @@ module Issues
private private
NO_REBALANCING_NEEDED = ((RelativePositioning::MIN_POSITION * 0.9999)..(RelativePositioning::MAX_POSITION * 0.9999)).freeze
def rebalance_if_needed(issue)
return unless issue
return if issue.relative_position.nil?
return if NO_REBALANCING_NEEDED.cover?(issue.relative_position)
IssueRebalancingWorker.perform_async(issue.id)
end
def create_assignee_note(issue, old_assignees) def create_assignee_note(issue, old_assignees)
SystemNoteService.change_issuable_assignees( SystemNoteService.change_issuable_assignees(
issue, issue.project, current_user, old_assignees) issue, issue.project, current_user, old_assignees)
......
...@@ -30,6 +30,7 @@ module Issues ...@@ -30,6 +30,7 @@ module Issues
user_agent_detail_service.create user_agent_detail_service.create
resolve_discussions_with_issue(issuable) resolve_discussions_with_issue(issuable)
delete_milestone_total_issue_counter_cache(issuable.milestone) delete_milestone_total_issue_counter_cache(issuable.milestone)
rebalance_if_needed(issuable)
super super
end end
......
...@@ -83,6 +83,7 @@ module Issues ...@@ -83,6 +83,7 @@ module Issues
raise ActiveRecord::RecordNotFound unless issue_before || issue_after raise ActiveRecord::RecordNotFound unless issue_before || issue_after
issue.move_between(issue_before, issue_after) issue.move_between(issue_before, issue_after)
rebalance_if_needed(issue)
end end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
......
...@@ -75,6 +75,19 @@ RSpec.describe Issues::CreateService do ...@@ -75,6 +75,19 @@ RSpec.describe Issues::CreateService do
expect(Todo.where(attributes).count).to eq 1 expect(Todo.where(attributes).count).to eq 1
end end
it 'rebalances if needed' do
create(:issue, project: project, relative_position: RelativePositioning::MAX_POSITION)
expect(IssueRebalancingWorker).to receive(:perform_async).with(Integer)
expect(issue.relative_position).to eq(project.issues.maximum(:relative_position))
end
it 'does not rebalance unless needed' do
expect(IssueRebalancingWorker).not_to receive(:perform_async).with(Integer)
expect(issue.relative_position).to eq(project.issues.maximum(:relative_position))
end
context 'when label belongs to project group' do context 'when label belongs to project group' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:group_labels) { create_pair(:group_label, group: group) } let(:group_labels) { create_pair(:group_label, group: group) }
......
...@@ -106,7 +106,7 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -106,7 +106,7 @@ RSpec.describe Issues::UpdateService, :mailer do
[issue, issue1, issue2].each do |issue| [issue, issue1, issue2].each do |issue|
issue.move_to_end issue.move_to_end
issue.save issue.save!
end end
opts[:move_between_ids] = [issue1.id, issue2.id] opts[:move_between_ids] = [issue1.id, issue2.id]
...@@ -116,6 +116,34 @@ RSpec.describe Issues::UpdateService, :mailer do ...@@ -116,6 +116,34 @@ RSpec.describe Issues::UpdateService, :mailer do
expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position) expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
end end
it 'rebalances if needed on the left' do
range = described_class::NO_REBALANCING_NEEDED
issue1 = create(:issue, project: project, relative_position: range.first - 100)
issue2 = create(:issue, project: project, relative_position: range.first)
issue.update!(relative_position: RelativePositioning::START_POSITION)
opts[:move_between_ids] = [issue1.id, issue2.id]
expect(IssueRebalancingWorker).to receive(:perform_async).with(issue.id)
update_issue(opts)
expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
end
it 'rebalances if needed on the right' do
range = described_class::NO_REBALANCING_NEEDED
issue1 = create(:issue, project: project, relative_position: range.last)
issue2 = create(:issue, project: project, relative_position: range.last + 100)
issue.update!(relative_position: RelativePositioning::START_POSITION)
opts[:move_between_ids] = [issue1.id, issue2.id]
expect(IssueRebalancingWorker).to receive(:perform_async).with(issue.id)
update_issue(opts)
expect(issue.relative_position).to be_between(issue1.relative_position, issue2.relative_position)
end
context 'when moving issue between issues from different projects' do context 'when moving issue between issues from different projects' do
let(:group) { create(:group) } let(:group) { create(:group) }
let(:subgroup) { create(:group, parent: group) } let(:subgroup) { create(:group, parent: group) }
......
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