|
8 | 8 |
|
9 | 9 | import json |
10 | 10 | from collections import defaultdict |
| 11 | +from contextlib import suppress |
11 | 12 |
|
12 | 13 | from django import forms |
13 | 14 | from django.core.exceptions import ValidationError |
@@ -692,29 +693,8 @@ def import_package(self, package_data): |
692 | 693 | # Vulnerabilities are fetched post import. |
693 | 694 | package_data.pop("affected_by_vulnerabilities", None) |
694 | 695 |
|
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) |
718 | 698 |
|
719 | 699 | if license_expression := package_data.get("declared_license_expression"): |
720 | 700 | license_expression = str(self.licensing.dedup(license_expression)) |
@@ -786,3 +766,43 @@ def import_dependency(self, dependency_data): |
786 | 766 | return |
787 | 767 |
|
788 | 768 | 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 |
0 commit comments