Skip to content

Commit 5bd72e6

Browse files
authored
Refactor the package lookups into a function #295 (#300)
Signed-off-by: tdruez <[email protected]>
1 parent e3f3fbb commit 5bd72e6

File tree

2 files changed

+86
-23
lines changed

2 files changed

+86
-23
lines changed

product_portfolio/importers.py

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import json
1010
from collections import defaultdict
11+
from contextlib import suppress
1112

1213
from django import forms
1314
from django.core.exceptions import ValidationError
@@ -692,29 +693,8 @@ def import_package(self, package_data):
692693
# Vulnerabilities are fetched post import.
693694
package_data.pop("affected_by_vulnerabilities", None)
694695

695-
unique_together_lookups = {
696-
field: package_data.get(field, "") for field in self.unique_together_fields
697-
}
698-
699-
# Check if the Package already exists in the local Dataspace
700-
try:
701-
package = Package.objects.scope(self.user.dataspace).get(**unique_together_lookups)
702-
self.existing["package"].append(str(package))
703-
except ObjectDoesNotExist:
704-
package = None
705-
706-
# Check if the Package already exists in the reference Dataspace
707-
reference_dataspace = Dataspace.objects.get_reference()
708-
user_dataspace = self.user.dataspace
709-
if not package and user_dataspace != reference_dataspace:
710-
qs = Package.objects.scope(reference_dataspace).filter(**unique_together_lookups)
711-
if qs.exists():
712-
reference_object = qs.first()
713-
try:
714-
package = copy_object(reference_object, user_dataspace, self.user, update=False)
715-
self.created["package"].append(str(package))
716-
except IntegrityError as error:
717-
self.errors["package"].append(str(error))
696+
# Check if the package already exists to prevent duplication.
697+
package = self.look_for_existing_package(package_data)
718698

719699
if license_expression := package_data.get("declared_license_expression"):
720700
license_expression = str(self.licensing.dedup(license_expression))
@@ -786,3 +766,43 @@ def import_dependency(self, dependency_data):
786766
return
787767

788768
self.created["dependency"].append(str(dependency.dependency_uid))
769+
770+
def look_for_existing_package(self, package_data):
771+
package = None
772+
package_qs = Package.objects.scope(self.user.dataspace)
773+
774+
# 1. Check if the Package already exists in the local Dataspace
775+
# Using exact match first: purl + download_url + filename
776+
exact_match_lookups = {
777+
field: package_data.get(field, "") for field in self.unique_together_fields
778+
}
779+
with suppress(ObjectDoesNotExist):
780+
package = package_qs.get(**exact_match_lookups)
781+
self.existing["package"].append(str(package))
782+
return package
783+
784+
# 2. If the package data does not include a download_url value:
785+
# Attemp to find an existing package using purl-only match.
786+
if not package_data.get("download_url"):
787+
purl_lookups = {field: package_data.get(field, "") for field in PACKAGE_URL_FIELDS}
788+
same_purl_packages = package_qs.filter(**purl_lookups)
789+
if len(same_purl_packages) == 1:
790+
package = same_purl_packages[0]
791+
self.existing["package"].append(str(package))
792+
return package
793+
794+
# Check if the Package already exists in the reference Dataspace
795+
# Using exact match only: purl + download_url + filename
796+
reference_dataspace = Dataspace.objects.get_reference()
797+
user_dataspace = self.user.dataspace
798+
if not package and user_dataspace != reference_dataspace:
799+
qs = Package.objects.scope(reference_dataspace).filter(**exact_match_lookups)
800+
if qs.exists():
801+
reference_object = qs.first()
802+
try:
803+
package = copy_object(reference_object, user_dataspace, self.user, update=False)
804+
self.created["package"].append(str(package))
805+
except IntegrityError as error:
806+
self.errors["package"].append(str(error))
807+
808+
return package

product_portfolio/tests/test_importers.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,6 +1121,49 @@ def test_product_portfolio_import_packages_from_scio_importer_multiple_package_o
11211121
self.assertEqual({}, existing)
11221122
self.assertEqual({}, errors)
11231123

1124+
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_dependencies")
1125+
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_packages")
1126+
def test_product_portfolio_import_packages_from_scio_importer_look_for_existing_package(
1127+
self, mock_fetch_packages, mock_fetch_dependencies
1128+
):
1129+
purl = "pkg:maven/org.apache.activemq/[email protected]"
1130+
filename = "activemq-camel.zip"
1131+
download_url = "https://download.url/activemq-camel.zip"
1132+
package1 = make_package(self.dataspace, package_url=purl)
1133+
package2 = make_package(self.dataspace, package_url=purl, filename=filename)
1134+
1135+
package_data = {
1136+
"type": "maven",
1137+
"namespace": "org.apache.activemq",
1138+
"name": "activemq-camel",
1139+
"version": "5.11.0",
1140+
"purl": purl,
1141+
}
1142+
mock_fetch_packages.return_value = [package_data]
1143+
mock_fetch_dependencies.return_value = []
1144+
1145+
importer = ImportPackageFromScanCodeIO(
1146+
user=self.super_user,
1147+
project_uuid=uuid.uuid4(),
1148+
product=self.product1,
1149+
)
1150+
1151+
# Check if the Package already exists in the local Dataspace
1152+
# Using exact match first: purl + download_url + filename
1153+
package = importer.look_for_existing_package(package_data)
1154+
self.assertEqual(package1, package)
1155+
1156+
# 2 packages are matched, cannot defined the one that should be used
1157+
package1.update(download_url=download_url)
1158+
package = importer.look_for_existing_package(package_data)
1159+
self.assertIsNone(package)
1160+
1161+
# If the package data does not include a download_url value:
1162+
# Attemp to find an existing package using purl-only match.
1163+
package2.delete()
1164+
package = importer.look_for_existing_package(package_data)
1165+
self.assertEqual(package1, package)
1166+
11241167
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_dependencies")
11251168
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.fetch_project_packages")
11261169
def test_product_portfolio_import_packages_from_scio_importer_duplicate_dependency(

0 commit comments

Comments
 (0)