← Back to team overview

launchpad-reviewers team mailing list archive

[Merge] ~enriqueesanchz/launchpad:add-return-import-vulnerability-job into launchpad:master

 

Enrique Sánchez has proposed merging ~enriqueesanchz/launchpad:add-return-import-vulnerability-job into launchpad:master.

Commit message:
Add VulnerabilityJobDerived.info property

- Use that property in `getImports`, `getExports` and job creation, to
return job info.
- Refactor test_vulnerability job tests
- Remove InformationType title conversion

Requested reviews:
  Launchpad code reviewers (launchpad-reviewers)

For more details, see:
https://code.launchpad.net/~enriqueesanchz/launchpad/+git/launchpad/+merge/494994
-- 
Your team Launchpad code reviewers is requested to review the proposed merge of ~enriqueesanchz/launchpad:add-return-import-vulnerability-job into launchpad:master.
diff --git a/lib/lp/bugs/interfaces/vulnerabilityjob.py b/lib/lp/bugs/interfaces/vulnerabilityjob.py
index f057825..85b3b81 100644
--- a/lib/lp/bugs/interfaces/vulnerabilityjob.py
+++ b/lib/lp/bugs/interfaces/vulnerabilityjob.py
@@ -75,6 +75,12 @@ class IVulnerabilityJob(Interface):
     def destroySelf():
         """Destroy this object."""
 
+    def info(self) -> dict:
+        """Retrieve information about the job.
+
+        :return: A dict of information about the job.
+        """
+
 
 class VulnerabilityJobInProgress(Exception):
     """The VulnerabilityJob for the handler is already in progress."""
@@ -104,7 +110,7 @@ class IImportVulnerabilityJobSource(IJobSource):
     def get(handler):
         """Retrieve the import job for a handler, if any.
 
-        :return: `None` or an `IImportVulnerabilityJobSource`.
+        :return: `None` or an `IImportVulnerabilityJob`.
         """
 
 
@@ -134,7 +140,7 @@ class IExportVulnerabilityJobSource(IJobSource):
     def get(handler):
         """Retrieve the export job for a handler, if any.
 
-        :return: `None` or an `IExportVulnerabilityJobSource`.
+        :return: `None` or an `IExportVulnerabilityJob`.
         """
 
 
diff --git a/lib/lp/bugs/model/exportvulnerabilityjob.py b/lib/lp/bugs/model/exportvulnerabilityjob.py
index 6da2147..077850a 100644
--- a/lib/lp/bugs/model/exportvulnerabilityjob.py
+++ b/lib/lp/bugs/model/exportvulnerabilityjob.py
@@ -111,8 +111,6 @@ class ExportVulnerabilityJob(VulnerabilityJobDerived):
                 "error_description": [],
                 "succeeded": [],
                 "failed": [],
-            },
-            "data": {
                 "export_link": "",
             },
         }
@@ -345,7 +343,7 @@ class ExportVulnerabilityJob(VulnerabilityJobDerived):
             expires=expires,
         )
 
