Commit 154dac8c authored by Sean McGivern's avatar Sean McGivern

Merge branch 'cablett-247867-epics-relative-position' into 'master'

Add EpicBoardPosition with relative positioning

See merge request gitlab-org/gitlab!48120
parents 9b516b9c bd15b60b
---
title: Add Epic Board Position model to store relative positioning of epics on a board
merge_request: 48120
author:
type: added
# frozen_string_literal: true
class AddEpicBoardPositions < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
def up
with_lock_retries do
create_table :boards_epic_board_positions do |t|
t.references :epic_board, foreign_key: { to_table: :boards_epic_boards, on_delete: :cascade }, null: false, index: false
t.references :epic, foreign_key: { on_delete: :cascade }, null: false, index: true
t.integer :relative_position
t.timestamps_with_timezone null: false
t.index [:epic_board_id, :epic_id], unique: true, name: :index_boards_epic_board_positions_on_epic_board_id_and_epic_id
end
end
end
def down
with_lock_retries do
drop_table :boards_epic_board_positions
end
end
end
779effb1db70aa8b9a24942ec3e0681064c01b69ee4731f82477c54361a670b0
\ No newline at end of file
...@@ -9864,6 +9864,24 @@ CREATE SEQUENCE boards_epic_board_labels_id_seq ...@@ -9864,6 +9864,24 @@ CREATE SEQUENCE boards_epic_board_labels_id_seq
ALTER SEQUENCE boards_epic_board_labels_id_seq OWNED BY boards_epic_board_labels.id; ALTER SEQUENCE boards_epic_board_labels_id_seq OWNED BY boards_epic_board_labels.id;
CREATE TABLE boards_epic_board_positions (
id bigint NOT NULL,
epic_board_id bigint NOT NULL,
epic_id bigint NOT NULL,
relative_position integer,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL
);
CREATE SEQUENCE boards_epic_board_positions_id_seq
START WITH 1
INCREMENT BY 1
NO MINVALUE
NO MAXVALUE
CACHE 1;
ALTER SEQUENCE boards_epic_board_positions_id_seq OWNED BY boards_epic_board_positions.id;
CREATE TABLE boards_epic_boards ( CREATE TABLE boards_epic_boards (
id bigint NOT NULL, id bigint NOT NULL,
hide_backlog_list boolean DEFAULT false NOT NULL, hide_backlog_list boolean DEFAULT false NOT NULL,
...@@ -17920,6 +17938,8 @@ ALTER TABLE ONLY boards ALTER COLUMN id SET DEFAULT nextval('boards_id_seq'::reg ...@@ -17920,6 +17938,8 @@ ALTER TABLE ONLY boards ALTER COLUMN id SET DEFAULT nextval('boards_id_seq'::reg
ALTER TABLE ONLY boards_epic_board_labels ALTER COLUMN id SET DEFAULT nextval('boards_epic_board_labels_id_seq'::regclass); ALTER TABLE ONLY boards_epic_board_labels ALTER COLUMN id SET DEFAULT nextval('boards_epic_board_labels_id_seq'::regclass);
ALTER TABLE ONLY boards_epic_board_positions ALTER COLUMN id SET DEFAULT nextval('boards_epic_board_positions_id_seq'::regclass);
ALTER TABLE ONLY boards_epic_boards ALTER COLUMN id SET DEFAULT nextval('boards_epic_boards_id_seq'::regclass); ALTER TABLE ONLY boards_epic_boards ALTER COLUMN id SET DEFAULT nextval('boards_epic_boards_id_seq'::regclass);
ALTER TABLE ONLY boards_epic_user_preferences ALTER COLUMN id SET DEFAULT nextval('boards_epic_user_preferences_id_seq'::regclass); ALTER TABLE ONLY boards_epic_user_preferences ALTER COLUMN id SET DEFAULT nextval('boards_epic_user_preferences_id_seq'::regclass);
...@@ -18953,6 +18973,9 @@ ALTER TABLE ONLY board_user_preferences ...@@ -18953,6 +18973,9 @@ ALTER TABLE ONLY board_user_preferences
ALTER TABLE ONLY boards_epic_board_labels ALTER TABLE ONLY boards_epic_board_labels
ADD CONSTRAINT boards_epic_board_labels_pkey PRIMARY KEY (id); ADD CONSTRAINT boards_epic_board_labels_pkey PRIMARY KEY (id);
ALTER TABLE ONLY boards_epic_board_positions
ADD CONSTRAINT boards_epic_board_positions_pkey PRIMARY KEY (id);
ALTER TABLE ONLY boards_epic_boards ALTER TABLE ONLY boards_epic_boards
ADD CONSTRAINT boards_epic_boards_pkey PRIMARY KEY (id); ADD CONSTRAINT boards_epic_boards_pkey PRIMARY KEY (id);
...@@ -20582,6 +20605,10 @@ CREATE INDEX index_boards_epic_board_labels_on_epic_board_id ON boards_epic_boar ...@@ -20582,6 +20605,10 @@ CREATE INDEX index_boards_epic_board_labels_on_epic_board_id ON boards_epic_boar
CREATE INDEX index_boards_epic_board_labels_on_label_id ON boards_epic_board_labels USING btree (label_id); CREATE INDEX index_boards_epic_board_labels_on_label_id ON boards_epic_board_labels USING btree (label_id);
CREATE UNIQUE INDEX index_boards_epic_board_positions_on_epic_board_id_and_epic_id ON boards_epic_board_positions USING btree (epic_board_id, epic_id);
CREATE INDEX index_boards_epic_board_positions_on_epic_id ON boards_epic_board_positions USING btree (epic_id);
CREATE INDEX index_boards_epic_boards_on_group_id ON boards_epic_boards USING btree (group_id); CREATE INDEX index_boards_epic_boards_on_group_id ON boards_epic_boards USING btree (group_id);
CREATE INDEX index_boards_epic_user_preferences_on_board_id ON boards_epic_user_preferences USING btree (board_id); CREATE INDEX index_boards_epic_user_preferences_on_board_id ON boards_epic_user_preferences USING btree (board_id);
...@@ -23844,6 +23871,9 @@ ALTER TABLE ONLY approver_groups ...@@ -23844,6 +23871,9 @@ ALTER TABLE ONLY approver_groups
ALTER TABLE ONLY packages_tags ALTER TABLE ONLY packages_tags
ADD CONSTRAINT fk_rails_1dfc868911 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_1dfc868911 FOREIGN KEY (package_id) REFERENCES packages_packages(id) ON DELETE CASCADE;
ALTER TABLE ONLY boards_epic_board_positions
ADD CONSTRAINT fk_rails_1ecfd9f2de FOREIGN KEY (epic_id) REFERENCES epics(id) ON DELETE CASCADE;
ALTER TABLE ONLY geo_repository_created_events ALTER TABLE ONLY geo_repository_created_events
ADD CONSTRAINT fk_rails_1f49e46a61 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_1f49e46a61 FOREIGN KEY (project_id) REFERENCES projects(id) ON DELETE CASCADE;
...@@ -24771,6 +24801,9 @@ ALTER TABLE ONLY gpg_signatures ...@@ -24771,6 +24801,9 @@ ALTER TABLE ONLY gpg_signatures
ALTER TABLE ONLY board_group_recent_visits ALTER TABLE ONLY board_group_recent_visits
ADD CONSTRAINT fk_rails_ca04c38720 FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_ca04c38720 FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE;
ALTER TABLE ONLY boards_epic_board_positions
ADD CONSTRAINT fk_rails_cb4563dd6e FOREIGN KEY (epic_board_id) REFERENCES boards_epic_boards(id) ON DELETE CASCADE;
ALTER TABLE ONLY vulnerability_finding_links ALTER TABLE ONLY vulnerability_finding_links
ADD CONSTRAINT fk_rails_cbdfde27ce FOREIGN KEY (vulnerability_occurrence_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE; ADD CONSTRAINT fk_rails_cbdfde27ce FOREIGN KEY (vulnerability_occurrence_id) REFERENCES vulnerability_occurrences(id) ON DELETE CASCADE;
......
...@@ -4,6 +4,7 @@ module Boards ...@@ -4,6 +4,7 @@ module Boards
class EpicBoard < ApplicationRecord class EpicBoard < ApplicationRecord
belongs_to :group, optional: false, inverse_of: :epic_boards belongs_to :group, optional: false, inverse_of: :epic_boards
has_many :epic_board_labels, foreign_key: :epic_board_id, inverse_of: :epic_board has_many :epic_board_labels, foreign_key: :epic_board_id, inverse_of: :epic_board
has_many :epic_board_positions, foreign_key: :epic_board_id, inverse_of: :epic_board
validates :name, length: { maximum: 255 } validates :name, length: { maximum: 255 }
end end
......
# frozen_string_literal: true
module Boards
class EpicBoardPosition < ApplicationRecord
include RelativePositioning
belongs_to :epic_board, optional: false, inverse_of: :epic_board_positions
belongs_to :epic, optional: false
alias_attribute :parent, :epic_board
validates :epic, uniqueness: { scope: :epic_board_id }
scope :order_relative_position, -> do
reorder('relative_position ASC', 'id DESC')
end
def self.relative_positioning_query_base(position)
where(epic_board_id: position.epic_board_id)
end
def self.relative_positioning_parent_column
:epic_board_id
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :epic_board_position, class: 'Boards::EpicBoardPosition' do
epic
epic_board
relative_position { RelativePositioning::START_POSITION }
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :epic_board, class: 'Boards::EpicBoard' do
name
group
end
end
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Boards::EpicBoardPosition do
let_it_be(:epic) { create(:epic) }
let_it_be(:group) { create(:group) }
let_it_be(:epic_board) { create(:epic_board, group: group) }
let_it_be(:epic_board_position) { create(:epic_board_position, epic: epic, epic_board: epic_board) }
describe 'associations' do
subject { build(:epic_board_position) }
it { is_expected.to belong_to(:epic).required }
it { is_expected.to belong_to(:epic_board).required.inverse_of(:epic_board_positions) }
end
describe 'validations' do
subject { build(:epic_board_position) }
specify { expect(subject).to be_valid }
it 'is valid with nil relative position' do
subject.relative_position = nil
expect(subject).to be_valid
end
it 'disallows a record with same epic and board' do
expect(build(:epic_board_position, epic: epic, epic_board: epic_board)).not_to be_valid
end
end
describe 'scopes' do
describe '.order_relative_position' do
let(:first) { epic_board_position }
let!(:second) { create(:epic_board_position, epic_board: epic_board, relative_position: RelativePositioning::START_POSITION + 7 ) }
it 'returns epic_board_positions in order' do
expect(described_class.order_relative_position).to eq([first, second])
end
end
end
context 'relative positioning' do
let_it_be(:positioning_group) { create(:group) }
let_it_be(:positioning_board) { create(:epic_board, group: positioning_group) }
it_behaves_like "a class that supports relative positioning" do
let(:factory) { :epic_board_position }
let(:default_params) { { parent: positioning_board } }
end
end
end
...@@ -6,6 +6,7 @@ RSpec.describe Boards::EpicBoard do ...@@ -6,6 +6,7 @@ RSpec.describe Boards::EpicBoard do
describe 'associations' do describe 'associations' do
it { is_expected.to belong_to(:group).required.inverse_of(:epic_boards) } it { is_expected.to belong_to(:group).required.inverse_of(:epic_boards) }
it { is_expected.to have_many(:epic_board_labels).inverse_of(:epic_board) } it { is_expected.to have_many(:epic_board_labels).inverse_of(:epic_board) }
it { is_expected.to have_many(:epic_board_positions).inverse_of(:epic_board) }
end end
describe 'validations' do describe 'validations' do
......
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