Merge lp:~bryce/launchpad/lp-548824 into lp:launchpad

Proposed by Bryce Harrington
Status: Merged
Approved by: Graham Binns
Approved revision: no longer in the source branch.
Merged at revision: 10961
Proposed branch: lp:~bryce/launchpad/lp-548824
Merge into: lp:launchpad
Diff against target: 168 lines (+84/-5)
4 files modified
lib/canonical/launchpad/database/temporaryblobstorage.py (+19/-3)
lib/canonical/launchpad/interfaces/temporaryblobstorage.py (+4/-0)
lib/canonical/launchpad/pagetests/webservice/xx-temporary-blob-storage.txt (+36/-2)
lib/lp/bugs/tests/test_apportjob.py (+25/-0)
To merge this branch: bzr merge lp:~bryce/launchpad/lp-548824
Reviewer Review Type Date Requested Status
Graham Binns (community) code Approve
Canonical Launchpad Engineering Pending
Review via email: mp+26202@code.launchpad.net

Commit message

[r=gmb][ui=none][bug=548824] Add a getProcessedData() API call to retrieve the metadata about the uploaded blob.

Description of the change

Adds a getProcessedData() API call to retrieve the metadata about the uploaded blob.

This should enable apport to do further testing on blobs it's uploaded.

To post a comment you must log in.
Revision history for this message
Bryce Harrington (bryce) wrote :

This passes ec2 test without issue, however I'm unsure about if I've done the tests correctly. Grahm, I'd appreciate it if you could give this a really deep review, and suggest how to do the tests better. (For instance, should it examine the data items inside the metadata itself?)

Revision history for this message
Graham Binns (gmb) wrote :

This looks fine, but you should add a test to lib/canonical/launchpad/pagetests/webservice/xx-temporary-blob-storage.txt to cover calling the method from the API. Other than that this is fine.

review: Needs Fixing (code)
Revision history for this message
Bryce Harrington (bryce) wrote :

I'm finally quite happy with this.

It turned out that calling .start() and .complete() on the job wasn't sufficient... it needed job.run() in order to actually parse out the metadata. Now it works swimmingly!

In rummaging around in the apport job internals I found there was some additional interesting metadata about the job itself, which I found interesting for debugging, but may or may not be of any interest for testing purposes. I opted to add a getJobStatus() routine to expose this info for apport's tests to use. Please review. If it looks like either something apport won't care about, or that it is something we don't want to expose, it's easily excised from this change.

Revision history for this message
Graham Binns (gmb) wrote :

> I'm finally quite happy with this.
>
> It turned out that calling .start() and .complete() on the job wasn't
> sufficient... it needed job.run() in order to actually parse out the metadata.
> Now it works swimmingly!
>

Hah. Shows what my memory's like, doesn't it?

> In rummaging around in the apport job internals I found there was some
> additional interesting metadata about the job itself, which I found
> interesting for debugging, but may or may not be of any interest for testing
> purposes. I opted to add a getJobStatus() routine to expose this info for
> apport's tests to use. Please review. If it looks like either something
> apport won't care about, or that it is something we don't want to expose, it's
> easily excised from this change.

I don't think we should expose this; it should be enough for people to be able to query to see whether the job has completed yet. Also, if we were going to expose this metadata we'd probably want to do it at the IJob level, since there are several systems in Launchpad (e.g. diff generation for merge proposals) which use it. Nice idea, though :).

