Skip to content

Commit

Permalink
Merge pull request #47 from nexB/14-purldb-add-package
Browse files Browse the repository at this point in the history
Lookup in PurlDB by purl in Add Package
  • Loading branch information
tdruez authored Feb 12, 2024
2 parents 7e32c30 + cc203b1 commit 13617d7
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 9 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ Release notes

### Version 5.0.2-dev

- Lookup in PurlDB by purl in Add Package form.
When a Package URL is available in the context of the "Add Package" form,
for example when using a link from the Vulnerabilities tab,
data is fetched from the PurlDB to initialize the form.
https://github.com/nexB/dejacode/issues/47

### Version 5.0.1

- Improve the stability of the "Check for new Package versions" feature.
Expand Down
47 changes: 47 additions & 0 deletions component_catalog/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -3654,6 +3654,53 @@ def test_component_catalog_package_add_view_create_proper(self):
expected = "Package "name.zip" was successfully created."
self.assertContains(response, expected)

@mock.patch("dejacode_toolkit.purldb.PurlDB.request_get")
@mock.patch("dejacode_toolkit.purldb.PurlDB.is_configured")
def test_component_catalog_package_add_view_initial_data(
self, mock_is_configured, mock_request_get
):
self.client.login(username=self.super_user.username, password="secret")
add_url = reverse("component_catalog:package_add")

mock_is_configured.return_value = True
self.dataspace.enable_purldb_access = True
self.dataspace.save()

puyrldb_entry = {
"filename": "abbot-1.4.0.jar",
"release_date": "2015-09-22",
"type": "maven",
"namespace": "abbot",
"name": "abbot",
"version": "1.4.0",
"qualifiers": "",
"subpath": "",
"primary_language": "Java",
"description": "Abbot Java GUI Test Library",
"declared_license_expression": "bsd-new OR eps-1.0 OR apache-2.0 OR mit",
}
mock_request_get.return_value = {
"count": 1,
"results": [puyrldb_entry],
}

response = self.client.get(add_url)
self.assertEqual({}, response.context["form"].initial)

response = self.client.get(add_url + "?package_url=pkg:maven/abbot/[email protected]")
expected = {
"filename": "abbot-1.4.0.jar",
"release_date": "2015-09-22",
"type": "maven",
"namespace": "abbot",
"name": "abbot",
"version": "1.4.0",
"primary_language": "Java",
"description": "Abbot Java GUI Test Library",
"license_expression": "bsd-new OR eps-1.0 OR apache-2.0 OR mit",
}
self.assertEqual(expected, response.context["form"].initial)

@mock.patch("dje.tasks.scancodeio_submit_scan.delay")
@mock.patch("dejacode_toolkit.scancodeio.ScanCodeIO.is_configured")
def test_component_catalog_package_add_view_create_with_submit_scan(
Expand Down
42 changes: 35 additions & 7 deletions component_catalog/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
from django.contrib.auth.decorators import login_required
from django.contrib.auth.mixins import LoginRequiredMixin
from django.core import signing
from django.core.validators import EMPTY_VALUES
from django.db.models import Prefetch
from django.http import FileResponse
from django.http import Http404
Expand All @@ -45,6 +46,7 @@
from natsort import natsorted
from notifications.signals import notify
from packageurl import PackageURL
from packageurl.contrib import purl2url
from packageurl.contrib import url2purl

from component_catalog.filters import ComponentFilterSet
Expand Down Expand Up @@ -1896,26 +1898,52 @@ def get_initial(self):
"""Pre-fill the form with initial data from a PurlDB entry or a `package_url`."""
initial = super().get_initial()

purldb_uuid = self.request.GET.get("purldb_uuid", None)
if purldb_uuid:
purldb_entry = PurlDB(self.request.user).get_package(purldb_uuid)
if purldb_entry := self.get_entry_from_purldb():
purldb_entry["license_expression"] = purldb_entry.get("declared_license_expression")
model_fields = [field.name for field in Package._meta.get_fields()]
model_fields = [
field.name
for field in Package._meta.get_fields()
# Generic keywords are not supported because of validation
if field.name != "keywords"
]
initial_from_purldb_entry = {
field_name: value
for field_name, value in purldb_entry.items()
if value and field_name in model_fields
if value not in EMPTY_VALUES and field_name in model_fields
}
initial.update(initial_from_purldb_entry)
messages.info(self.request, "Initial data fetched from PurlDB.")

package_url = self.request.GET.get("package_url", None)
if package_url:
elif package_url := self.request.GET.get("package_url", None):
purl = PackageURL.from_string(package_url)
package_url_dict = purl.to_dict(encode=True, empty="")
initial.update(package_url_dict)
if download_url := purl2url.get_download_url(package_url):
initial.update({"download_url": download_url})

return initial

def get_entry_from_purldb(self):
user = self.request.user
purldb = PurlDB(user)
is_purldb_enabled = all(
[
purldb.is_configured(),
user.dataspace.enable_purldb_access,
]
)

if not is_purldb_enabled:
return

purldb_uuid = self.request.GET.get("purldb_uuid", None)
package_url = self.request.GET.get("package_url", None)

if purldb_uuid:
return purldb.get_package(purldb_uuid)
elif package_url:
return purldb.get_package_by_purl(package_url)

def get_success_message(self, cleaned_data):
success_message = super().get_success_message(cleaned_data)

Expand Down
5 changes: 5 additions & 0 deletions dejacode_toolkit/purldb.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ def get_package(self, uuid):
"""Get a Package details entry providing its `uuid`."""
return self.request_get(url=f"{self.package_api_url}{uuid}/")

def get_package_by_purl(self, package_url):
"""Get a Package details entry providing its `package_url`."""
if results := self.find_packages({"purl": package_url}):
return results[0]

def find_packages(self, payload, timeout=None):
"""Get Packages details using provided `payload` filters on the PurlDB package list."""
response = self.request_get(self.package_api_url, params=payload, timeout=timeout)
Expand Down
2 changes: 1 addition & 1 deletion dje/templates/includes/messages_alert.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{% for message in messages %}
{% if wrapper_classes %}<div class="{{ wrapper_classes }}">{% endif %}
<div class="alert alert-{% if message.level_tag == 'error' %}danger{% else %}{{ message.level_tag }}{% endif %} alert-dismissible" role="alert">
<div class="alert alert-{% if message.level_tag == 'error' %}danger{% elif message.level_tag == 'info' %}primary{% else %}{{ message.level_tag }}{% endif %} alert-dismissible" role="alert">
<strong>{{ message.level_tag|title }}:</strong> {{ message|linebreaksbr }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
Expand Down
5 changes: 4 additions & 1 deletion dje/templates/object_form.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ <h1 class="header-title">
</div>
</div>

{% include 'includes/form_errors_alert.html' %}
{% block messages-alert %}
{% include 'includes/form_errors_alert.html' %}
{% include 'includes/messages_alert.html' with wrapper_classes='container p-0' %}
{% endblock %}

<div class="card card-border-color card-border-color-primary">
<div class="card-body bg-light p-4">
Expand Down

0 comments on commit 13617d7

Please sign in to comment.