Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor the Webhook.get_payload to use Serializers #1325 #1326

Merged
merged 4 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ v34.7.1 (unreleased)
The `package_uid` is now included in each BOM Component as a property.
https://github.com/nexB/scancode.io/issues/1316

- Add ``results_url`` and ``summary_url`` on the API ProjectSerializer.
https://github.com/nexB/scancode.io/issues/1325

v34.7.0 (2024-07-02)
--------------------

Expand Down
17 changes: 16 additions & 1 deletion scanpipe/api/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from django.apps import apps

from rest_framework import serializers
from rest_framework.reverse import reverse
from taggit.serializers import TaggitSerializer
from taggit.serializers import TagListSerializerField

Expand Down Expand Up @@ -144,6 +145,7 @@ class Meta:

class ProjectSerializer(
ExcludeFromListViewMixin,
SerializerExcludeFieldsMixin,
PipelineChoicesMixin,
TaggitSerializer,
serializers.ModelSerializer,
Expand Down Expand Up @@ -177,6 +179,8 @@ class ProjectSerializer(
discovered_dependencies_summary = serializers.SerializerMethodField()
codebase_relations_summary = serializers.SerializerMethodField()
labels = TagListSerializerField(required=False)
results_url = serializers.SerializerMethodField()
summary_url = serializers.SerializerMethodField()

class Meta:
model = Project
Expand Down Expand Up @@ -210,8 +214,9 @@ class Meta:
"discovered_packages_summary",
"discovered_dependencies_summary",
"codebase_relations_summary",
"results_url",
"summary_url",
)

exclude_from_list_view = [
"settings",
"input_root",
Expand Down Expand Up @@ -258,6 +263,16 @@ def validate_input_urls(self, value):
"""Add support for providing multiple URLs in a single string."""
return [url for entry in value for url in entry.split()]

def get_action_url(self, obj, action_name):
request = self.context.get("request")
return reverse(f"project-{action_name}", kwargs={"pk": obj.pk}, request=request)

def get_results_url(self, obj):
return self.get_action_url(obj, "results")

def get_summary_url(self, obj):
return self.get_action_url(obj, "summary")

def create(self, validated_data):
"""
Create a new `project` with `upload_file` and `pipeline` as optional.
Expand Down
29 changes: 17 additions & 12 deletions scanpipe/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3807,20 +3807,25 @@ def __str__(self):
return str(self.uuid)

def get_payload(self, pipeline_run):
return {
"project": {
"uuid": self.project.uuid,
"name": self.project.name,
"input_sources": self.project.get_inputs_with_source(),
},
"run": {
"uuid": pipeline_run.uuid,
"pipeline_name": pipeline_run.pipeline_name,
"status": pipeline_run.status,
"scancodeio_version": pipeline_run.scancodeio_version,
},
"""Return the Webhook payload generated from project and pipeline_run data."""
from scanpipe.api.serializers import ProjectSerializer
from scanpipe.api.serializers import RunSerializer

project_serializer = ProjectSerializer(
instance=self.project,
exclude_fields=("url", "runs"),
)
run_serializer = RunSerializer(
instance=pipeline_run,
exclude_fields=("url", "project"),
)
payload = {
"project": project_serializer.data,
"run": run_serializer.data,
}

return payload

def deliver(self, pipeline_run):
"""Deliver this Webhook by sending a POST request to the `target_url`."""
payload = self.get_payload(pipeline_run)
Expand Down
13 changes: 2 additions & 11 deletions scanpipe/tests/pipes/test_scancode.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,17 +143,8 @@ def test_scanpipe_pipes_scancode_extract_archive_vmimage_qcow2(self):
self.assertEqual(sorted(expected), sorted(results))

else:
expected = {
str(input_location): [
"Unable to read kernel at: /boot/vmlinuz-6.5.0-1022-azure.\n"
"libguestfs requires the kernel executable to be readable.\n"
"This is the case by default on most Linux distributions except on "
"Ubuntu.\nPlease follow the ExtractCode installation instructions "
"in the README.rst at:\n"
"https://github.com/nexB/extractcode/blob/main/README.rst '\n"
]
}
self.assertEqual(expected, errors)
expected = "libguestfs requires the kernel executable to be readable"
self.assertIn(expected, errors[str(input_location)][0])

def test_scanpipe_pipes_scancode_get_resource_info(self):
input_location = str(self.data / "aboutcode" / "notice.NOTICE")
Expand Down
8 changes: 8 additions & 0 deletions scanpipe/tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,14 @@ def test_scanpipe_api_project_detail(self):
self.assertEqual(1, response.data["package_count"])
self.assertEqual(1, response.data["dependency_count"])
self.assertEqual(1, response.data["relation_count"])
self.assertEqual(
f"http://testserver/api/projects/{self.project1.uuid}/results/",
response.data["results_url"],
)
self.assertEqual(
f"http://testserver/api/projects/{self.project1.uuid}/summary/",
response.data["summary_url"],
)

expected = {"": 1}
self.assertEqual(expected, response.data["codebase_resources_summary"])
Expand Down
63 changes: 62 additions & 1 deletion scanpipe/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -1895,7 +1895,7 @@ def test_scanpipe_codebase_resource_model_walk_method_problematic_filenames(self
self.assertEqual(expected_paths, result)

@mock.patch("requests.post")
def test_scanpipe_webhook_subscription_deliver_method(self, mock_post):
def test_scanpipe_webhook_subscription_model_deliver_method(self, mock_post):
webhook = self.project1.add_webhook_subscription("https://localhost")
self.assertFalse(webhook.delivered)
run1 = self.create_run()
Expand All @@ -1922,6 +1922,67 @@ def test_scanpipe_webhook_subscription_deliver_method(self, mock_post):
self.assertTrue(webhook.success)
self.assertEqual("text", webhook.response_text)

def test_scanpipe_webhook_subscription_model_get_payload(self):
webhook = self.project1.add_webhook_subscription("https://localhost")
run1 = self.create_run()
payload = webhook.get_payload(run1)

expected = {
"project": {
"name": "Analysis",
"uuid": str(self.project1.uuid),
"is_archived": False,
"notes": "",
"labels": [],
"settings": {},
"input_sources": [],
"input_root": [],
"output_root": [],
"next_run": "pipeline",
"extra_data": {},
"message_count": 0,
"resource_count": 0,
"package_count": 0,
"dependency_count": 0,
"relation_count": 0,
"codebase_resources_summary": {},
"discovered_packages_summary": {
"total": 0,
"with_missing_resources": 0,
"with_modified_resources": 0,
},
"discovered_dependencies_summary": {
"total": 0,
"is_runtime": 0,
"is_optional": 0,
"is_resolved": 0,
},
"codebase_relations_summary": {},
"results_url": f"/api/projects/{self.project1.uuid}/results/",
"summary_url": f"/api/projects/{self.project1.uuid}/summary/",
},
"run": {
"pipeline_name": "pipeline",
"status": run1.status,
"description": "",
"selected_groups": None,
"selected_steps": None,
"uuid": str(run1.uuid),
"scancodeio_version": "",
"task_id": None,
"task_start_date": None,
"task_end_date": None,
"task_exitcode": None,
"task_output": "",
"log": "",
"execution_time": None,
},
}

del payload["project"]["created_date"]
del payload["run"]["created_date"]
self.assertDictEqual(expected, payload)

def test_scanpipe_discovered_package_model_extract_purl_data(self):
package_data = {}
expected = {
Expand Down