I'm happy with the rest of the branch. r=me with the getJobStatus() code removed. Good work!

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/database/temporaryblobstorage.py'
2--- lib/canonical/launchpad/database/temporaryblobstorage.py 2010-02-26 17:35:01 +0000
3+++ lib/canonical/launchpad/database/temporaryblobstorage.py 2010-05-28 15:37:41 +0000
4@@ -58,18 +58,34 @@
5 finally:
6 self.file_alias.close()
7
8- def hasBeenProcessed(self):
9- """See `ITemporaryBlobStorage`."""
10+ @property
11+ def _apport_job(self):
12 # Imported here to avoid circular imports
13 from lp.bugs.interfaces.apportjob import IProcessApportBlobJobSource
14 try:
15 job_for_blob = getUtility(
16 IProcessApportBlobJobSource).getByBlobUUID(self.uuid)
17 except SQLObjectNotFound:
18+ return None
19+
20+ return job_for_blob
21+
22+ def hasBeenProcessed(self):
23+ """See `ITemporaryBlobStorage`."""
24+ job_for_blob = self._apport_job
25+ if not job_for_blob:
26 return False
27-
28 return (job_for_blob.job.status == JobStatus.COMPLETED)
29
30+ def getProcessedData(self):
31+ """See `ITemporaryBlobStorage`."""
32+ job_for_blob = self._apport_job
33+ if not job_for_blob:
34+ return None
35+ if 'processed_data' not in job_for_blob.metadata:
36+ return {}
37+
38+ return job_for_blob.metadata['processed_data']
39
40 class TemporaryStorageManager:
41 """A tool to create temporary BLOB's in Launchpad."""
42
43=== modified file 'lib/canonical/launchpad/interfaces/temporaryblobstorage.py'
44--- lib/canonical/launchpad/interfaces/temporaryblobstorage.py 2010-02-26 17:35:01 +0000
45+++ lib/canonical/launchpad/interfaces/temporaryblobstorage.py 2010-05-28 15:37:41 +0000
46@@ -49,6 +49,10 @@
47 def hasBeenProcessed():
48 """Return True if this blob has been processed."""
49
50+ @export_read_operation()
51+ def getProcessedData():
52+ """Returns a dict containing the processed blob data."""
53+
54
55 class ITemporaryStorageManager(Interface):
56 """A tool to create temporary blobs."""
57
58=== modified file 'lib/canonical/launchpad/pagetests/webservice/xx-temporary-blob-storage.txt'
59--- lib/canonical/launchpad/pagetests/webservice/xx-temporary-blob-storage.txt 2010-02-26 17:35:01 +0000
60+++ lib/canonical/launchpad/pagetests/webservice/xx-temporary-blob-storage.txt 2010-05-28 15:37:41 +0000
61@@ -13,11 +13,23 @@
62 set.
63
64 >>> login('foo.bar@canonical.com')
65+ >>> import os
66 >>> from zope.component import getUtility
67+ >>> from canonical.config import config
68 >>> from canonical.launchpad.interfaces.temporaryblobstorage import (
69 ... ITemporaryStorageManager)
70
71- >>> blob_token = getUtility(ITemporaryStorageManager).new("some data")
72+ >>> testfiles = os.path.join(config.root,
73+ ... 'lib/lp/bugs/tests/testfiles')
74+ >>> blob_file = open(
75+ ... os.path.join(testfiles, 'extra_filebug_data.msg'))
76+ >>> blob_data = blob_file.read()
77+ >>> print blob_data[307:373]
78+ --boundary
79+ Content-disposition: attachment; filename='attachment1'
80+
81+ >>> blob_token = getUtility(ITemporaryStorageManager).new(blob_data)
82+
83 >>> logout()
84
85 >>> temporary_blobs = webservice.get('/temporary-blobs').jsonBody()
86@@ -48,7 +60,6 @@
87 >>> blob['token'] == blob_token
88 True
89
90-
91 Checking whether a blob has been processed
92 ------------------------------------------
93
94@@ -65,6 +76,13 @@
95 ... blob['self_link'], 'hasBeenProcessed').jsonBody()
96 False
97
98+However, since the blob has not been processed there will be no
99+job processed data at this point.
100+
101+ >>> print webservice.named_get(
102+ ... blob['self_link'], 'getProcessedData').jsonBody()
103+ None
104+
105 Once the blob has been processed, its hasBeenProcessed() method will
106 return True.
107
108@@ -74,8 +92,24 @@
109 ... getUtility(ITemporaryStorageManager).fetch(blob_token))
110 >>> job.job.start()
111 >>> job.job.complete()
112+ >>> job.run()
113 >>> logout()
114
115 >>> print webservice.named_get(
116 ... blob['self_link'], 'hasBeenProcessed').jsonBody()
117 True
118+
119+And now the blob's parsed-out metadata is now accessible.
120+
121+ >>> metadata = webservice.named_get(
122+ ... blob['self_link'], 'getProcessedData').jsonBody()
123+
124+ >>> print metadata['extra_description']
125+ This should be added to the description.
126+
127+ >>> print len(metadata['comments'])
128+ 2
129+
130+ >>> attachment = metadata['attachments'][0]
131+ >>> print attachment['description']
132+ attachment1
133
134=== modified file 'lib/lp/bugs/tests/test_apportjob.py'
135--- lib/lp/bugs/tests/test_apportjob.py 2010-04-13 13:13:45 +0000
136+++ lib/lp/bugs/tests/test_apportjob.py 2010-05-28 15:37:41 +0000
137@@ -338,6 +338,31 @@
138 view.publishTraverse(view.request, blob_uuid)
139 return view
140
141+ def test_blob_has_been_processed(self):
142+ # Using the TemporaryBlobStorageAddView to upload a new BLOB
143+ # will show blob as being processed
144+ blob_uuid = self._create_blob_and_job_using_storeblob()
145+ blob = getUtility(ITemporaryStorageManager).fetch(blob_uuid)
146+
147+ self.assertFalse(
148+ blob.hasBeenProcessed(),
149+ "BLOB should not be processed, but indicates it has.")
150+
151+ def test_blob_get_processed_data(self):
152+ # Using the TemporaryBlobStorageAddView to upload a new BLOB
153+ # should indicate there two attachments were processed.
154+ blob_uuid = self._create_blob_and_job_using_storeblob()
155+ blob = getUtility(ITemporaryStorageManager).fetch(blob_uuid)
156+ job = getUtility(IProcessApportBlobJobSource).getByBlobUUID(blob_uuid)
157+ job.job.start()
158+ job.job.complete()
159+ job.run()
160+ blob_meta = blob.getProcessedData()
161+
162+ self.assertEqual(
163+ len(blob_meta['attachments']), 2,
164+ "BLOB metadata: %s" %(str(blob_meta)))
165+
166 def test_adding_blob_adds_job(self):
167 # Using the TemporaryBlobStorageAddView to upload a new BLOB
168 # will add a new ProcessApportBlobJob for that BLOB.