Commit 1d399264 authored by Andreas Brandl's avatar Andreas Brandl

Merge branch '250479-iteration-lists-creation' into 'master'

Add iteration_id column to lists

See merge request gitlab-org/gitlab!48103
parents 66d9c55d dda8f64e
...@@ -7,7 +7,7 @@ class List < ApplicationRecord ...@@ -7,7 +7,7 @@ class List < ApplicationRecord
belongs_to :label belongs_to :label
has_many :list_user_preferences has_many :list_user_preferences
enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3, milestone: 4 } enum list_type: { backlog: 0, label: 1, closed: 2, assignee: 3, milestone: 4, iteration: 5 }
validates :board, :list_type, presence: true, unless: :importing? validates :board, :list_type, presence: true, unless: :importing?
validates :label, :position, presence: true, if: :label? validates :label, :position, presence: true, if: :label?
......
---
title: Add iteration_id column to lists
merge_request: 48103
author:
type: added
# frozen_string_literal: true
class AddIterationIdToLists < ActiveRecord::Migration[6.0]
DOWNTIME = false
def change
add_column :lists, :iteration_id, :bigint
end
end
# frozen_string_literal: true
class AddIterationListsForeignKey < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
INDEX_NAME = 'index_lists_on_iteration_id'
disable_ddl_transaction!
def up
add_concurrent_index :lists, :iteration_id, name: INDEX_NAME
add_concurrent_foreign_key :lists, :sprints, column: :iteration_id, on_delete: :cascade
end
def down
remove_foreign_key_if_exists :lists, :sprints, column: :iteration_id
remove_concurrent_index_by_name :lists, INDEX_NAME
end
end
6d2e6937c9e41975b1fd402bf2985796792a1e5f8e4f4f98bc76b65ff73c4e02
\ No newline at end of file
c7567489156bbc047cf9f7827f060ad507fd5d328179f2796566a7dc54806e3e
\ No newline at end of file
...@@ -13652,7 +13652,8 @@ CREATE TABLE lists ( ...@@ -13652,7 +13652,8 @@ CREATE TABLE lists (
milestone_id integer, milestone_id integer,
max_issue_count integer DEFAULT 0 NOT NULL, max_issue_count integer DEFAULT 0 NOT NULL,
max_issue_weight integer DEFAULT 0 NOT NULL, max_issue_weight integer DEFAULT 0 NOT NULL,
limit_metric character varying(20) limit_metric character varying(20),
iteration_id bigint
); );
CREATE SEQUENCE lists_id_seq CREATE SEQUENCE lists_id_seq
...@@ -21617,6 +21618,8 @@ CREATE UNIQUE INDEX index_list_user_preferences_on_user_id_and_list_id ON list_u ...@@ -21617,6 +21618,8 @@ CREATE UNIQUE INDEX index_list_user_preferences_on_user_id_and_list_id ON list_u
CREATE UNIQUE INDEX index_lists_on_board_id_and_label_id ON lists USING btree (board_id, label_id); CREATE UNIQUE INDEX index_lists_on_board_id_and_label_id ON lists USING btree (board_id, label_id);
CREATE INDEX index_lists_on_iteration_id ON lists USING btree (iteration_id);
CREATE INDEX index_lists_on_label_id ON lists USING btree (label_id); CREATE INDEX index_lists_on_label_id ON lists USING btree (label_id);
CREATE INDEX index_lists_on_list_type ON lists USING btree (list_type); CREATE INDEX index_lists_on_list_type ON lists USING btree (list_type);
...@@ -23263,6 +23266,9 @@ ALTER TABLE ONLY notes ...@@ -23263,6 +23266,9 @@ ALTER TABLE ONLY notes
ALTER TABLE ONLY members ALTER TABLE ONLY members
ADD CONSTRAINT fk_2e88fb7ce9 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE; ADD CONSTRAINT fk_2e88fb7ce9 FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE;
ALTER TABLE ONLY lists
ADD CONSTRAINT fk_30f2a831f4 FOREIGN KEY (iteration_id) REFERENCES sprints(id) ON DELETE CASCADE;
ALTER TABLE ONLY approvals ALTER TABLE ONLY approvals
ADD CONSTRAINT fk_310d714958 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE; ADD CONSTRAINT fk_310d714958 FOREIGN KEY (merge_request_id) REFERENCES merge_requests(id) ON DELETE CASCADE;
......
...@@ -19,11 +19,14 @@ module EE ...@@ -19,11 +19,14 @@ module EE
base.belongs_to :user base.belongs_to :user
base.belongs_to :milestone base.belongs_to :milestone
base.belongs_to :iteration
base.validates :user, presence: true, if: :assignee? base.validates :user, presence: true, if: :assignee?
base.validates :milestone, presence: true, if: :milestone? base.validates :milestone, presence: true, if: :milestone?
base.validates :iteration, presence: true, if: :iteration?
base.validates :user_id, uniqueness: { scope: :board_id }, if: :assignee? base.validates :user_id, uniqueness: { scope: :board_id }, if: :assignee?
base.validates :milestone_id, uniqueness: { scope: :board_id }, if: :milestone? base.validates :milestone_id, uniqueness: { scope: :board_id }, if: :milestone?
base.validates :iteration_id, uniqueness: { scope: :board_id }, if: :iteration?
base.validates :max_issue_count, numericality: { only_integer: true, greater_than_or_equal_to: 0 } base.validates :max_issue_count, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
base.validates :max_issue_weight, numericality: { only_integer: true, greater_than_or_equal_to: 0 } base.validates :max_issue_weight, numericality: { only_integer: true, greater_than_or_equal_to: 0 }
base.validates :limit_metric, inclusion: { base.validates :limit_metric, inclusion: {
...@@ -32,11 +35,14 @@ module EE ...@@ -32,11 +35,14 @@ module EE
allow_nil: true allow_nil: true
} }
base.validates :list_type, base.validates :list_type,
exclusion: { in: %w[assignee], message: _('Assignee lists not available with your current license') }, exclusion: { in: %w[assignee], message: -> (_object, _data) { _('Assignee lists not available with your current license') } },
unless: -> { board&.resource_parent&.feature_available?(:board_assignee_lists) } unless: -> { board&.resource_parent&.feature_available?(:board_assignee_lists) }
base.validates :list_type, base.validates :list_type,
exclusion: { in: %w[milestone], message: _('Milestone lists not available with your current license') }, exclusion: { in: %w[milestone], message: -> (_object, _data) { _('Milestone lists not available with your current license') } },
unless: -> { board&.resource_parent&.feature_available?(:board_milestone_lists) } unless: -> { board&.resource_parent&.feature_available?(:board_milestone_lists) }
base.validates :list_type,
exclusion: { in: %w[iteration], message: -> (_object, _data) { _('Iteration lists not available with your current license') } },
unless: -> { board&.resource_parent&.feature_available?(:iterations) }
base.scope :without_types, ->(list_types) { where.not(list_type: list_types) } base.scope :without_types, ->(list_types) { where.not(list_type: list_types) }
end end
...@@ -58,6 +64,8 @@ module EE ...@@ -58,6 +64,8 @@ module EE
user.to_reference user.to_reference
when 'milestone' when 'milestone'
milestone.title milestone.title
when 'iteration'
iteration.title
else else
super super
end end
...@@ -78,11 +86,11 @@ module EE ...@@ -78,11 +86,11 @@ module EE
module ClassMethods module ClassMethods
def destroyable_types def destroyable_types
super + [:assignee, :milestone] super + [:assignee, :milestone, :iteration]
end end
def movable_types def movable_types
super + [:assignee, :milestone] super + [:assignee, :milestone, :iteration]
end end
end end
end end
......
...@@ -8,6 +8,7 @@ RSpec.describe List do ...@@ -8,6 +8,7 @@ RSpec.describe List do
describe 'relationships' do describe 'relationships' do
it { is_expected.to belong_to(:user) } it { is_expected.to belong_to(:user) }
it { is_expected.to belong_to(:milestone) } it { is_expected.to belong_to(:milestone) }
it { is_expected.to belong_to(:iteration) }
end end
describe 'validations' do describe 'validations' do
...@@ -75,6 +76,33 @@ RSpec.describe List do ...@@ -75,6 +76,33 @@ RSpec.describe List do
end end
end end
context 'when it is an iteration type' do
let(:iteration) { build(:iteration, title: 'awesome-iteration') }
subject { described_class.new(list_type: :iteration, iteration: iteration, board: board) }
it { is_expected.to be_destroyable }
it { is_expected.to be_movable }
describe 'validations' do
it { is_expected.to validate_presence_of(:iteration) }
it 'is invalid when feature is not available' do
stub_licensed_features(iterations: false)
expect(subject).to be_invalid
expect(subject.errors[:list_type])
.to contain_exactly('Iteration lists not available with your current license')
end
end
describe '#title' do
it 'returns the iteration title' do
expect(subject.title).to eq('awesome-iteration')
end
end
end
describe '#wip_limits_available?' do describe '#wip_limits_available?' do
let!(:project) { create(:project) } let!(:project) { create(:project) }
let!(:group) { create(:group) } let!(:group) { create(:group) }
......
...@@ -347,6 +347,7 @@ excluded_attributes: ...@@ -347,6 +347,7 @@ excluded_attributes:
- :board_id - :board_id
- :label_id - :label_id
- :milestone_id - :milestone_id
- :iteration_id
epic: epic:
- :start_date_sourcing_milestone_id - :start_date_sourcing_milestone_id
- :due_date_sourcing_milestone_id - :due_date_sourcing_milestone_id
......
...@@ -15417,6 +15417,9 @@ msgstr "" ...@@ -15417,6 +15417,9 @@ msgstr ""
msgid "Iteration changed to" msgid "Iteration changed to"
msgstr "" msgstr ""
msgid "Iteration lists not available with your current license"
msgstr ""
msgid "Iteration removed" msgid "Iteration removed"
msgstr "" msgstr ""
......
...@@ -656,6 +656,7 @@ boards: ...@@ -656,6 +656,7 @@ boards:
lists: lists:
- user - user
- milestone - milestone
- iteration
- board - board
- label - label
- list_user_preferences - list_user_preferences
......
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