Commit 84038b63 authored by James Lopez's avatar James Lopez

Merge branch 'tc-create-project-settings' into 'master'

Introduce project_settings table

See merge request gitlab-org/gitlab!19761
parents ea9ee18c 4fb5926f
......@@ -190,6 +190,7 @@ class Project < ApplicationRecord
has_one :error_tracking_setting, inverse_of: :project, class_name: 'ErrorTracking::ProjectErrorTrackingSetting'
has_one :metrics_setting, inverse_of: :project, class_name: 'ProjectMetricsSetting'
has_one :grafana_integration, inverse_of: :project
has_one :project_setting, ->(project) { where_or_create_by(project: project) }, inverse_of: :project
# Merge Requests for target project should be removed with it
has_many :merge_requests, foreign_key: 'target_project_id', inverse_of: :target_project
......
# frozen_string_literal: true
class ProjectSetting < ApplicationRecord
belongs_to :project, inverse_of: :project_setting
self.primary_key = :project_id
def self.where_or_create_by(attrs)
where(primary_key => safe_find_or_create_by(attrs))
end
end
......@@ -90,6 +90,7 @@ module Projects
end
@project.track_project_repository
@project.create_project_setting unless @project.project_setting
event_service.create_project(@project, current_user)
system_hook_service.execute_hooks_for(@project, :create)
......
---
title: Introduce project_settings table
merge_request: 19761
author:
type: added
# frozen_string_literal: true
class CreateProjectSettings < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
create_table :project_settings, id: false do |t|
t.timestamps_with_timezone null: false
t.references :project, primary_key: true, default: nil, type: :integer, index: false, foreign_key: { on_delete: :cascade }
end
end
end
# frozen_string_literal: true
class BackfillProjectSettings < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
MIGRATION = 'BackfillProjectSettings'
DELAY_INTERVAL = 2.minutes
BATCH_SIZE = 10_000
disable_ddl_transaction!
class Project < ActiveRecord::Base
include EachBatch
self.table_name = 'projects'
end
def up
say "Scheduling `#{MIGRATION}` jobs"
queue_background_migration_jobs_by_range_at_intervals(Project, MIGRATION, DELAY_INTERVAL, batch_size: BATCH_SIZE)
end
def down
# NOOP
end
end
......@@ -3296,6 +3296,11 @@ ActiveRecord::Schema.define(version: 2020_02_06_111847) do
t.index ["project_id"], name: "index_project_repository_states_on_project_id", unique: true
end
create_table "project_settings", primary_key: "project_id", id: :integer, default: nil, force: :cascade do |t|
t.datetime_with_timezone "created_at", null: false
t.datetime_with_timezone "updated_at", null: false
end
create_table "project_statistics", id: :serial, force: :cascade do |t|
t.integer "project_id", null: false
t.integer "namespace_id", null: false
......@@ -4881,6 +4886,7 @@ ActiveRecord::Schema.define(version: 2020_02_06_111847) do
add_foreign_key "project_repositories", "projects", on_delete: :cascade
add_foreign_key "project_repositories", "shards", on_delete: :restrict
add_foreign_key "project_repository_states", "projects", on_delete: :cascade
add_foreign_key "project_settings", "projects", on_delete: :cascade
add_foreign_key "project_statistics", "projects", on_delete: :cascade
add_foreign_key "project_tracing_settings", "projects", on_delete: :cascade
add_foreign_key "projects", "pool_repositories", name: "fk_6e5c14658a", on_delete: :nullify
......
# frozen_string_literal: true
module Gitlab
module BackgroundMigration
# Backfill project_settings for a range of projects
class BackfillProjectSettings
def perform(start_id, end_id)
ActiveRecord::Base.connection.execute <<~SQL
INSERT INTO project_settings (project_id, created_at, updated_at)
SELECT projects.id, now(), now()
FROM projects
WHERE projects.id BETWEEN #{start_id} AND #{end_id}
ON CONFLICT (project_id) DO NOTHING;
SQL
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::BackgroundMigration::BackfillProjectSettings, :migration, schema: 20200114113341 do
let(:projects) { table(:projects) }
let(:project_settings) { table(:project_settings) }
let(:namespace) { table(:namespaces).create(name: 'user', path: 'user') }
let(:project) { projects.create(namespace_id: namespace.id) }
subject { described_class.new }
describe '#perform' do
it 'creates settings for all projects in range' do
projects.create(id: 5, namespace_id: namespace.id)
projects.create(id: 7, namespace_id: namespace.id)
projects.create(id: 8, namespace_id: namespace.id)
subject.perform(5, 7)
expect(project_settings.all.pluck(:project_id)).to contain_exactly(5, 7)
end
end
end
......@@ -452,6 +452,7 @@ project:
- package_files
- tracing_setting
- alerting_setting
- project_setting
- webide_pipelines
- reviews
- incident_management_setting
......@@ -613,4 +614,4 @@ epic:
- due_date_sourcing_epic
- events
- resource_label_events
- user_mentions
\ No newline at end of file
- user_mentions
# frozen_string_literal: true
require 'spec_helper'
require Rails.root.join('db', 'post_migrate', '20200122123016_backfill_project_settings.rb')
describe BackfillProjectSettings, :migration, :sidekiq, schema: 20200114113341 do
let(:projects) { table(:projects) }
let(:namespace) { table(:namespaces).create(name: 'user', path: 'user') }
let(:project) { projects.create(namespace_id: namespace.id) }
describe '#up' do
before do
stub_const("#{described_class}::BATCH_SIZE", 2)
projects.create(id: 1, namespace_id: namespace.id)
projects.create(id: 2, namespace_id: namespace.id)
projects.create(id: 3, namespace_id: namespace.id)
end
it 'schedules BackfillProjectSettings background jobs' do
Sidekiq::Testing.fake! do
Timecop.freeze do
migrate!
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(2.minutes, 1, 2)
expect(described_class::MIGRATION).to be_scheduled_delayed_migration(4.minutes, 3, 3)
expect(BackgroundMigrationWorker.jobs.size).to eq(2)
end
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe ProjectSetting, type: :model do
it { is_expected.to belong_to(:project) }
end
......@@ -69,6 +69,7 @@ describe Project do
it { is_expected.to have_one(:forked_from_project).through(:fork_network_member) }
it { is_expected.to have_one(:auto_devops).class_name('ProjectAutoDevops') }
it { is_expected.to have_one(:error_tracking_setting).class_name('ErrorTracking::ProjectErrorTrackingSetting') }
it { is_expected.to have_one(:project_setting) }
it { is_expected.to have_many(:commit_statuses) }
it { is_expected.to have_many(:ci_pipelines) }
it { is_expected.to have_many(:builds) }
......@@ -155,6 +156,11 @@ describe Project do
expect(project.pages_metadatum).to be_an_instance_of(ProjectPagesMetadatum)
expect(project.pages_metadatum).to be_persisted
end
it 'automatically creates a project setting row' do
expect(project.project_setting).to be_an_instance_of(ProjectSetting)
expect(project.project_setting).to be_persisted
end
end
context 'updating cd_cd_settings' do
......
......@@ -43,6 +43,12 @@ describe Projects::CreateService, '#execute' do
create_project(user, opts)
end
it 'creates associated project settings' do
project = create_project(user, opts)
expect(project.project_setting).to be_persisted
end
end
context "admin creates project with other user's namespace_id" 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