Commit 83573907 authored by Adam Hegyi's avatar Adam Hegyi

Merge branch '208412-fj-add-group-features-table-and-basics' into 'master'

Start basic implementation of Group feature class

See merge request gitlab-org/gitlab!82017
parents 27c053e3 8964f64d
......@@ -96,6 +96,8 @@ class Group < Namespace
has_many :group_callouts, class_name: 'Users::GroupCallout', foreign_key: :group_id
has_one :group_feature, inverse_of: :group, class_name: 'Groups::FeatureSetting'
delegate :prevent_sharing_groups_outside_hierarchy, :new_user_signups_cap, :setup_for_company, :jobs_to_be_done, to: :namespace_settings
delegate :runner_token_expiration_interval, :runner_token_expiration_interval=, :runner_token_expiration_interval_human_readable, :runner_token_expiration_interval_human_readable=, to: :namespace_settings, allow_nil: true
delegate :subgroup_runner_token_expiration_interval, :subgroup_runner_token_expiration_interval=, :subgroup_runner_token_expiration_interval_human_readable, :subgroup_runner_token_expiration_interval_human_readable=, to: :namespace_settings, allow_nil: true
......@@ -119,6 +121,8 @@ class Group < Namespace
message: Gitlab::Regex.group_name_regex_message },
if: :name_changed?
validates :group_feature, presence: true
add_authentication_token_field :runners_token,
encrypted: -> { Feature.enabled?(:groups_tokens_optional_encryption, default_enabled: true) ? :optional : :required },
prefix: RunnersTokenPrefixable::RUNNERS_TOKEN_PREFIX
......@@ -127,6 +131,7 @@ class Group < Namespace
after_destroy :post_destroy_hook
after_save :update_two_factor_requirement
after_update :path_changed_hook, if: :saved_change_to_path?
after_create -> { create_or_load_association(:group_feature) }
scope :with_users, -> { includes(:users) }
......@@ -796,6 +801,10 @@ class Group < Namespace
super || build_dependency_proxy_setting
end
def group_feature
super || build_group_feature
end
def crm_enabled?
crm_settings&.enabled?
end
......
# frozen_string_literal: true
module Groups
class FeatureSetting < ApplicationRecord
self.primary_key = :group_id
self.table_name = 'group_features'
belongs_to :group
validates :group, presence: true
end
end
# frozen_string_literal: true
class AddGroupFeaturesTable < Gitlab::Database::Migration[1.0]
enable_lock_retries!
def up
create_table :group_features, id: false do |t|
t.references :group, index: false, foreign_key: { to_table: :namespaces, on_delete: :cascade }, null: false
t.timestamps_with_timezone null: false
t.integer :wiki_access_level, default: Featurable::ENABLED, null: false, limit: 2
end
execute('ALTER TABLE group_features ADD PRIMARY KEY (group_id)')
end
def down
drop_table :group_features
end
end
# frozen_string_literal: true
class BackfillGroupFeatures < Gitlab::Database::Migration[1.0]
disable_ddl_transaction!
MIGRATION = 'BackfillGroupFeatures'
INTERVAL = 2.minutes
BATCH_SIZE = 10_000
SUB_BATCH_SIZE = 1_000
def up
queue_batched_background_migration(
MIGRATION,
:namespaces,
:id,
BATCH_SIZE,
job_interval: INTERVAL,
batch_size: BATCH_SIZE,
max_batch_size: BATCH_SIZE,
sub_batch_size: SUB_BATCH_SIZE
)
end
def down
Gitlab::Database::BackgroundMigration::BatchedMigration
.for_configuration(MIGRATION, :namespaces, :id, [BATCH_SIZE])
.delete_all
end
end
4f565a313c37d12f24afe26a9e344d4273c54d0f080b3108d56ed98bf7299ecc
\ No newline at end of file
4e2de14559b47a9bf3fa8910fdb84ff62cb18442620f4147e40e8026bf4bcf1b
\ No newline at end of file
......@@ -15679,6 +15679,13 @@ CREATE SEQUENCE group_deploy_tokens_id_seq
ALTER SEQUENCE group_deploy_tokens_id_seq OWNED BY group_deploy_tokens.id;
CREATE TABLE group_features (
group_id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
updated_at timestamp with time zone NOT NULL,
wiki_access_level smallint DEFAULT 20 NOT NULL
);
CREATE TABLE group_group_links (
id bigint NOT NULL,
created_at timestamp with time zone NOT NULL,
......@@ -24529,6 +24536,9 @@ ALTER TABLE ONLY group_deploy_keys
ALTER TABLE ONLY group_deploy_tokens
ADD CONSTRAINT group_deploy_tokens_pkey PRIMARY KEY (id);
ALTER TABLE ONLY group_features
ADD CONSTRAINT group_features_pkey PRIMARY KEY (group_id);
ALTER TABLE ONLY group_group_links
ADD CONSTRAINT group_group_links_pkey PRIMARY KEY (id);
......@@ -32199,6 +32209,9 @@ ALTER TABLE ONLY requirements
ALTER TABLE ONLY metrics_dashboard_annotations
ADD CONSTRAINT fk_rails_345ab51043 FOREIGN KEY (cluster_id) REFERENCES clusters(id) ON DELETE CASCADE;
ALTER TABLE ONLY group_features
ADD CONSTRAINT fk_rails_356514082b FOREIGN KEY (group_id) REFERENCES namespaces(id) ON DELETE CASCADE;
ALTER TABLE ONLY wiki_page_slugs
ADD CONSTRAINT fk_rails_358b46be14 FOREIGN KEY (wiki_page_meta_id) REFERENCES wiki_page_meta(id) ON DELETE CASCADE;
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Backfill group_features for an array of groups
class BackfillGroupFeatures < ::Gitlab::BackgroundMigration::BaseJob
include Gitlab::Database::DynamicModelHelpers
def perform(start_id, end_id, batch_table, batch_column, sub_batch_size, pause_ms, batch_size)
pause_ms = 0 if pause_ms < 0
parent_batch_relation = relation_scoped_to_range(batch_table, batch_column, start_id, end_id)
parent_batch_relation.each_batch(column: batch_column, of: sub_batch_size, order_hint: :type) do |sub_batch|
batch_metrics.time_operation(:upsert_group_features) do
upsert_group_features(sub_batch, batch_size)
end
sleep(pause_ms * 0.001)
end
end
def batch_metrics
@batch_metrics ||= Gitlab::Database::BackgroundMigration::BatchMetrics.new
end
private
def relation_scoped_to_range(source_table, source_key_column, start_id, stop_id)
define_batchable_model(source_table, connection: connection)
.where(source_key_column => start_id..stop_id)
.where(type: 'Group')
end
def upsert_group_features(relation, batch_size)
connection.execute(
<<~SQL
INSERT INTO group_features (group_id, created_at, updated_at)
SELECT namespaces.id as group_id, now(), now()
FROM namespaces
WHERE namespaces.type = 'Group' AND namespaces.id IN(#{relation.select(:id).limit(batch_size).to_sql})
ON CONFLICT (group_id) DO NOTHING;
SQL
)
end
end
end
end
......@@ -240,6 +240,7 @@ group_deletion_schedules: :gitlab_main
group_deploy_keys: :gitlab_main
group_deploy_keys_groups: :gitlab_main
group_deploy_tokens: :gitlab_main
group_features: :gitlab_main
group_group_links: :gitlab_main
group_import_states: :gitlab_main
group_merge_request_approval_settings: :gitlab_main
......
# frozen_string_literal: true
require 'spec_helper'
RSpec.describe Gitlab::BackgroundMigration::BackfillGroupFeatures, :migration, schema: 20220302114046 do
let(:group_features) { table(:group_features) }
let(:namespaces) { table(:namespaces) }
subject { described_class.new(connection: ActiveRecord::Base.connection) }
describe '#perform' do
it 'creates settings for all group namespaces in range' do
namespaces.create!(id: 1, name: 'group1', path: 'group1', type: 'Group')
namespaces.create!(id: 2, name: 'user', path: 'user')
namespaces.create!(id: 3, name: 'group2', path: 'group2', type: 'Group')
# Checking that no error is raised if the group_feature for a group already exists
namespaces.create!(id: 4, name: 'group3', path: 'group3', type: 'Group')
group_features.create!(id: 1, group_id: 4)
expect(group_features.count).to eq 1
expect { subject.perform(1, 4, :namespaces, :id, 10, 0, 4) }.to change { group_features.count }.by(2)
expect(group_features.count).to eq 3
expect(group_features.all.pluck(:group_id)).to contain_exactly(1, 3, 4)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
require_migration!
RSpec.describe BackfillGroupFeatures, :migration do
let(:migration) { described_class::MIGRATION }
describe '#up' do
it 'schedules background jobs for each batch of namespaces' do
migrate!
expect(migration).to have_scheduled_batched_migration(
table_name: :namespaces,
column_name: :id,
job_arguments: [described_class::BATCH_SIZE],
interval: described_class::INTERVAL,
batch_size: described_class::BATCH_SIZE
)
end
end
describe '#down' do
it 'deletes all batched migration records' do
migrate!
schema_migrate_down!
expect(migration).not_to have_scheduled_batched_migration
end
end
end
......@@ -41,6 +41,7 @@ RSpec.describe Group do
it { is_expected.to have_many(:contacts).class_name('CustomerRelations::Contact') }
it { is_expected.to have_many(:organizations).class_name('CustomerRelations::Organization') }
it { is_expected.to have_one(:crm_settings) }
it { is_expected.to have_one(:group_feature) }
describe '#members & #requesters' do
let(:requester) { create(:user) }
......@@ -295,6 +296,21 @@ RSpec.describe Group do
it_behaves_like 'a BulkUsersByEmailLoad model'
context 'after initialized' do
it 'has a group_feature' do
expect(described_class.new.group_feature).to be_present
end
end
context 'when creating a new project' do
let_it_be(:group) { create(:group) }
it 'automatically creates the groups feature for the group' do
expect(group.group_feature).to be_an_instance_of(Groups::FeatureSetting)
expect(group.group_feature).to be_persisted
end
end
context 'traversal_ids on create' do
context 'default traversal_ids' do
let(:group) { build(: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