# frozen_string_literal: true module Packages module Npm class DeprecatePackageService < BaseService Deprecated = Struct.new(:package_id, :message) BATCH_SIZE = 50 def initialize(project, params) super(project, nil, params) end def execute(async: false) return ::Packages::Npm::DeprecatePackageWorker.perform_async(project.id, filtered_params) if async packages.select(:id, :version).each_batch(of: BATCH_SIZE) do |relation| deprecated_metadatum = handle_batch(relation) update_metadatum(deprecated_metadatum) end end private # To avoid passing the whole metadata to the worker def filtered_params { package_name: params[:package_name], versions: params[:versions].transform_values { |version| version.slice(:deprecated) } } end def packages ::Packages::Npm::PackageFinder .new(params['package_name'], project: project, last_of_each_version: false) .execute end def handle_batch(relation) relation .preload_npm_metadatum .filter_map { |package| deprecate(package) } end def deprecate(package) deprecation_message = params.dig('versions', package.version, 'deprecated') return if deprecation_message.nil? npm_metadatum = package.npm_metadatum return if identical?(npm_metadatum.package_json['deprecated'], deprecation_message) Deprecated.new(npm_metadatum.package_id, deprecation_message) end def identical?(package_json_deprecated, deprecation_message) package_json_deprecated == deprecation_message || (package_json_deprecated.nil? && deprecation_message.empty?) end def update_metadatum(deprecated_metadatum) return if deprecated_metadatum.empty? deprecation_message = deprecated_metadatum.first.message ::Packages::Npm::Metadatum .package_id_in(deprecated_metadatum.map(&:package_id)) .update_all(update_clause(deprecation_message)) end def update_clause(deprecation_message) if deprecation_message.empty? "package_json = package_json - 'deprecated'" else ["package_json = jsonb_set(package_json, '{deprecated}', ?)", deprecation_message.to_json] end end end end end