-        self.metadata["data"]["export_link"] = lfa.getURL()
+        self.metadata["result"]["export_link"] = lfa.getURL()
 
     def notifyUserError(self, error):
         """Calls up and also saves the error text in this job's metadata.
diff --git a/lib/lp/bugs/model/tests/test_vulnerability.py b/lib/lp/bugs/model/tests/test_vulnerability.py
index 30f49bc..91015e5 100644
--- a/lib/lp/bugs/model/tests/test_vulnerability.py
+++ b/lib/lp/bugs/model/tests/test_vulnerability.py
@@ -7,12 +7,12 @@ from pathlib import Path
 
 from fixtures import MockPatch
 from storm.store import Store
+from testscenarios.testcase import WithScenarios
 from testtools.matchers import MatchesStructure
 from zope.component import getUtility
 from zope.security.interfaces import Unauthorized
 from zope.security.proxy import removeSecurityProxy
 
-from testscenarios.testcase import WithScenarios
 from lp.app.enums import InformationType
 from lp.app.errors import (
     IncompatibleArguments,
@@ -678,6 +678,27 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
         self.information_type = InformationType.PROPRIETARY
         self.team = self.factory.makeTeam(members=[self.requester])
 
+    def _check_job_attributes(self, job, info, repo, expected_commit_sha1):
+        """Helper to check the common attributes of a created job."""
+        self.assertIsInstance(job, ImportVulnerabilityJob)
+        self.assertEqual(job.git_repository, removeSecurityProxy(repo).id)
+        self.assertEqual(job.git_ref, self.git_ref)
+        self.assertEqual(job.git_paths, self.git_paths)
+        self.assertEqual(job.information_type, self.information_type.value)
+        self.assertEqual(job.import_since_commit_sha1, expected_commit_sha1)
+
+        self.assertEqual(
+            info,
+            {
+                "job_details": {
+                    "created_at": job.job.date_created,
+                    "started_at": job.job.date_started,
+                    "finished_at": job.job.date_finished,
+                },
+                **job.metadata,
+            },
+        )
+
     def test_importData(self):
         """Test that we can create a ImportVulnerabilityJob using importData
         method.
@@ -699,7 +720,7 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
         )
 
         with person_logged_in(self.requester):
-            getUtility(IVulnerabilitySet).importData(
+            info = getUtility(IVulnerabilitySet).importData(
                 self.requester,
                 self.handler,
                 repo,
@@ -709,16 +730,11 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
                 import_since_commit_sha1=None,
             )
 
-            job = getUtility(IImportVulnerabilityJobSource).get(self.handler)
-            naked_job = removeSecurityProxy(job)
-            self.assertIsInstance(naked_job, ImportVulnerabilityJob)
-            self.assertEqual(naked_job.git_repository, repo.id)
-            self.assertEqual(naked_job.git_ref, self.git_ref)
-            self.assertEqual(naked_job.git_paths, self.git_paths)
-            self.assertEqual(
-                naked_job.information_type, self.information_type.value
-            )
-            self.assertEqual(naked_job.import_since_commit_sha1, None)
+        job = getUtility(IImportVulnerabilityJobSource).get(self.handler)
+        naked_job = removeSecurityProxy(job)
+        self._check_job_attributes(
+            naked_job, info, repo, expected_commit_sha1=None
+        )
 
     def test_importData_uct_handler(self):
         """Test that we can create a ImportVulnerabilityJob using importData
@@ -743,7 +759,7 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
 
         handler = VulnerabilityHandlerEnum.UCT
         with person_logged_in(self.requester):
-            getUtility(IVulnerabilitySet).importData(
+            info = getUtility(IVulnerabilitySet).importData(
                 requester,
                 handler,
                 repo,
@@ -753,16 +769,11 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
                 import_since_commit_sha1=None,
             )
 
-            job = getUtility(IImportVulnerabilityJobSource).get(handler)
-            naked_job = removeSecurityProxy(job)
-            self.assertIsInstance(naked_job, ImportVulnerabilityJob)
-            self.assertEqual(naked_job.git_repository, repo.id)
-            self.assertEqual(naked_job.git_ref, self.git_ref)
-            self.assertEqual(naked_job.git_paths, self.git_paths)
-            self.assertEqual(
-                naked_job.information_type, self.information_type.value
-            )
-            self.assertEqual(naked_job.import_since_commit_sha1, None)
+        job = getUtility(IImportVulnerabilityJobSource).get(handler)
+        naked_job = removeSecurityProxy(job)
+        self._check_job_attributes(
+            naked_job, info, repo, expected_commit_sha1=None
+        )
 
     def test_importData_multiple_handlers(self):
         """Test that the feature flag allows to create ImportVulnerabilityJob
@@ -802,10 +813,10 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
                     import_since_commit_sha1=None,
                 )
 
