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

20201005094331_migrate_compliance_framework_enum_to_database_framework_record.rb « post_migrate « db - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: a5fab8576e44c103404d867435224eaada92bea7 (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
# frozen_string_literal: true

class MigrateComplianceFrameworkEnumToDatabaseFrameworkRecord < ActiveRecord::Migration[6.0]
  include Gitlab::Database::MigrationHelpers

  DOWNTIME = false

  disable_ddl_transaction!

  class TmpComplianceFramework < ActiveRecord::Base
    self.table_name = 'compliance_management_frameworks'
  end

  class TmpProjectSettings < ActiveRecord::Base
    # Maps data between ComplianceManagement::ComplianceFramework::FRAMEWORKS(enum) and new ComplianceManagement::Framework model
    ENUM_FRAMEWORK_MAPPING = {
      1 => {
        name: 'GDPR',
        description: 'General Data Protection Regulation',
        color: '#1aaa55'
      }.freeze,
      2 => {
        name: 'HIPAA',
        description: 'Health Insurance Portability and Accountability Act',
        color: '#1f75cb'
      }.freeze,
      3 => {
        name: 'PCI-DSS',
        description: 'Payment Card Industry-Data Security Standard',
        color: '#6666c4'
      }.freeze,
      4 => {
        name: 'SOC 2',
        description: 'Service Organization Control 2',
        color: '#dd2b0e'
      }.freeze,
      5 => {
        name: 'SOX',
        description: 'Sarbanes-Oxley',
        color: '#fc9403'
      }.freeze
    }.freeze

    self.table_name = 'project_compliance_framework_settings'

    include EachBatch

    def raw_compliance_framework
      # Because we have an `enum` definition in ComplianceManagement::ComplianceFramework::ProjectSettings, this is very unlikely to fail.
      ENUM_FRAMEWORK_MAPPING.fetch(framework).merge(namespace_id: root_namespace_id)
    end
  end

  def up
    TmpComplianceFramework.reset_column_information
    TmpProjectSettings.reset_column_information

    # This is our standard recursive namespace query, we use it to determine the root_namespace_id in the same query.
    lateral_join = <<~SQL
      INNER JOIN LATERAL (
        WITH RECURSIVE "base_and_ancestors" AS (
          (
            SELECT "ns".* FROM "namespaces" as ns WHERE "ns"."id" = projects.namespace_id
          ) UNION
          (
             SELECT "ns".* FROM "namespaces" as ns, "base_and_ancestors" WHERE "ns"."id" = "base_and_ancestors"."parent_id"
          )
        ) SELECT "namespaces".* FROM "base_and_ancestors" AS "namespaces" WHERE parent_id IS NULL LIMIT 1) AS root_namespaces ON TRUE
    SQL

    TmpProjectSettings.each_batch(of: 100) do |query|
      project_settings_with_root_group = query
        .select(:project_id, :framework, 'root_namespaces.id as root_namespace_id')
        .from("(SELECT * FROM project_compliance_framework_settings) as project_compliance_framework_settings") # this is needed for the LATERAL JOIN
        .joins("INNER JOIN projects on projects.id = project_compliance_framework_settings.project_id")
        .joins(lateral_join)
        .to_a

      ActiveRecord::Base.transaction do
        raw_frameworks = project_settings_with_root_group.map(&:raw_compliance_framework)
        TmpComplianceFramework.insert_all(raw_frameworks.uniq) # Create compliance frameworks per group

        unique_namespace_ids = project_settings_with_root_group.map(&:root_namespace_id).uniq

        framework_records = TmpComplianceFramework.select(:id, :namespace_id, :name).where(namespace_id: unique_namespace_ids)

        project_settings_with_root_group.each do |project_setting|
          framework = framework_records.find do |record|
            # name is unique within a group
            record.name == project_setting.raw_compliance_framework[:name] && record[:namespace_id] == project_setting.raw_compliance_framework[:namespace_id]
          end

          project_setting.update_column(:framework_id, framework.id)
        end
      end
    end
  end

  def down
    # data migration, no-op
  end
end