launchpad-reviewers team mailing list archive
-
launchpad-reviewers team
-
Mailing list archive
-
Message #27986
[Merge] ~ilasc/launchpad:add-get-status-artifacts-api into launchpad:master
Ioana Lasc has proposed merging ~ilasc/launchpad:add-get-status-artifacts-api into launchpad:master.
Commit message:
Add a get status artifacts endpoint
Requested reviews:
Launchpad code reviewers (launchpad-reviewers)
For more details, see:
https://code.launchpad.net/~ilasc/launchpad/+git/launchpad/+merge/414392
--
Your team Launchpad code reviewers is requested to review the proposed merge of ~ilasc/launchpad:add-get-status-artifacts-api into launchpad:master.
diff --git a/lib/lp/code/interfaces/revisionstatus.py b/lib/lp/code/interfaces/revisionstatus.py
index 20c6a5a..852571c 100644
--- a/lib/lp/code/interfaces/revisionstatus.py
+++ b/lib/lp/code/interfaces/revisionstatus.py
@@ -16,6 +16,7 @@ import http.client
from lazr.restful.declarations import (
error_status,
+ export_read_operation,
export_write_operation,
exported,
exported_as_webservice_entry,
@@ -78,6 +79,18 @@ class IRevisionStatusReportView(Interface):
date_finished = exported(Datetime(
title=_("When the report has finished.")), readonly=False)
+ @operation_parameters(
+ artifact_type=Choice(vocabulary=RevisionStatusArtifactType,
+ required=False))
+ @scoped(AccessTokenScope.REPOSITORY_BUILD_STATUS.title)
+ @export_read_operation()
+ @operation_for_version("devel")
+ def getArtifactsURLs(artifact_type):
+ """Retrieves the list of URLs for artifacts that exist for this report.
+
+ :param artifact_type: The type of artifact for the report.
+ """
+
class IRevisionStatusReportEditableAttributes(Interface):
"""`IRevisionStatusReport` attributes that can be edited.
@@ -235,6 +248,9 @@ class IRevisionStatusArtifactSet(Interface):
def findByReport(report):
"""Returns the set of artifacts for a given report."""
+ def getArtifactDownloadUrls(report, clauses):
+ """Returns download URLs for all artifacts under a given report."""
+
class IRevisionStatusArtifact(Interface):
id = Int(title=_("ID"), required=True, readonly=True)
@@ -249,3 +265,9 @@ class IRevisionStatusArtifact(Interface):
artifact_type = Choice(
title=_('The type of artifact, only log for now.'),
vocabulary=RevisionStatusArtifactType)
+
+ def downloadUrl():
+ """URL for downloading the artifact.
+
+ :return: A URL for downloading this artifact.
+ """
diff --git a/lib/lp/code/model/revisionstatus.py b/lib/lp/code/model/revisionstatus.py
index de9bfff..a4c3061 100644
--- a/lib/lp/code/model/revisionstatus.py
+++ b/lib/lp/code/model/revisionstatus.py
@@ -33,6 +33,7 @@ from lp.services.database.enumcol import DBEnum
from lp.services.database.interfaces import IStore
from lp.services.database.sqlbase import convert_storm_clause_to_string
from lp.services.database.stormbase import StormBase
+from lp.services.librarian.browser import ProxiedLibraryFileAlias
from lp.services.librarian.interfaces import ILibraryFileAliasSet
@@ -117,6 +118,20 @@ class RevisionStatusReport(StormBase):
if result is not None:
self.transitionToNewResult(result)
+ def getArtifactsURLs(self, artifact_type):
+ clauses = [
+ RevisionStatusArtifact.report == self,
+ ]
+ if artifact_type == RevisionStatusArtifactType.LOG:
+ clauses.extend([RevisionStatusArtifact.artifact_type ==
+ RevisionStatusArtifactType.LOG, ])
+ elif artifact_type == RevisionStatusArtifactType.BINARY:
+ clauses.extend([RevisionStatusArtifact.artifact_type ==
+ RevisionStatusArtifactType.BINARY, ])
+
+ return getUtility(
+ IRevisionStatusArtifactSet).getArtifactDownloadUrls(clauses)
+
@implementer(IRevisionStatusReportSet)
class RevisionStatusReportSet:
@@ -184,6 +199,14 @@ class RevisionStatusArtifact(StormBase):
self.report = report
self.artifact_type = artifact_type
+ def downloadUrl(self):
+ if self.library_file.restricted:
+ url = ProxiedLibraryFileAlias(
+ self.library_file, self.report).http_url
+ else:
+ url = self.library_file.http_url
+ return url
+
@implementer(IRevisionStatusArtifactSet)
class RevisionStatusArtifactSet:
@@ -204,3 +227,11 @@ class RevisionStatusArtifactSet:
return IStore(RevisionStatusArtifact).find(
RevisionStatusArtifact,
RevisionStatusArtifact.report == report)
+
+ def getArtifactDownloadUrls(self, clauses):
+ artifacts = IStore(RevisionStatusArtifact).find(
+ RevisionStatusArtifact, *clauses)
+ urls = []
+ for artifact in list(artifacts):
+ urls.append(artifact.downloadUrl())
+ return urls
diff --git a/lib/lp/code/model/tests/test_revisionstatus.py b/lib/lp/code/model/tests/test_revisionstatus.py
index 6ce84c5..05367de 100644
--- a/lib/lp/code/model/tests/test_revisionstatus.py
+++ b/lib/lp/code/model/tests/test_revisionstatus.py
@@ -6,6 +6,7 @@
import hashlib
import io
+import requests
from testtools.matchers import (
AnyMatch,
Equals,
@@ -40,10 +41,11 @@ from lp.testing.pages import webservice_for_person
class TestRevisionStatusReport(TestCaseWithFactory):
layer = DatabaseFunctionalLayer
- def makeRevisionStatusArtifact(self, report):
+ def makeRevisionStatusArtifact(self, report, artifact_type=None):
# We don't need to upload files to the librarian in this test suite.
lfa = self.factory.makeLibraryFileAlias(db_only=True)
- return self.factory.makeRevisionStatusArtifact(lfa=lfa, report=report)
+ return self.factory.makeRevisionStatusArtifact(
+ lfa=lfa, report=report, artifact_type=artifact_type)
def test_owner_public(self):
# The owner of a public repository can view and edit its reports and
@@ -239,3 +241,64 @@ class TestRevisionStatusReportWebservice(TestCaseWithFactory):
result_summary=Equals(initial_result_summary),
result=Equals(RevisionStatusResult.SUCCEEDED),
date_finished=GreaterThan(date_finished_before_update)))
+
+ def test_getArtifactsURLs(self):
+ report = self.factory.makeRevisionStatusReport()
+ artifact_log = self.factory.makeRevisionStatusArtifact(
+ report=report, artifact_type=RevisionStatusArtifactType.LOG,
+ content=b'log_data')
+ log_url = artifact_log.library_file.http_url
+ artifact_binary = self.factory.makeRevisionStatusArtifact(
+ report=report, artifact_type=RevisionStatusArtifactType.BINARY,
+ content=b'binary_data')
+ binary_url = artifact_binary.library_file.http_url
+ requester = report.creator
+ repository = report.git_repository
+ report_url = api_url(report)
+ webservice = self.getWebservice(requester, repository)
+ response = webservice.named_get(
+ report_url, "getArtifactsURLs", artifact_type="Log")
+ self.assertEqual(200, response.status)
+ with person_logged_in(requester):
+ self.assertIn(log_url, response.jsonBody())
+ self.assertNotIn(binary_url, response.jsonBody())
+ file = requests.get(response.jsonBody()[0])
+ self.assertEqual(b'log_data', file.content)
+ response = webservice.named_get(
+ report_url, "getArtifactsURLs", artifact_type="Binary")
+ self.assertEqual(200, response.status)
+ with person_logged_in(requester):
+ self.assertNotIn(log_url, response.jsonBody())
+ self.assertIn(binary_url, response.jsonBody())
+ file = requests.get(response.jsonBody()[0])
+ self.assertEqual(b'binary_data', file.content)
+ response = webservice.named_get(
+ report_url, "getArtifactsURLs")
+ self.assertEqual(200, response.status)
+ with person_logged_in(requester):
+ self.assertIn(log_url, response.jsonBody())
+ self.assertIn(binary_url, response.jsonBody())
+
+ def test_getArtifactsURLs_restricted(self):
+ requester = self.factory.makePerson()
+ with person_logged_in(requester):
+ kwargs = {"owner": requester}
+ kwargs["information_type"] = InformationType.USERDATA
+ repository = self.factory.makeGitRepository(**kwargs)
+ report = self.factory.makeRevisionStatusReport(
+ git_repository=repository)
+ report_url = api_url(report)
+ artifact = self.factory.makeRevisionStatusArtifact(
+ report=report, artifact_type=RevisionStatusArtifactType.LOG,
+ content=b'log_data', restricted=True)
+ log_url = 'http://code.launchpad.test/%s/+status/%s/+files/%s' % (
+ repository.unique_name, report.id,
+ artifact.library_file.filename)
+
+ webservice = self.getWebservice(requester, repository)
+ response = webservice.named_get(
+ report_url, "getArtifactsURLs", artifact_type="Log")
+ self.assertEqual(200, response.status)
+
+ with person_logged_in(requester):
+ self.assertIn(log_url, response.jsonBody())
diff --git a/lib/lp/testing/factory.py b/lib/lp/testing/factory.py
index 1588aad..d2a9a4e 100644
--- a/lib/lp/testing/factory.py
+++ b/lib/lp/testing/factory.py
@@ -1871,13 +1871,16 @@ class BareLaunchpadObjectFactory(ObjectFactory):
result_summary, result)
def makeRevisionStatusArtifact(
- self, lfa=None, report=None,
- artifact_type=RevisionStatusArtifactType.LOG):
+ self, lfa=None, content=None, report=None,
+ artifact_type=None, restricted=False):
"""Create a new RevisionStatusArtifact."""
if lfa is None:
- lfa = self.makeLibraryFileAlias()
+ lfa = self.makeLibraryFileAlias(
+ content=content, restricted=restricted)
if report is None:
report = self.makeRevisionStatusReport()
+ if artifact_type is None:
+ artifact_type = RevisionStatusArtifactType.LOG
return getUtility(IRevisionStatusArtifactSet).new(
lfa, report, artifact_type)
Follow ups