Welcome to mirror list, hosted at ThFree Co, Russian Federation.

propagate_service_template.rb « projects « services « app - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 0483c951f1e07dd814895cddbdcb419397a2b6ed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# frozen_string_literal: true

module Projects
  class PropagateServiceTemplate
    BATCH_SIZE = 100

    delegate :data_fields_present?, to: :template

    def self.propagate(template)
      new(template).propagate
    end

    def initialize(template)
      @template = template
    end

    def propagate
      return unless template.active?

      propagate_projects_with_template
    end

    private

    attr_reader :template

    def propagate_projects_with_template
      loop do
        batch = Project.uncached { project_ids_batch }

        bulk_create_from_template(batch) unless batch.empty?

        break if batch.size < BATCH_SIZE
      end
    end

    def bulk_create_from_template(batch)
      service_list = batch.map do |project_id|
        service_hash.values << project_id
      end

      Project.transaction do
        results = bulk_insert(Service, service_hash.keys << 'project_id', service_list)

        if data_fields_present?
          data_list = results.map { |row| data_hash.values << row['id'] }

          bulk_insert(template.data_fields.class, data_hash.keys << 'service_id', data_list)
        end

        run_callbacks(batch)
      end
    end

    def project_ids_batch
      Project.connection.select_values(
        <<-SQL
          SELECT id
          FROM projects
          WHERE NOT EXISTS (
            SELECT true
            FROM services
            WHERE services.project_id = projects.id
            AND services.type = #{ActiveRecord::Base.connection.quote(template.type)}
          )
          AND projects.pending_delete = false
          AND projects.archived = false
          LIMIT #{BATCH_SIZE}
        SQL
      )
    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

    def service_hash
      @service_hash ||= template.as_json(methods: :type, except: %w[id template project_id])
    end

    def data_hash
      @data_hash ||= template.data_fields.as_json(only: template.data_fields.class.column_names).except('id', 'service_id')
    end

    # rubocop: disable CodeReuse/ActiveRecord
    def run_callbacks(batch)
      if active_external_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_issue_tracker?
      template.issue_tracker? && !template.default
    end

    def active_external_wiki?
      template.type == 'ExternalWikiService'
    end
  end
end