From 6759958bef4eece2529fc189c38cddbd1bf23bfe Mon Sep 17 00:00:00 2001 From: tdruez <489057+tdruez@users.noreply.github.com> Date: Mon, 3 Jun 2024 08:07:19 +0400 Subject: [PATCH] Use "unknown" as the Package URL default type for missing data #1249 (#1251) Signed-off-by: tdruez --- CHANGELOG.rst | 5 +++++ scanpipe/models.py | 28 +++++++++++++--------------- scanpipe/tests/test_models.py | 13 ++++++++++++- 3 files changed, 30 insertions(+), 16 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index d89acd803..d85945392 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -12,6 +12,11 @@ v34.6.0 (unreleased) - Add ability to filter by tag on the resource list view. https://github.com/nexB/scancode.io/issues/1217 +- Use "unknown" as the Package URL default type when no values are provided for that + field. This allows to create a discovered package instance instead of raising a + Project error message. + https://github.com/nexB/scancode.io/issues/1249 + v34.5.0 (2024-05-22) -------------------- diff --git a/scanpipe/models.py b/scanpipe/models.py index a2917e0b8..cf1c71191 100644 --- a/scanpipe/models.py +++ b/scanpipe/models.py @@ -3106,27 +3106,25 @@ def extract_purl_data(cls, package_data): @classmethod def create_from_data(cls, project, package_data): """ - Create and returns a DiscoveredPackage for a `project` from the `package_data`. - If one of the values of the required fields is not available, a "ProjectMessage" - is created instead of a new DiscoveredPackage instance. + Create and return a DiscoveredPackage for a given `project` based on + `package_data`. + + If the required `name` field is missing in `package_data`, a `ProjectMessage` + is created instead of a DiscoveredPackage instance. + + If the `type` field is missing in `package_data`, it defaults to "unknown" + before creating the DiscoveredPackage. """ package_data = package_data.copy() - required_fields = ["type", "name"] - missing_values = [ - field_name - for field_name in required_fields - if not package_data.get(field_name) - ] - - if missing_values: - message = ( - f"No values for the following required fields: " - f"{', '.join(missing_values)}" - ) + if not package_data.get("name"): + message = 'No values provided for the required "name" field.' project.add_warning(description=message, model=cls, details=package_data) return + if not package_data.get("type"): + package_data["type"] = "unknown" + qualifiers = package_data.get("qualifiers") if qualifiers: package_data["qualifiers"] = normalize_qualifiers(qualifiers, encode=True) diff --git a/scanpipe/tests/test_models.py b/scanpipe/tests/test_models.py index 16e4b95db..87302c555 100644 --- a/scanpipe/tests/test_models.py +++ b/scanpipe/tests/test_models.py @@ -2447,7 +2447,7 @@ def test_scanpipe_discovered_package_model_create_from_data(self): self.assertEqual(package_count, DiscoveredPackage.objects.count()) error = project1.projectmessages.latest("created_date") self.assertEqual("DiscoveredPackage", error.model) - expected_message = "No values for the following required fields: name" + expected_message = 'No values provided for the required "name" field.' self.assertEqual(expected_message, error.description) self.assertEqual(package_data1["purl"], error.details["purl"]) self.assertEqual("", error.details["name"]) @@ -2466,6 +2466,17 @@ def test_scanpipe_discovered_package_model_create_from_data(self): self.assertEqual(package_count, DiscoveredPackage.objects.count()) self.assertEqual(project_message_count, ProjectMessage.objects.count()) + def test_scanpipe_discovered_package_model_create_from_data_missing_type(self): + project1 = Project.objects.create(name="Analysis") + + incomplete_data = dict(package_data1) + incomplete_data["type"] = "" + + package = DiscoveredPackage.create_from_data(project1, incomplete_data) + self.assertEqual(project1, package.project) + self.assertEqual("pkg:unknown/debian/adduser@3.118?arch=all", str(package)) + self.assertEqual("unknown", package.type) + @skipIf(connection.vendor == "sqlite", "No max_length constraints on SQLite.") def test_scanpipe_discovered_dependency_model_create_from_data(self): project1 = Project.objects.create(name="Analysis")