-                job = getUtility(IImportVulnerabilityJobSource).get(handler)
-                naked_job = removeSecurityProxy(job)
-                self.assertIsInstance(naked_job, ImportVulnerabilityJob)
-                self.assertEqual(naked_job.handler, handler)
+            job = getUtility(IImportVulnerabilityJobSource).get(handler)
+            naked_job = removeSecurityProxy(job)
+            self.assertIsInstance(naked_job, ImportVulnerabilityJob)
+            self.assertEqual(naked_job.handler, handler)
 
     def test_importData_feature_flag_disabled(self):
         """Test that if the feature flag is disabled it raises the
@@ -981,7 +992,7 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
 
         import_since_commit_sha1 = "1" * 40
         with person_logged_in(self.requester):
-            getUtility(IVulnerabilitySet).importData(
+            info = getUtility(IVulnerabilitySet).importData(
                 self.requester,
                 self.handler,
                 repo,
@@ -991,18 +1002,14 @@ class TestVulnerabilitySetImportData(TestCaseWithFactory):
                 import_since_commit_sha1,
             )
 
-            job = getUtility(IImportVulnerabilityJobSource).get(self.handler)
-            naked_job = removeSecurityProxy(job)
-            self.assertIsInstance(naked_job, ImportVulnerabilityJob)
-            self.assertEqual(naked_job.git_repository, repo.id)
-            self.assertEqual(naked_job.git_ref, self.git_ref)
-            self.assertEqual(naked_job.git_paths, self.git_paths)
-            self.assertEqual(
-                naked_job.information_type, self.information_type.value
-            )
-            self.assertEqual(
-                naked_job.import_since_commit_sha1, import_since_commit_sha1
-            )
+        job = getUtility(IImportVulnerabilityJobSource).get(self.handler)
+        naked_job = removeSecurityProxy(job)
+        self._check_job_attributes(
+            naked_job,
+            info,
+            repo,
+            expected_commit_sha1=import_since_commit_sha1,
+        )
 
     def test_importData_duplicated(self):
         """Test that we cannot create a duplicated ImportVulnerabilityJob
@@ -1165,7 +1172,7 @@ class TestVulnerabilitySetExportData(TestCaseWithFactory):
         self.factory.makeDistribution(name="soss", owner=self.requester)
 
         with person_logged_in(self.requester):
