Commit 2a6b4e45 authored by James Lopez's avatar James Lopez

Merge branch '209831-improve-propagate-service' into 'master'

Extract PropagateService mixin

See merge request gitlab-org/gitlab!41701
parents d96d4e10 2f8fa628
# frozen_string_literal: true # frozen_string_literal: true
class ServiceList class ServiceList
def initialize(batch, service_hash, extra_hash = {}) def initialize(batch_ids, service_hash)
@batch = batch @batch_ids = batch_ids
@service_hash = service_hash @service_hash = service_hash
@extra_hash = extra_hash
end end
def to_array def to_array
...@@ -13,15 +12,15 @@ class ServiceList ...@@ -13,15 +12,15 @@ class ServiceList
private private
attr_reader :batch, :service_hash, :extra_hash attr_reader :batch_ids, :service_hash
def columns def columns
(service_hash.keys << 'project_id') + extra_hash.keys (service_hash.keys << 'project_id')
end end
def values def values
batch.map do |project_id| batch_ids.map do |project_id|
(service_hash.values << project_id) + extra_hash.values (service_hash.values << project_id)
end end
end end
end end
...@@ -2,17 +2,7 @@ ...@@ -2,17 +2,7 @@
module Admin module Admin
class PropagateIntegrationService class PropagateIntegrationService
BATCH_SIZE = 100 include PropagateService
delegate :data_fields_present?, to: :integration
def self.propagate(integration)
new(integration).propagate
end
def initialize(integration)
@integration = integration
end
def propagate def propagate
update_inherited_integrations update_inherited_integrations
...@@ -21,8 +11,6 @@ module Admin ...@@ -21,8 +11,6 @@ module Admin
private private
attr_reader :integration
# rubocop: disable Cop/InBatches # rubocop: disable Cop/InBatches
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def update_inherited_integrations def update_inherited_integrations
...@@ -50,61 +38,9 @@ module Admin ...@@ -50,61 +38,9 @@ module Admin
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def create_integration_for_projects_without_integration
loop do
batch = Project.uncached { Project.ids_without_integration(integration, BATCH_SIZE) }
bulk_create_from_integration(batch) unless batch.empty?
break if batch.size < BATCH_SIZE
end
end
def bulk_create_from_integration(batch)
service_list = ServiceList.new(batch, service_hash, { 'inherit_from_id' => integration.id }).to_array
Project.transaction do
results = bulk_insert(*service_list)
if data_fields_present?
data_list = DataList.new(results, data_fields_hash, integration.data_fields.class).to_array
bulk_insert(*data_list)
end
run_callbacks(batch)
end
end
def bulk_insert(klass, columns, values_array)
items_to_insert = values_array.map { |array| Hash[columns.zip(array)] }
klass.insert_all(items_to_insert, returning: [:id])
end
# rubocop: disable CodeReuse/ActiveRecord
def run_callbacks(batch)
if integration.issue_tracker?
Project.where(id: batch).update_all(has_external_issue_tracker: true)
end
if active_external_wiki?
Project.where(id: batch).update_all(has_external_wiki: true)
end
end
# rubocop: enable CodeReuse/ActiveRecord
def active_external_wiki?
integration.type == 'ExternalWikiService'
end
def service_hash def service_hash
@service_hash ||= integration.to_service_hash @service_hash ||= integration.to_service_hash
.tap { |json| json['inherit_from_id'] = integration.id } .tap { |json| json['inherit_from_id'] = integration.id }
end end
def data_fields_hash
@data_fields_hash ||= integration.to_data_fields_hash
end
end end
end end
# frozen_string_literal: true
module Admin
class PropagateServiceTemplate
include PropagateService
def propagate
return unless integration.active?
create_integration_for_projects_without_integration
end
private
def service_hash
@service_hash ||= integration.to_service_hash
end
end
end
# frozen_string_literal: true # frozen_string_literal: true
module Projects module Admin
class PropagateServiceTemplate module PropagateService
BATCH_SIZE = 100 extend ActiveSupport::Concern
delegate :data_fields_present?, to: :template BATCH_SIZE = 100
def self.propagate(template) delegate :data_fields_present?, to: :integration
new(template).propagate
end
def initialize(template) class_methods do
@template = template def propagate(integration)
new(integration).propagate
end
end end
def propagate def initialize(integration)
return unless template.active? @integration = integration
propagate_projects_with_template
end end
private private
attr_reader :template attr_reader :integration
def propagate_projects_with_template def create_integration_for_projects_without_integration
loop do loop do
batch = Project.uncached { Project.ids_without_integration(template, BATCH_SIZE) } batch_ids = Project.uncached { Project.ids_without_integration(integration, BATCH_SIZE) }
bulk_create_from_template(batch) unless batch.empty? bulk_create_from_integration(batch_ids) unless batch_ids.empty?
break if batch.size < BATCH_SIZE break if batch_ids.size < BATCH_SIZE
end end
end end
def bulk_create_from_template(batch) def bulk_create_from_integration(batch_ids)
service_list = ServiceList.new(batch, service_hash).to_array service_list = ServiceList.new(batch_ids, service_hash).to_array
Project.transaction do Service.transaction do
results = bulk_insert(*service_list) results = bulk_insert(*service_list)
if data_fields_present? if data_fields_present?
data_list = DataList.new(results, data_fields_hash, template.data_fields.class).to_array data_list = DataList.new(results, data_fields_hash, integration.data_fields.class).to_array
bulk_insert(*data_list) bulk_insert(*data_list)
end end
run_callbacks(batch) run_callbacks(batch_ids)
end end
end end
...@@ -56,28 +54,20 @@ module Projects ...@@ -56,28 +54,20 @@ module Projects
klass.insert_all(items_to_insert, returning: [:id]) klass.insert_all(items_to_insert, returning: [:id])
end end
def service_hash
@service_hash ||= template.to_service_hash
end
def data_fields_hash
@data_fields_hash ||= template.to_data_fields_hash
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def run_callbacks(batch) def run_callbacks(batch_ids)
if template.issue_tracker? if integration.issue_tracker?
Project.where(id: batch).update_all(has_external_issue_tracker: true) Project.where(id: batch_ids).update_all(has_external_issue_tracker: true)
end end
if active_external_wiki? if integration.type == 'ExternalWikiService'
Project.where(id: batch).update_all(has_external_wiki: true) Project.where(id: batch_ids).update_all(has_external_wiki: true)
end end
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
def active_external_wiki? def data_fields_hash
template.type == 'ExternalWikiService' @data_fields_hash ||= integration.to_data_fields_hash
end end
end end
end end
...@@ -12,7 +12,7 @@ class PropagateServiceTemplateWorker # rubocop:disable Scalability/IdempotentWor ...@@ -12,7 +12,7 @@ class PropagateServiceTemplateWorker # rubocop:disable Scalability/IdempotentWor
def perform(template_id) def perform(template_id)
return unless try_obtain_lease_for(template_id) return unless try_obtain_lease_for(template_id)
Projects::PropagateServiceTemplate.propagate(Service.find_by(id: template_id)) Admin::PropagateServiceTemplate.propagate(Service.find_by(id: template_id))
end end
# rubocop: enable CodeReuse/ActiveRecord # rubocop: enable CodeReuse/ActiveRecord
......
...@@ -2,10 +2,10 @@ ...@@ -2,10 +2,10 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Projects::PropagateServiceTemplate do RSpec.describe Admin::PropagateServiceTemplate do
describe '.propagate' do describe '.propagate' do
let!(:service_template) do let!(:service_template) do
PushoverService.create( PushoverService.create!(
template: true, template: true,
active: true, active: true,
push_events: false, push_events: false,
...@@ -31,8 +31,7 @@ RSpec.describe Projects::PropagateServiceTemplate do ...@@ -31,8 +31,7 @@ RSpec.describe Projects::PropagateServiceTemplate do
end end
it 'creates services for a project that has another service' do it 'creates services for a project that has another service' do
BambooService.create( BambooService.create!(
template: true,
active: true, active: true,
project: project, project: project,
properties: { properties: {
...@@ -51,7 +50,7 @@ RSpec.describe Projects::PropagateServiceTemplate do ...@@ -51,7 +50,7 @@ RSpec.describe Projects::PropagateServiceTemplate do
end end
it 'does not create the service if it exists already' do it 'does not create the service if it exists already' do
other_service = BambooService.create( other_service = BambooService.create!(
template: true, template: true,
active: true, active: true,
properties: { properties: {
...@@ -110,7 +109,7 @@ RSpec.describe Projects::PropagateServiceTemplate do ...@@ -110,7 +109,7 @@ RSpec.describe Projects::PropagateServiceTemplate do
let(:project_total) { 5 } let(:project_total) { 5 }
before do before do
stub_const 'Projects::PropagateServiceTemplate::BATCH_SIZE', 3 stub_const('Admin::PropagateServiceTemplate::BATCH_SIZE', 3)
project_total.times { create(:project) } project_total.times { create(:project) }
......
...@@ -21,7 +21,7 @@ RSpec.describe PropagateServiceTemplateWorker do ...@@ -21,7 +21,7 @@ RSpec.describe PropagateServiceTemplateWorker do
stub_exclusive_lease("propagate_service_template_worker:#{template.id}", stub_exclusive_lease("propagate_service_template_worker:#{template.id}",
timeout: PropagateServiceTemplateWorker::LEASE_TIMEOUT) timeout: PropagateServiceTemplateWorker::LEASE_TIMEOUT)
expect(Projects::PropagateServiceTemplate) expect(Admin::PropagateServiceTemplate)
.to receive(:propagate) .to receive(:propagate)
.with(template) .with(template)
......
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