# 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(batching_scope: ->(relation) { relation.select(:id, :raw_metadata) }) do |findings| migrate_remediations( findings, Link .where(vulnerability_occurrence_id: findings.map(&:id)) .group(:vulnerability_occurrence_id, :name, :url) .count ) end end private def migrate_remediations(findings, existing_links) findings.each do |finding| create_links(build_links_from(finding, existing_links)) rescue ActiveRecord::StatementInvalid => e logger.error( message: e.message, class: self.class.name, model_id: finding.id ) 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 build_links_from(finding, existing_links) extract_links(finding.raw_metadata).filter_map do |link| key = [finding.id, link['name'], link['url']] build_link(finding, link) unless existing_links.key?(key) end end def create_links(attributes) return if attributes.empty? 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 rescue JSON::ParserError => e logger.warn( message: e.message, class: self.class.name ) [] end def logger @logger ||= ::Gitlab::AppLogger end end end end