-            getUtility(IVulnerabilitySet).exportData(
+            info = getUtility(IVulnerabilitySet).exportData(
                 self.requester,
                 self.handler,
                 None,
@@ -1175,6 +1182,17 @@ class TestVulnerabilitySetExportData(TestCaseWithFactory):
         naked_job = removeSecurityProxy(job)
         self.assertIsInstance(naked_job, ExportVulnerabilityJob)
         self.assertEqual(naked_job.handler, self.handler)
+        self.assertEqual(
+            {
+                "job_details": {
+                    "created_at": naked_job.job.date_created,
+                    "started_at": naked_job.job.date_started,
+                    "finished_at": naked_job.job.date_finished,
+                },
+                **naked_job.metadata,
+            },
+            info,
+        )
 
     def test_exportData_multiple_handlers(self):
         """Test that the feature flag allows to create ExportVulnerabilityJob
@@ -1620,23 +1638,28 @@ class TestVulnerabilityGetJobs(WithScenarios, TestCaseWithFactory):
     """Tests for collecting export vulnerability job info."""
 
     layer = LaunchpadZopelessLayer
-    
+
     scenarios = [
-        ("soss", {
-            "handler": VulnerabilityHandlerEnum.SOSS,
-            "name": "soss",
-            "distribution_name": "soss",
-            "cve_key": "2025-1979",
-        }),
-        ("uct", {
-            "handler": VulnerabilityHandlerEnum.UCT,
-            "name": "uct",
-            "distribution_name": "ubuntu",
-            "cve_key": "2025-0001",
-        })
+        (
+            "soss",
+            {
+                "handler": VulnerabilityHandlerEnum.SOSS,
+                "name": "soss",
+                "distribution_name": "soss",
+                "cve_key": "2025-1979",
+            },
+        ),
+        (
+            "uct",
+            {
+                "handler": VulnerabilityHandlerEnum.UCT,
+                "name": "uct",
+                "distribution_name": "ubuntu",
+                "cve_key": "2025-0001",
+            },
+        ),
     ]
 
-
     def setUp(self):
         super().setUp()
         self.requester = self.factory.makePerson()
@@ -1646,10 +1669,12 @@ class TestVulnerabilityGetJobs(WithScenarios, TestCaseWithFactory):
             repository=self.repository,
             paths=("ref/heads/main", "ref/tags/v1.0"),
         )
-        
+
         if self.name == "uct":
-            self.distribution = getUtility(IDistributionSet).getByName(self.distribution_name)
-            
+            self.distribution = getUtility(IDistributionSet).getByName(
+                self.distribution_name
+            )
+
             # Create data from UCT CVE file
             distroseries = self.factory.makeDistroSeries(
                 name="xenial",
@@ -1666,13 +1691,13 @@ class TestVulnerabilityGetJobs(WithScenarios, TestCaseWithFactory):
             self.distribution = self.factory.makeDistribution(
                 name=self.distribution_name,
             )
-        
-        # Grant permissions to the user that will request the data    
+
+        # Grant permissions to the user that will request the data
         self.distribution.security_admin = self.requester
 
     def _create_import_job(self):
         """Helper to create a single ImportVulnerabilityJob."""
-        
+
         cve_name = "CVE-" + self.cve_key
         cve_path = (
             Path(__file__).parent
@@ -1684,7 +1709,7 @@ class TestVulnerabilityGetJobs(WithScenarios, TestCaseWithFactory):
             / "sampledata"
             / cve_name
         )
-        
+
         with open(cve_path, "rb") as file:
             self.useFixture(
                 GitHostingFixture(
@@ -1828,7 +1853,7 @@ class TestVulnerabilityGetJobs(WithScenarios, TestCaseWithFactory):
     def test_getImports_unauthenticated(self):
         """Test imports fail when requester lacks handler rights."""
         self.factory.makeCVE(self.cve_key)
-        
+
         # Remove permissions
         self.distribution.security_admin = self.factory.makePerson()
 
@@ -1864,7 +1889,6 @@ class TestVulnerabilityGetJobs(WithScenarios, TestCaseWithFactory):
 
         self.assertEqual(result["request"], naked_job.metadata["request"])
         self.assertEqual(result["result"], naked_job.metadata["result"])
-        self.assertEqual(result["data"], naked_job.metadata["data"])
 
         job_details = {
             "created_at": naked_job.job.date_created,
@@ -1900,7 +1924,6 @@ class TestVulnerabilityGetJobs(WithScenarios, TestCaseWithFactory):
 
             self.assertEqual(result["request"], naked_job.metadata["request"])
             self.assertEqual(result["result"], naked_job.metadata["result"])
-            self.assertEqual(result["data"], naked_job.metadata["data"])
 
             job_details = {
                 "created_at": naked_job.job.date_created,
@@ -1939,7 +1962,6 @@ class TestVulnerabilityGetJobs(WithScenarios, TestCaseWithFactory):
 
             self.assertEqual(result["request"], naked_job.metadata["request"])
             self.assertEqual(result["result"], naked_job.metadata["result"])
-            self.assertEqual(result["data"], naked_job.metadata["data"])
 
             job_details = {
                 "created_at": naked_job.job.date_created,
diff --git a/lib/lp/bugs/model/vulnerability.py b/lib/lp/bugs/model/vulnerability.py
index 81807c3..f19247b 100644
--- a/lib/lp/bugs/model/vulnerability.py
+++ b/lib/lp/bugs/model/vulnerability.py
@@ -481,7 +481,7 @@ class VulnerabilitySet:
 
         # Trigger the import job after validations pass
         job_source = getUtility(IImportVulnerabilityJobSource)
-        job_source.create(
+        job = job_source.create(
             handler,
             git_repository.id,
             git_ref,
@@ -490,7 +490,7 @@ class VulnerabilitySet:
             import_since_commit_sha1,
         )
 
-        return None
+        return job.info
 
     def exportData(
         self,
@@ -533,39 +533,17 @@ class VulnerabilitySet:
 
         # Trigger the export job after validations pass
         job_source = getUtility(IExportVulnerabilityJobSource)
-        job_source.create(handler)
+        job = job_source.create(handler)
 
-        return None
+        return job.info
 
     def _get_jobs_info(self, vulnerability_jobs):
         # Merge fields from Job and VulnerabilityJob.metadata
+        from lp.bugs.model.vulnerabilityjob import VulnerabilityJobDerived
+
         jobs_info = []
         for vulnerability_job in vulnerability_jobs:
-            job = vulnerability_job.job
-            if not job:
-                continue
-
-            job_details = {
-                "created_at": job.date_created,
-                "started_at": job.date_started,
-                "finished_at": job.date_finished,
-            }
-            job_info = dict(
-                vulnerability_job.metadata or {}
-            ).copy()  # copy so we don’t mutate the original
-
-            request = job_info.get("request")
-            if request and "information_type" in request:
-                info_type_number = request["information_type"]
-                try:
-                    request["information_type"] = InformationType.items[
-                        info_type_number
-                    ].title
-                except (KeyError, IndexError):
-                    request["information_type"] = ""
-
-            job_info["job_details"] = job_details
-            jobs_info.append(job_info)
+            jobs_info.append(VulnerabilityJobDerived(vulnerability_job).info)
 
         return jobs_info
 
diff --git a/lib/lp/bugs/model/vulnerabilityjob.py b/lib/lp/bugs/model/vulnerabilityjob.py
index 80a0753..0a88be4 100644
--- a/lib/lp/bugs/model/vulnerabilityjob.py
+++ b/lib/lp/bugs/model/vulnerabilityjob.py
@@ -100,3 +100,14 @@ class VulnerabilityJobDerived(BaseRunnableJob, metaclass=EnumeratedSubclass):
             ]
         )
         return vars
+
+    @property
+    def info(self) -> dict:
+        return {
+            "job_details": {
+                "created_at": self.job.date_created,
+                "started_at": self.job.date_started,
+                "finished_at": self.job.date_finished,
+            },
+            **self.metadata,
+        }
diff --git a/lib/lp/bugs/tests/test_exportvulnerabilityjob.py b/lib/lp/bugs/tests/test_exportvulnerabilityjob.py
index 8408a40..fb099a6 100644
--- a/lib/lp/bugs/tests/test_exportvulnerabilityjob.py
+++ b/lib/lp/bugs/tests/test_exportvulnerabilityjob.py
@@ -148,8 +148,6 @@ class ExportVulnerabilityJobTests(WithScenarios, TestCaseWithFactory):
                 "error_description": [],
                 "succeeded": [],
                 "failed": [],
-            },
-            "data": {
                 "export_link": "",
             },
         }
@@ -238,8 +236,6 @@ class ExportVulnerabilityJobTests(WithScenarios, TestCaseWithFactory):
                 "error_description": [],
                 "succeeded": [],
                 "failed": [],
-            },
-            "data": {
                 "export_link": "",
             },
         }
@@ -327,8 +323,6 @@ class ExportVulnerabilityJobTests(WithScenarios, TestCaseWithFactory):
                     "error_description": [],
                     "succeeded": cve_names,
                     "failed": [],
-                },
-                "data": {
                     "export_link": export_link,
                 },
             },
@@ -526,7 +520,7 @@ class ExportVulnerabilityTestViaCelery(WithScenarios, TestCaseWithFactory):
         self.assertEqual(metadata_result, naked_job_metadata["result"])
 
         self.assertThat(
-            naked_job_metadata["data"]["export_link"],
+            naked_job_metadata["result"]["export_link"],
             MatchesRegex(
                 r".*exported_vulnerabilities_[0-9]+\.zip$",
             ),

Follow ups