diff --git a/ee/lib/gitlab/license_scanning/package_licenses.rb b/ee/lib/gitlab/license_scanning/package_licenses.rb index d6f64c4aebcf870c9ef38fb393bb6175421414c2..6a4bcfe0fc6feffe73e9810e36648de2b9139a27 100644 --- a/ee/lib/gitlab/license_scanning/package_licenses.rb +++ b/ee/lib/gitlab/license_scanning/package_licenses.rb @@ -4,6 +4,7 @@ module Gitlab module LicenseScanning class PackageLicenses include Gitlab::InternalEventsTracking + include Gitlab::Utils::StrongMemoize BATCH_SIZE = 700 UNKNOWN_LICENSE = { @@ -136,7 +137,7 @@ def add_components_without_licenses(components_without_licenses) packages_for_batch.each do |package| requested_data_for_package(package).each do |component| - license_ids = package.license_ids_for(version: component[:version]) + license_ids = license_ids_for(package, component[:version]) next if license_ids.empty? @@ -150,6 +151,12 @@ def add_components_without_licenses(components_without_licenses) end end + def license_ids_for(package, version) + strong_memoize_with(:license_ids_for, package, version) do + package.license_ids_for(version: version) + end + end + def requested_data_for_package(package) component_data[component_data_key(name: package.name, purl_type: package.purl_type)] end diff --git a/ee/spec/lib/gitlab/license_scanning/package_licenses_spec.rb b/ee/spec/lib/gitlab/license_scanning/package_licenses_spec.rb index 5d3aacd1717741dcb410a21274e25f613856a479..4badd86cd20fc04b5b2ab9b2c56fa16daaa6283c 100644 --- a/ee/spec/lib/gitlab/license_scanning/package_licenses_spec.rb +++ b/ee/spec/lib/gitlab/license_scanning/package_licenses_spec.rb @@ -729,5 +729,54 @@ end end end + + context 'when processing identical components' do + let_it_be(:components_to_fetch) do + [ + Hashie::Mash.new({ name: "beego", purl_type: "golang", version: "v1.10.0", path: nil }), + Hashie::Mash.new({ name: "beego", purl_type: "golang", version: "v1.10.0", path: nil }), + Hashie::Mash.new({ name: "camelcase", purl_type: "npm", version: "1.2.1", path: "" }), + Hashie::Mash.new({ name: "camelcase", purl_type: "npm", version: "4.1.0", path: "package-lock.json" }), + Hashie::Mash.new({ name: "cliui", purl_type: "npm", version: "2.1.0", path: "package-lock.json" }), + Hashie::Mash.new({ name: "cliui", purl_type: "npm", version: "2.1.0", path: "package-lock.json" }), + Hashie::Mash.new({ name: "cliui", purl_type: "golang", version: "2.1.0", path: "package-lock.json" }), + Hashie::Mash.new({ name: "cliui", purl_type: "golang", version: "2.1.1", path: "package-lock.json" }) + ] + end + + let(:package1) do + instance_double(PackageMetadata::Package, name: "beego", purl_type: "golang", + license_ids_for: [1]) + end + + let(:package2) do + instance_double(PackageMetadata::Package, name: "camelcase", purl_type: "npm", + license_ids_for: [1]) + end + + let(:package3) do + instance_double(PackageMetadata::Package, name: "cliui", purl_type: "npm", + license_ids_for: [1]) + end + + let(:package4) do + instance_double(PackageMetadata::Package, name: "cliui", purl_type: "golang", + license_ids_for: [1]) + end + + it 'only calls the model once to get licenses for a package' do + expect(PackageMetadata::Package) + .to receive(:packages_for) + .with(components: components_to_fetch) + .and_return([package1, package2, package3, package4]) + + fetch + + expect(package1).to have_received(:license_ids_for).exactly(1).times + expect(package2).to have_received(:license_ids_for).exactly(2).times + expect(package3).to have_received(:license_ids_for).exactly(1).times + expect(package4).to have_received(:license_ids_for).exactly(2).times + end + end end end