# frozen_string_literal: true module Gitlab module BackgroundMigration # The class to migrate the link data into their own records from the json attribute class MigrateLinksForVulnerabilityFindings < BatchedMigrationJob feature_category :vulnerability_management operation_name :migrate_links_for_vulnerability_findings # The class is mimicking Vulnerabilites::Finding class Finding < ApplicationRecord self.table_name = 'vulnerability_occurrences' validates :details, json_schema: { filename: 'vulnerability_finding_details', draft: 7 }, if: false end # The class is mimicking Vulnerabilites::FindingLink class Link < ApplicationRecord self.table_name = 'vulnerability_finding_links' end def perform each_sub_batch do |sub_batch| migrate_remediations(sub_batch) end end private def migrate_remediations(sub_batch) sub_batch.each do |finding| links = extract_links(finding.raw_metadata) list_of_attrs = links.map do |link| build_link(finding, link) end next unless list_of_attrs.present? begin create_links(list_of_attrs) rescue ActiveRecord::RecordNotUnique rescue StandardError => e logger.error( message: e.message, class: self.class.name, model_id: finding.id ) end end end def build_link(finding, link) current_time = Time.current { vulnerability_occurrence_id: finding.id, name: link['name'], url: link['url'], created_at: current_time, updated_at: current_time } end def create_links(attributes) Link.upsert_all(attributes, returning: false) end def extract_links(metadata) parsed_metadata = Gitlab::Json.parse(metadata) parsed_links = Array.wrap(parsed_metadata['links']) return [] if parsed_links.blank? parsed_links.select { |link| link.try(:[], 'url').present? }.uniq end def logger @logger ||= ::Gitlab::AppLogger end end end end