Merge ~jugmac00/launchpad:revisionsstatusreport-attach-and-setlog-now-accept-file-object into launchpad:master

Proposed by Jürgen Gmach
Status: Merged
Approved by: Jürgen Gmach
Approved revision: d83f4fd5ba13e74e811535c6e78acc89346ef42d
Merge reported by: Otto Co-Pilot
Merged at revision: not available
Proposed branch: ~jugmac00/launchpad:revisionsstatusreport-attach-and-setlog-now-accept-file-object
Merge into: launchpad:master
Diff against target: 179 lines (+87/-6)
3 files modified
lib/lp/code/interfaces/revisionstatus.py (+4/-2)
lib/lp/code/model/revisionstatus.py (+23/-3)
lib/lp/code/model/tests/test_revisionstatus.py (+60/-1)
Reviewer Review Type Date Requested Status
Colin Watson (community) Approve
Review via email: mp+415871@code.launchpad.net

Commit message

RevisionStatusReport.attach/.setLog now also accept a file object

To post a comment you must log in.
Revision history for this message
Colin Watson (cjwatson) wrote :

I'd consider changing `CIUpload` to use this in the same MP. Probably not a major issue in this case, but when doing refactorings like this in the past I've sometimes found that changing the calling code along with the underlying refactoring causes me to notice problems I'd otherwise have missed.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/lib/lp/code/interfaces/revisionstatus.py b/lib/lp/code/interfaces/revisionstatus.py
index 90d4e02..8dcb2e4 100644
--- a/lib/lp/code/interfaces/revisionstatus.py
+++ b/lib/lp/code/interfaces/revisionstatus.py
@@ -149,7 +149,8 @@ class IRevisionStatusReportEdit(Interface):
149 def setLog(log_data):149 def setLog(log_data):
150 """Set a new log on an existing status report.150 """Set a new log on an existing status report.
151151
152 :param log_data: The contents (in bytes) of the log.152 :param log_data: The contents of the log, either as bytes or as a file
153 object.
153 """154 """
154155
155 # XXX cjwatson 2022-01-14: artifact_type isn't currently exported, but156 # XXX cjwatson 2022-01-14: artifact_type isn't currently exported, but
@@ -166,7 +167,8 @@ class IRevisionStatusReportEdit(Interface):
166 def attach(name, data, artifact_type=RevisionStatusArtifactType.BINARY):167 def attach(name, data, artifact_type=RevisionStatusArtifactType.BINARY):
167 """Attach a new artifact to an existing status report.168 """Attach a new artifact to an existing status report.
168169
169 :param data: The contents (in bytes) of the artifact.170 :param data: The contents of the artifact, either as bytes or as a file
171 object.
170 :param artifact_type: The type of the artifact. This may currently172 :param artifact_type: The type of the artifact. This may currently
171 only be `RevisionStatusArtifactType.BINARY`, but more types may173 only be `RevisionStatusArtifactType.BINARY`, but more types may
172 be added in future.174 be added in future.
diff --git a/lib/lp/code/model/revisionstatus.py b/lib/lp/code/model/revisionstatus.py
index 8ef5cba..1f74af3 100644
--- a/lib/lp/code/model/revisionstatus.py
+++ b/lib/lp/code/model/revisionstatus.py
@@ -8,6 +8,7 @@ __all__ = [
8 ]8 ]
99
10import io10import io
11import os
1112
12import pytz13import pytz
13from storm.locals import (14from storm.locals import (
@@ -90,9 +91,18 @@ class RevisionStatusReport(StormBase):
90 def setLog(self, log_data):91 def setLog(self, log_data):
91 filename = '%s-%s.txt' % (self.title, self.commit_sha1)92 filename = '%s-%s.txt' % (self.title, self.commit_sha1)
9293
94 if isinstance(log_data, bytes):
95 file = io.BytesIO(log_data)
96 size = len(log_data)
97 else:
98 file = log_data
99 file.seek(0, os.SEEK_END)
100 size = file.tell()
101 file.seek(0)
102
93 lfa = getUtility(ILibraryFileAliasSet).create(103 lfa = getUtility(ILibraryFileAliasSet).create(
94 name=filename, size=len(log_data),104 name=filename, size=size,
95 file=io.BytesIO(log_data), contentType='text/plain',105 file=file, contentType='text/plain',
96 restricted=self.git_repository.private)106 restricted=self.git_repository.private)
97107
98 getUtility(IRevisionStatusArtifactSet).new(108 getUtility(IRevisionStatusArtifactSet).new(
@@ -101,8 +111,18 @@ class RevisionStatusReport(StormBase):
101 def attach(self, name, data,111 def attach(self, name, data,
102 artifact_type=RevisionStatusArtifactType.BINARY):112 artifact_type=RevisionStatusArtifactType.BINARY):
103 """See `IRevisionStatusReport`."""113 """See `IRevisionStatusReport`."""
114
115 if isinstance(data, bytes):
116 file = io.BytesIO(data)
117 size = len(data)
118 else:
119 file = data
120 file.seek(0, os.SEEK_END)
121 size = file.tell()
122 file.seek(0)
123
104 lfa = getUtility(ILibraryFileAliasSet).create(124 lfa = getUtility(ILibraryFileAliasSet).create(
105 name=name, size=len(data), file=io.BytesIO(data),125 name=name, size=size, file=file,
106 contentType='application/octet-stream',126 contentType='application/octet-stream',
107 restricted=self.git_repository.private)127 restricted=self.git_repository.private)
108 getUtility(IRevisionStatusArtifactSet).new(lfa, self, artifact_type)128 getUtility(IRevisionStatusArtifactSet).new(lfa, self, artifact_type)
diff --git a/lib/lp/code/model/tests/test_revisionstatus.py b/lib/lp/code/model/tests/test_revisionstatus.py
index 7b5ed39..33f9c3c 100644
--- a/lib/lp/code/model/tests/test_revisionstatus.py
+++ b/lib/lp/code/model/tests/test_revisionstatus.py
@@ -4,9 +4,14 @@
4"""Tests for revision status reports and artifacts."""4"""Tests for revision status reports and artifacts."""
55
6import hashlib6import hashlib
7from hashlib import sha1
7import io8import io
9import os
810
9from fixtures import FakeLogger11from fixtures import (
12 FakeLogger,
13 TempDir,
14 )
10import requests15import requests
11from storm.store import Store16from storm.store import Store
12from testtools.matchers import (17from testtools.matchers import (
@@ -30,6 +35,7 @@ from lp.code.interfaces.revisionstatus import (
30 IRevisionStatusReportSet,35 IRevisionStatusReportSet,
31 )36 )
32from lp.services.auth.enums import AccessTokenScope37from lp.services.auth.enums import AccessTokenScope
38from lp.services.osutils import write_file
33from lp.services.webapp.authorization import check_permission39from lp.services.webapp.authorization import check_permission
34from lp.testing import (40from lp.testing import (
35 anonymous_logged_in,41 anonymous_logged_in,
@@ -37,6 +43,7 @@ from lp.testing import (
37 person_logged_in,43 person_logged_in,
38 TestCaseWithFactory,44 TestCaseWithFactory,
39 )45 )
46from lp.testing.dbuser import switch_dbuser
40from lp.testing.layers import (47from lp.testing.layers import (
41 DatabaseFunctionalLayer,48 DatabaseFunctionalLayer,
42 LaunchpadFunctionalLayer,49 LaunchpadFunctionalLayer,
@@ -195,6 +202,32 @@ class TestRevisionStatusReportWebservice(TestCaseWithFactory):
195 def test_setLog_private(self):202 def test_setLog_private(self):
196 self._test_setLog(private=True)203 self._test_setLog(private=True)
197204
205 def test_setLog_with_file_object(self):
206 switch_dbuser("launchpad_main")
207
208 # create log file
209 path = os.path.join(
210 self.useFixture(TempDir()).path, "test", "build:0.log"
211 )
212 content = "some log content"
213 write_file(path, content.encode("utf-8"))
214
215 report = self.factory.makeRevisionStatusReport(
216 title="build:0",
217 ci_build=self.factory.makeCIBuild(),
218 )
219
220 with person_logged_in(report.creator):
221 with open(path, "rb") as f:
222 report.setLog(f)
223
224 artifacts = list(getUtility(
225 IRevisionStatusArtifactSet).findByReport(report))
226 self.assertEqual(
227 artifacts[0].library_file.content.sha1,
228 sha1(content.encode()).hexdigest()
229 )
230
198 def _test_attach(self, private):231 def _test_attach(self, private):
199 requester = self.factory.makePerson()232 requester = self.factory.makePerson()
200 with person_logged_in(requester):233 with person_logged_in(requester):
@@ -234,6 +267,32 @@ class TestRevisionStatusReportWebservice(TestCaseWithFactory):
234 def test_attach_private(self):267 def test_attach_private(self):
235 self._test_attach(private=True)268 self._test_attach(private=True)
236269
270 def test_attach_with_file_object(self):
271 switch_dbuser("launchpad_main")
272
273 # create text file
274 path = os.path.join(
275 self.useFixture(TempDir()).path, "test.md"
276 )
277 content = "some content"
278 write_file(path, content.encode("utf-8"))
279
280 report = self.factory.makeRevisionStatusReport(
281 title="build:0",
282 ci_build=self.factory.makeCIBuild(),
283 )
284
285 with person_logged_in(report.creator):
286 with open(path, "rb") as f:
287 report.attach("text", f)
288
289 artifacts = list(getUtility(
290 IRevisionStatusArtifactSet).findByReport(report))
291 self.assertEqual(
292 artifacts[0].library_file.content.sha1,
293 sha1(content.encode()).hexdigest()
294 )
295
237 def test_update(self):296 def test_update(self):
238 report = self.factory.makeRevisionStatusReport(297 report = self.factory.makeRevisionStatusReport(
239 result=RevisionStatusResult.FAILED)298 result=RevisionStatusResult.FAILED)

Subscribers

People subscribed via source and target branches

to status/vote changes: