14575.2.1
by Jeroen Vermeulen
Lint. |
1 |
Updating Product.remote_product |
2 |
=============================== |
|
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
3 |
|
4 |
The remote_product attribute of a Product is used to present links for |
|
5 |
filing and searching bugs in the Product's bug tracker, in case it's not |
|
6 |
using Launchpad to track its bugs. We don't expect users to set the |
|
7 |
remote_product themselves, so we have a script that tries to set this |
|
8 |
automatically. |
|
9 |
||
14600.2.2
by Curtis Hovey
Moved webapp to lp.services. |
10 |
>>> from lp.services.webapp.interfaces import ( |
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
11 |
... IStoreSelector, DEFAULT_FLAVOR, MAIN_STORE) |
12 |
>>> store = getUtility(IStoreSelector).get(MAIN_STORE, DEFAULT_FLAVOR) |
|
13 |
>>> store.execute("UPDATE Product SET remote_product = 'not-None'") |
|
14 |
<storm...> |
|
15 |
||
8523.3.1
by Gavin Panella
Bugs tree reorg after automated migration. |
16 |
>>> from lp.bugs.interfaces.bugtracker import BugTrackerType |
12070.1.4
by Tim Penhey
Move FakeLogger and BufferLogger to lp.services.log.logging and delete the QuietFakeLogger. |
17 |
>>> from lp.services.log.logger import FakeLogger, BufferLogger |
14538.2.45
by Curtis Hovey
Moved scripts and tests to lp.bugs. |
18 |
>>> from lp.bugs.scripts.updateremoteproduct import ( |
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
19 |
... RemoteProductUpdater) |
10315.2.4
by Jeroen Vermeulen
Replace all "Fake" and "Mock" transaction managers I can find with one implementation. |
20 |
>>> from lp.testing.faketransaction import FakeTransaction |
12070.1.4
by Tim Penhey
Move FakeLogger and BufferLogger to lp.services.log.logging and delete the QuietFakeLogger. |
21 |
>>> updater = RemoteProductUpdater(FakeTransaction(), BufferLogger()) |
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
22 |
|
7793.5.4
by Bjorn Tillenius
First cut at implementing updateRemoteProduct(). |
23 |
|
14575.2.1
by Jeroen Vermeulen
Lint. |
24 |
Testing |
25 |
------- |
|
7793.5.4
by Bjorn Tillenius
First cut at implementing updateRemoteProduct(). |
26 |
|
27 |
To help testing, there is a method, _getExternalBugTracker(), that |
|
28 |
creates the ExternalBugTracker for the given BugTracker. |
|
29 |
||
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
30 |
>>> rt = factory.makeBugTracker( |
31 |
... bugtrackertype=BugTrackerType.RT, |
|
32 |
... base_url=u'http://rt.example.com/') |
|
33 |
>>> rt_external = updater._getExternalBugTracker(rt) |
|
34 |
>>> rt_external.__class__.__name__ |
|
35 |
'RequestTracker' |
|
36 |
>>> rt_external.baseurl |
|
37 |
u'http://rt.example.com' |
|
38 |
||
7793.5.4
by Bjorn Tillenius
First cut at implementing updateRemoteProduct(). |
39 |
For testing, _getExternalBugTracker() can be overridden to return an |
40 |
ExternalBugTracker that doesn't require network access. |
|
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
41 |
|
42 |
>>> class FakeExternalBugTracker: |
|
7840.1.1
by Graham Binns
Fixed bug 333354. |
43 |
... |
44 |
... def initializeRemoteBugDB(self, bug_ids): |
|
45 |
... print "Initializing DB for bugs: %s." % bug_ids |
|
46 |
... |
|
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
47 |
... def getRemoteProduct(self, remote_bug): |
48 |
... return 'product-for-bug-%s' % remote_bug |
|
49 |
||
50 |
||
7793.5.8
by Bjorn Tillenius
Rename TestRemoteProductUpdater to show intent. |
51 |
>>> class NoNetworkRemoteProductUpdater(RemoteProductUpdater): |
7844.2.1
by Graham Binns
update-remote-product should now handle errors. |
52 |
... |
53 |
... external_bugtracker_to_return = FakeExternalBugTracker |
|
54 |
... |
|
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
55 |
... def _getExternalBugTracker(self, bug_tracker): |
7844.2.1
by Graham Binns
update-remote-product should now handle errors. |
56 |
... return self.external_bugtracker_to_return() |
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
57 |
|
7793.5.4
by Bjorn Tillenius
First cut at implementing updateRemoteProduct(). |
58 |
|
14575.2.1
by Jeroen Vermeulen
Lint. |
59 |
update() |
60 |
-------- |
|
7793.5.10
by Bjorn Tillenius
Add method for updating all products needing updating. |
61 |
|
62 |
The update method simply loops over all the bug tracker types that can |
|
7793.5.16
by Bjorn Tillenius
Don't try to update EMAILADDRESS bug trackers. |
63 |
track more than one product, and calls updateByBugTrackerType(). Any bug |
64 |
tracker type that isn't specified as being for a single product is being |
|
7793.5.17
by Bjorn Tillenius
Typo. |
65 |
looped over. The EMAILADDRESS one is special, though. It could be used |
7793.5.16
by Bjorn Tillenius
Don't try to update EMAILADDRESS bug trackers. |
66 |
for more than one product, but we have no way of interacting with it, so |
67 |
it's skipped as well. |
|
7793.5.10
by Bjorn Tillenius
Add method for updating all products needing updating. |
68 |
|
69 |
>>> class TrackerTypeCollectingUpdater(RemoteProductUpdater): |
|
70 |
... def __init__(self): |
|
12070.1.4
by Tim Penhey
Move FakeLogger and BufferLogger to lp.services.log.logging and delete the QuietFakeLogger. |
71 |
... self.logger = BufferLogger() |
7793.5.10
by Bjorn Tillenius
Add method for updating all products needing updating. |
72 |
... self.looped_over_bug_tracker_types = set() |
73 |
... def updateByBugTrackerType(self, bugtracker_type): |
|
74 |
... self.looped_over_bug_tracker_types.add(bugtracker_type) |
|
75 |
||
8523.3.1
by Gavin Panella
Bugs tree reorg after automated migration. |
76 |
>>> from lp.bugs.interfaces.bugtracker import ( |
7793.5.10
by Bjorn Tillenius
Add method for updating all products needing updating. |
77 |
... SINGLE_PRODUCT_BUGTRACKERTYPES) |
78 |
>>> multi_product_trackers = set( |
|
79 |
... bugtracker_type for bugtracker_type in BugTrackerType.items |
|
80 |
... if bugtracker_type not in SINGLE_PRODUCT_BUGTRACKERTYPES) |
|
7793.5.16
by Bjorn Tillenius
Don't try to update EMAILADDRESS bug trackers. |
81 |
>>> multi_product_trackers.remove(BugTrackerType.EMAILADDRESS) |
7793.5.10
by Bjorn Tillenius
Add method for updating all products needing updating. |
82 |
|
83 |
>>> updater = TrackerTypeCollectingUpdater() |
|
84 |
>>> updater.update() |
|
7793.5.16
by Bjorn Tillenius
Don't try to update EMAILADDRESS bug trackers. |
85 |
>>> multi_product_trackers.symmetric_difference( |
7793.5.10
by Bjorn Tillenius
Add method for updating all products needing updating. |
86 |
... updater.looped_over_bug_tracker_types) |
87 |
set([]) |
|
88 |
||
89 |
||
14575.2.1
by Jeroen Vermeulen
Lint. |
90 |
updateByBugTrackerType() |
91 |
------------------------ |
|
7793.5.4
by Bjorn Tillenius
First cut at implementing updateRemoteProduct(). |
92 |
|
7793.5.9
by Bjorn Tillenius
Rename updateRemoteProduct() to something more sensible. |
93 |
The updateByBugTrackerType() method looks at the bug watches that are |
7793.5.4
by Bjorn Tillenius
First cut at implementing updateRemoteProduct(). |
94 |
linked to the product, to decide what remote_product should be set to. |
95 |
It accepts a single parameter, the type of the bug tracker that should |
|
96 |
be updated. |
|
97 |
||
98 |
||
14575.2.1
by Jeroen Vermeulen
Lint. |
99 |
No bug watches |
100 |
.............. |
|
7793.5.4
by Bjorn Tillenius
First cut at implementing updateRemoteProduct(). |
101 |
|
102 |
If there are no bug watches, nothing will be done. |
|
103 |
||
104 |
>>> bugzilla_product = factory.makeProduct( |
|
105 |
... name=u'bugzilla-product', official_malone=False) |
|
106 |
>>> bugzilla = factory.makeBugTracker( |
|
107 |
... bugtrackertype=BugTrackerType.BUGZILLA) |
|
108 |
>>> bugzilla_product.bugtracker = bugzilla |
|
109 |
>>> rt_product = factory.makeProduct( |
|
110 |
... name=u'rt-product', official_malone=False) |
|
111 |
>>> rt = factory.makeBugTracker( |
|
112 |
... bugtrackertype=BugTrackerType.RT) |
|
113 |
>>> rt_product.bugtracker = rt |
|
114 |
||
115 |
>>> list(bugzilla_product.getLinkedBugWatches()) |
|
116 |
[] |
|
7793.5.9
by Bjorn Tillenius
Rename updateRemoteProduct() to something more sensible. |
117 |
>>> updater.updateByBugTrackerType(BugTrackerType.RT) |
7793.5.4
by Bjorn Tillenius
First cut at implementing updateRemoteProduct(). |
118 |
>>> print bugzilla_product.remote_product |
119 |
None |
|
120 |
>>> print rt_product.remote_product |
|
121 |
None |
|
122 |
||
123 |
||
14575.2.1
by Jeroen Vermeulen
Lint. |
124 |
Linked bug watches |
125 |
.................. |
|
7793.5.4
by Bjorn Tillenius
First cut at implementing updateRemoteProduct(). |
126 |
|
127 |
If there are bug watches for a product having a None remote_product, an |
|
128 |
arbitrary bug watch will be retrieved, and queried for its remote |
|
129 |
product. Products having a bug tracker of a different type than the |
|
130 |
given one are ignored. |
|
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
131 |
|
8342.5.11
by Gavin Panella
Amend some more tests. |
132 |
>>> import transaction |
14605.1.1
by Curtis Hovey
Moved canonical.config to lp.services. |
133 |
>>> from lp.services.config import config |
14604.1.1
by Curtis Hovey
Separate test-authoring classes from test-running classes. |
134 |
>>> from lp.testing.layers import LaunchpadZopelessLayer |
8342.5.11
by Gavin Panella
Amend some more tests. |
135 |
|
136 |
>>> def switch_db_to_launchpad(): |
|
137 |
... transaction.commit() |
|
138 |
... LaunchpadZopelessLayer.switchDbUser('launchpad') |
|
139 |
||
140 |
>>> def switch_db_to_updateremoteproduct(): |
|
141 |
... transaction.commit() |
|
142 |
... LaunchpadZopelessLayer.switchDbUser( |
|
143 |
... config.updateremoteproduct.dbuser) |
|
144 |
||
7793.5.14
by Bjorn Tillenius
Add logging to show that the script actually does the right thing. |
145 |
>>> updater = NoNetworkRemoteProductUpdater( |
12070.1.4
by Tim Penhey
Move FakeLogger and BufferLogger to lp.services.log.logging and delete the QuietFakeLogger. |
146 |
... FakeTransaction(), BufferLogger()) |
8342.5.11
by Gavin Panella
Amend some more tests. |
147 |
|
148 |
>>> switch_db_to_launchpad() |
|
149 |
||
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
150 |
>>> bugzilla_bugtask = factory.makeBugTask(target=bugzilla_product) |
151 |
>>> bugzilla_bugwatch = factory.makeBugWatch( |
|
152 |
... '42', bugtracker=bugzilla, bug=bugzilla_bugtask.bug) |
|
153 |
>>> bugzilla_bugtask.bugwatch = bugzilla_bugwatch |
|
154 |
>>> rt_bugtask = factory.makeBugTask(target=rt_product) |
|
155 |
>>> rt_bugwatch = factory.makeBugWatch( |
|
156 |
... '84', bugtracker=rt, bug=rt_bugtask.bug) |
|
157 |
>>> rt_bugtask.bugwatch = rt_bugwatch |
|
158 |
||
8342.5.11
by Gavin Panella
Amend some more tests. |
159 |
>>> switch_db_to_updateremoteproduct() |
7675.85.2
by Jonathan Lange
Undo revision generated by step 2 of process. |
160 |
|
7793.5.9
by Bjorn Tillenius
Rename updateRemoteProduct() to something more sensible. |
161 |
>>> updater.updateByBugTrackerType(BugTrackerType.RT) |
7840.1.1
by Graham Binns
Fixed bug 333354. |
162 |
Initializing DB for bugs: [u'84']. |
163 |
||
7793.5.1
by Bjorn Tillenius
Add basic tests and pseudo-code for updating the remote_product attributes. |
164 |
>>> print rt_product.remote_product |
165 |
product-for-bug-84 |
|
166 |
||
167 |
>>> print bugzilla_product.remote_product |
|
168 |
None |
|
7793.5.5
by Bjorn Tillenius
Make sure that Products having a remote_product set aren't updated. |
169 |
|
170 |
||
14575.2.1
by Jeroen Vermeulen
Lint. |
171 |
remote_product already set |
172 |
.......................... |
|
7793.5.5
by Bjorn Tillenius
Make sure that Products having a remote_product set aren't updated. |
173 |
|
174 |
If a product already has remote_product set, it will not be updated. |
|
175 |
||
8342.5.11
by Gavin Panella
Amend some more tests. |
176 |
>>> switch_db_to_launchpad() |
177 |
||
7793.5.5
by Bjorn Tillenius
Make sure that Products having a remote_product set aren't updated. |
178 |
>>> rt_product = factory.makeProduct(official_malone=False) |
179 |
>>> rt = factory.makeBugTracker( |
|
180 |
... bugtrackertype=BugTrackerType.RT) |
|
181 |
>>> rt_product.bugtracker = rt |
|
182 |
>>> rt_bugtask = factory.makeBugTask(target=rt_product) |
|
183 |
>>> rt_bugwatch = factory.makeBugWatch( |
|
184 |
... '84', bugtracker=rt, bug=rt_bugtask.bug) |
|
185 |
>>> rt_bugtask.bugwatch = rt_bugwatch |
|
186 |
||
8342.5.11
by Gavin Panella
Amend some more tests. |
187 |
>>> switch_db_to_updateremoteproduct() |
188 |
||
7793.5.5
by Bjorn Tillenius
Make sure that Products having a remote_product set aren't updated. |
189 |
>>> rt_product.remote_product = u'already-set' |
7793.5.14
by Bjorn Tillenius
Add logging to show that the script actually does the right thing. |
190 |
>>> updater = NoNetworkRemoteProductUpdater( |
12070.1.4
by Tim Penhey
Move FakeLogger and BufferLogger to lp.services.log.logging and delete the QuietFakeLogger. |
191 |
... FakeTransaction(), BufferLogger()) |
7793.5.9
by Bjorn Tillenius
Rename updateRemoteProduct() to something more sensible. |
192 |
>>> updater.updateByBugTrackerType(BugTrackerType.RT) |
7793.5.5
by Bjorn Tillenius
Make sure that Products having a remote_product set aren't updated. |
193 |
>>> print rt_product.remote_product |
194 |
already-set |
|
7793.5.6
by Bjorn Tillenius
Commit the transaction after each update. |
195 |
|
7793.5.11
by Bjorn Tillenius
Whitespace fixes. |
196 |
|
14575.2.1
by Jeroen Vermeulen
Lint. |
197 |
Transaction handling |
198 |
.................... |
|
7793.5.6
by Bjorn Tillenius
Commit the transaction after each update. |
199 |
|
200 |
To avoid long-running write transactions, the transaction is committed |
|
201 |
after each product's remote_product has been updated. |
|
202 |
||
8342.5.11
by Gavin Panella
Amend some more tests. |
203 |
>>> switch_db_to_launchpad() |
204 |
||
7793.5.6
by Bjorn Tillenius
Commit the transaction after each update. |
205 |
>>> for index in range(3): |
206 |
... rt_product = factory.makeProduct(official_malone=False) |
|
207 |
... rt = factory.makeBugTracker( |
|
208 |
... bugtrackertype=BugTrackerType.RT) |
|
209 |
... rt_product.bugtracker = rt |
|
210 |
... rt_bugtask = factory.makeBugTask(target=rt_product) |
|
211 |
... rt_bugwatch = factory.makeBugWatch( |
|
212 |
... '84', bugtracker=rt, bug=rt_bugtask.bug) |
|
213 |
... rt_bugtask.bugwatch = rt_bugwatch |
|
8342.5.11
by Gavin Panella
Amend some more tests. |
214 |
|
215 |
>>> switch_db_to_updateremoteproduct() |
|
216 |
||
7793.5.8
by Bjorn Tillenius
Rename TestRemoteProductUpdater to show intent. |
217 |
>>> updater = NoNetworkRemoteProductUpdater( |
12070.1.4
by Tim Penhey
Move FakeLogger and BufferLogger to lp.services.log.logging and delete the QuietFakeLogger. |
218 |
... FakeTransaction(log_calls=True), BufferLogger()) |
7840.1.1
by Graham Binns
Fixed bug 333354. |
219 |
>>> updater.print_method_calls = False |
7793.5.9
by Bjorn Tillenius
Rename updateRemoteProduct() to something more sensible. |
220 |
>>> updater.updateByBugTrackerType(BugTrackerType.RT) |
7840.1.1
by Graham Binns
Fixed bug 333354. |
221 |
Initializing DB for bugs: [u'84']. |
222 |
COMMIT |
|
223 |
Initializing DB for bugs: [u'84']. |
|
224 |
COMMIT |
|
225 |
Initializing DB for bugs: [u'84']. |
|
7793.5.6
by Bjorn Tillenius
Commit the transaction after each update. |
226 |
COMMIT |
7793.5.13
by Bjorn Tillenius
Add a cronscript for updating the remote_product. |
227 |
|
228 |
||
14575.2.1
by Jeroen Vermeulen
Lint. |
229 |
Error handling |
230 |
.............. |
|
7844.2.1
by Graham Binns
update-remote-product should now handle errors. |
231 |
|
232 |
If the ExternalBugTracker raises any BugWatchUpdateErrors, |
|
233 |
updateByBugTrackerType() will simply log the error and then continue. |
|
234 |
This is a simplistic approach but it means that problems with one bug |
|
235 |
tracker don't break the run for all bug trackers. |
|
236 |
||
8342.5.11
by Gavin Panella
Amend some more tests. |
237 |
>>> switch_db_to_launchpad() |
238 |
||
7844.2.1
by Graham Binns
update-remote-product should now handle errors. |
239 |
>>> new_rt_product = factory.makeProduct( |
240 |
... name='fooix', official_malone=False) |
|
241 |
>>> new_rt_product.bugtracker = rt |
|
242 |
>>> new_rt_bugtask = factory.makeBugTask(target=new_rt_product) |
|
243 |
>>> new_rt_bugwatch = factory.makeBugWatch( |
|
244 |
... '42', bugtracker=rt, bug=new_rt_bugtask.bug) |
|
245 |
>>> new_rt_bugtask.bugwatch = new_rt_bugwatch |
|
246 |
||
8342.5.11
by Gavin Panella
Amend some more tests. |
247 |
>>> switch_db_to_updateremoteproduct() |
8523.3.1
by Gavin Panella
Bugs tree reorg after automated migration. |
248 |
>>> from lp.bugs.externalbugtracker.base import ( |
7844.2.1
by Graham Binns
update-remote-product should now handle errors. |
249 |
... BugNotFound, BugWatchUpdateError) |
250 |
>>> class BrokenOnInitExternalBugTracker( |
|
251 |
... FakeExternalBugTracker): |
|
252 |
... def initializeRemoteBugDB(self, bug_ids): |
|
253 |
... raise BugWatchUpdateError("This here is an error") |
|
254 |
||
255 |
>>> updater.logger = FakeLogger() |
|
256 |
>>> updater.external_bugtracker_to_return = ( |
|
257 |
... BrokenOnInitExternalBugTracker) |
|
258 |
>>> updater.updateByBugTrackerType(BugTrackerType.RT) |
|
12070.1.41
by Tim Penhey
Changed my mind about the colon. Removed it. |
259 |
INFO 1 projects using RT needing updating. |
260 |
DEBUG Trying to update fooix |
|
7844.2.1
by Graham Binns
update-remote-product should now handle errors. |
261 |
ERROR Unable to set remote_product for 'fooix': This here is an error |
262 |
||
263 |
>>> class BrokenOnGetRemoteProductExternalBugTracker( |
|
264 |
... FakeExternalBugTracker): |
|
265 |
... def getRemoteProduct(self, remote_bug): |
|
266 |
... raise BugNotFound("Didn't find bug %s." % remote_bug) |
|
267 |
||
268 |
>>> updater.external_bugtracker_to_return = ( |
|
269 |
... BrokenOnGetRemoteProductExternalBugTracker) |
|
270 |
>>> updater.updateByBugTrackerType(BugTrackerType.RT) |
|
12070.1.41
by Tim Penhey
Changed my mind about the colon. Removed it. |
271 |
INFO 1 projects using RT needing updating. |
272 |
DEBUG Trying to update fooix |
|
7844.2.1
by Graham Binns
update-remote-product should now handle errors. |
273 |
Initializing DB for bugs: [u'42']. |
12070.1.41
by Tim Penhey
Changed my mind about the colon. Removed it. |
274 |
ERROR Unable to set remote_product for 'fooix': Didn't find bug 42. |
7844.2.1
by Graham Binns
update-remote-product should now handle errors. |
275 |
|
7849.2.1
by Graham Binns
Fixed bug 334448 |
276 |
AssertionErrors are also handled. |
277 |
||
278 |
>>> class RaisesAssertionErrorExternalBugTracker(FakeExternalBugTracker): |
|
279 |
... def initializeRemoteBugDB(self, bug_ids): |
|
280 |
... assert True == False, "True isn't False!" |
|
281 |
||
282 |
>>> updater.external_bugtracker_to_return = ( |
|
283 |
... RaisesAssertionErrorExternalBugTracker) |
|
284 |
>>> updater.updateByBugTrackerType(BugTrackerType.RT) |
|
12070.1.41
by Tim Penhey
Changed my mind about the colon. Removed it. |
285 |
INFO 1 projects using RT needing updating. |
286 |
DEBUG Trying to update fooix |
|
287 |
ERROR Unable to set remote_product for 'fooix': True isn't False! |
|
7849.2.1
by Graham Binns
Fixed bug 334448 |
288 |