debian-mirror-gitlab/db/post_migrate/20201005094331_migrate_compliance_framework_enum_to_database_framework_record.rb
2021-01-03 14:25:43 +05:30

103 lines
3.7 KiB
Ruby

# 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