Merge lp:~wgrant/launchpad/rework-bug-default-type-2 into lp:launchpad

Proposed by William Grant
Status: Merged
Approved by: j.c.sackett
Approved revision: no longer in the source branch.
Merged at revision: 15738
Proposed branch: lp:~wgrant/launchpad/rework-bug-default-type-2
Merge into: lp:launchpad
Prerequisite: lp:~wgrant/launchpad/rebuild-projectgroup-filebug
Diff against target: 369 lines (+126/-34)
10 files modified
lib/lp/bugs/browser/bugtarget.py (+21/-15)
lib/lp/bugs/browser/tests/test_bugtarget_filebug.py (+37/-8)
lib/lp/bugs/model/bug.py (+5/-9)
lib/lp/registry/interfaces/distribution.py (+6/-0)
lib/lp/registry/interfaces/product.py (+7/-1)
lib/lp/registry/model/distribution.py (+4/-0)
lib/lp/registry/model/product.py (+7/-0)
lib/lp/registry/tests/test_distribution.py (+7/-1)
lib/lp/registry/tests/test_product.py (+12/-0)
lib/lp/testing/factory.py (+20/-0)
To merge this branch: bzr merge lp:~wgrant/launchpad/rework-bug-default-type-2
Reviewer Review Type Date Requested Status
j.c.sackett (community) Approve
Review via email: mp+117836@code.launchpad.net

Commit message

Introduce {Product,Distribution}.getDefaultBugInformationType, use it in place of Product.private_bugs in most places.

Description of the change

This branch introduces {Product,Distribution}.getDefaultBugInformationType, and replaces most callsites of Product.private_bugs. This simplifies model and view code, and lets us be a bit more flexible.

bugimport still needs porting, as it's a little more complex.

To post a comment you must log in.
Revision history for this message
j.c.sackett (jcsackett) wrote :

Thanks, this looks good.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/bugs/browser/bugtarget.py'
2--- lib/lp/bugs/browser/bugtarget.py 2012-08-02 08:31:08 +0000
3+++ lib/lp/bugs/browser/bugtarget.py 2012-08-03 00:28:29 +0000
4@@ -120,6 +120,7 @@
5 from lp.registry.enums import (
6 InformationType,
7 PRIVATE_INFORMATION_TYPES,
8+ PUBLIC_INFORMATION_TYPES,
9 SECURITY_INFORMATION_TYPES,
10 )
11 from lp.registry.interfaces.distribution import IDistribution
12@@ -276,7 +277,8 @@
13 cache.objects['private_types'] = [
14 type.name for type in PRIVATE_INFORMATION_TYPES]
15 cache.objects['bug_private_by_default'] = (
16- IProduct.providedBy(self.context) and self.context.private_bugs)
17+ self.context.pillar.getDefaultBugInformationType() in
18+ PRIVATE_INFORMATION_TYPES)
19 cache.objects['information_type_data'] = [
20 {'value': term.name, 'description': term.description,
21 'name': term.title,
22@@ -365,14 +367,18 @@
23 return field_names
24
25 @property
26+ def default_information_type(self):
27+ value = self.context.pillar.getDefaultBugInformationType()
28+ if (self.extra_data
29+ and self.extra_data.private
30+ and value in PUBLIC_INFORMATION_TYPES):
31+ value = InformationType.USERDATA
32+ return value
33+
34+ @property
35 def initial_values(self):
36 """Give packagename a default value, if applicable."""
37- if (self.context and IProduct.providedBy(self.context)
38- and self.context.private_bugs):
39- type = InformationType.USERDATA
40- else:
41- type = InformationType.PUBLIC
42- values = {'information_type': type}
43+ values = {'information_type': self.default_information_type}
44
45 if IDistributionSourcePackage.providedBy(self.context):
46 values['packagename'] = self.context.name
47@@ -512,10 +518,14 @@
48 packagename = data.get("packagename")
49
50 information_type = data.get("information_type")
51- # If the old UI is enabled, security bugs are always embargoed
52- # when filed, but can be disclosed after they've been reported.
53- if information_type is None and data.get("security_related"):
54- information_type = InformationType.PRIVATESECURITY
55+ if information_type is None:
56+ # If the old UI is enabled, security bugs are always embargoed
57+ # when filed, but can be disclosed after they've been reported.
58+ # Otherwise we use the default.
59+ if data.get("security_related"):
60+ information_type = InformationType.PRIVATESECURITY
61+ else:
62+ information_type = self.default_information_type
63
64 context = self.context
65
66@@ -559,10 +569,6 @@
67 notifications.append(
68 'Additional information was added to the bug description.')
69
70- if not self.is_bug_supervisor and extra_data.private:
71- if params.information_type in (None, InformationType.PUBLIC):
72- params.information_type = InformationType.USERDATA
73-
74 # Apply any extra options given by privileged users.
75 if self.is_bug_supervisor:
76 if 'assignee' in data:
77
78=== modified file 'lib/lp/bugs/browser/tests/test_bugtarget_filebug.py'
79--- lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2012-08-03 00:18:27 +0000
80+++ lib/lp/bugs/browser/tests/test_bugtarget_filebug.py 2012-08-03 00:28:29 +0000
81@@ -42,7 +42,10 @@
82 find_main_content,
83 find_tag_by_id,
84 )
85-from lp.testing.views import create_initialized_view
86+from lp.testing.views import (
87+ create_initialized_view,
88+ create_view,
89+ )
90
91
92 class TestBugTargetFileBugConfirmationMessage(TestCaseWithFactory):
93@@ -350,7 +353,8 @@
94 }]
95 self.assertEqual(expected_guidelines, view.bug_reporting_guidelines)
96
97- def filebug_via_view(self, private_bugs=False, information_type=None):
98+ def filebug_via_view(self, private_bugs=False, information_type=None,
99+ extra_data_token=None):
100 form = {
101 'field.title': 'A bug',
102 'field.comment': 'A comment',
103@@ -362,28 +366,53 @@
104 if private_bugs:
105 removeSecurityProxy(product).private_bugs = True
106 with person_logged_in(product.owner):
107- view = create_initialized_view(
108- product, '+filebug', form=form, principal=product.owner)
109+ view = create_view(
110+ product, '+filebug', method='POST', form=form,
111+ principal=product.owner)
112+ if extra_data_token is not None:
113+ view = view.publishTraverse(view.request, extra_data_token)
114+ view.initialize()
115 bug_url = view.request.response.getHeader('Location')
116 bug_number = bug_url.split('/')[-1]
117- return getUtility(IBugSet).getByNameOrID(bug_number)
118+ return (getUtility(IBugSet).getByNameOrID(bug_number), view)
119
120 def test_filebug_default_information_type(self):
121 # If we don't specify the bug's information_type, it is PUBLIC for
122 # products with private_bugs=False.
123- bug = self.filebug_via_view()
124+ bug, view = self.filebug_via_view()
125+ self.assertEqual(
126+ InformationType.PUBLIC, view.default_information_type)
127 self.assertEqual(InformationType.PUBLIC, bug.information_type)
128
129 def test_filebug_set_information_type(self):
130 # When we specify the bug's information_type, it is set.
131- bug = self.filebug_via_view(information_type='PRIVATESECURITY')
132+ bug, view = self.filebug_via_view(information_type='PRIVATESECURITY')
133 self.assertEqual(
134 InformationType.PRIVATESECURITY, bug.information_type)
135
136 def test_filebug_information_type_with_private_bugs(self):
137 # If we don't specify the bug's information_type, it is USERDATA for
138 # products with private_bugs=True.
139- bug = self.filebug_via_view(private_bugs=True)
140+ bug, view = self.filebug_via_view(private_bugs=True)
141+ self.assertEqual(
142+ InformationType.USERDATA, view.default_information_type)
143+ self.assertEqual(InformationType.USERDATA, bug.information_type)
144+
145+ def test_filebug_information_type_with_public_blob(self):
146+ # Bugs filed with an apport blob that doesn't request privacy
147+ # are public by default.
148+ blob = self.factory.makeProcessedApportBlob({})
149+ bug, view = self.filebug_via_view(extra_data_token=blob.uuid)
150+ self.assertEqual(
151+ InformationType.PUBLIC, view.default_information_type)
152+ self.assertEqual(InformationType.PUBLIC, bug.information_type)
153+
154+ def test_filebug_information_type_with_private_blob(self):
155+ # An apport blob can ask for the bug to be private.
156+ blob = self.factory.makeProcessedApportBlob({'private': True})
157+ bug, view = self.filebug_via_view(extra_data_token=blob.uuid)
158+ self.assertEqual(
159+ InformationType.USERDATA, view.default_information_type)
160 self.assertEqual(InformationType.USERDATA, bug.information_type)
161
162 def test_filebug_information_type_normal_projects(self):
163
164=== modified file 'lib/lp/bugs/model/bug.py'
165--- lib/lp/bugs/model/bug.py 2012-08-01 07:10:54 +0000
166+++ lib/lp/bugs/model/bug.py 2012-08-03 00:28:29 +0000
167@@ -2638,11 +2638,12 @@
168 # of its attribute values below.
169 params = snapshot_bug_params(bug_params)
170
171+ context = params.product or params.distribution
172+
173 if params.information_type is None:
174- # If the private_bugs flag is set on a product, then
175- # force the new bug report to be private.
176- if params.product and params.product.private_bugs:
177- params.information_type = InformationType.USERDATA
178+ if context is not None:
179+ params.information_type = (
180+ context.getDefaultBugInformationType())
181 else:
182 params.information_type = InformationType.PUBLIC
183
184@@ -2662,11 +2663,6 @@
185 bug, params.owner, target, status=params.status)
186
187 if params.information_type in SECURITY_INFORMATION_TYPES:
188- if params.product:
189- context = params.product
190- else:
191- context = params.distribution
192-
193 if context.security_contact:
194 bug.subscribe(context.security_contact, params.owner)
195 else:
196
197=== modified file 'lib/lp/registry/interfaces/distribution.py'
198--- lib/lp/registry/interfaces/distribution.py 2012-07-26 04:36:03 +0000
199+++ lib/lp/registry/interfaces/distribution.py 2012-08-03 00:28:29 +0000
200@@ -625,6 +625,12 @@
201 :return: A sequence of `InformationType`s.
202 """
203
204+ def getDefaultBugInformationType():
205+ """Get the default information type of a new bug in this distro.
206+
207+ :return: The `InformationType`.
208+ """
209+
210 def userCanEdit(user):
211 """Can the user edit this distribution?"""
212
213
214=== modified file 'lib/lp/registry/interfaces/product.py'
215--- lib/lp/registry/interfaces/product.py 2012-07-26 04:36:03 +0000
216+++ lib/lp/registry/interfaces/product.py 2012-08-03 00:28:29 +0000
217@@ -794,11 +794,17 @@
218 """Mutator for private_bugs that checks entitlement."""
219
220 def getAllowedBugInformationTypes():
221- """Get the information types that a bug in this distribution can have.
222+ """Get the information types that a bug in this project can have.
223
224 :return: A sequence of `InformationType`s.
225 """
226
227+ def getDefaultBugInformationType():
228+ """Get the default information type of a new bug in this project.
229+
230+ :return: The `InformationType`.
231+ """
232+
233 def getVersionSortedSeries(statuses=None, filter_statuses=None):
234 """Return all the series sorted by the name field as a version.
235
236
237=== modified file 'lib/lp/registry/model/distribution.py'
238--- lib/lp/registry/model/distribution.py 2012-08-01 07:10:54 +0000
239+++ lib/lp/registry/model/distribution.py 2012-08-03 00:28:29 +0000
240@@ -1603,6 +1603,10 @@
241 types.discard(InformationType.PROPRIETARY)
242 return types
243
244+ def getDefaultBugInformationType(self):
245+ """See `IDistribution.`"""
246+ return InformationType.PUBLIC
247+
248 def userCanEdit(self, user):
249 """See `IDistribution`."""
250 if user is None:
251
252=== modified file 'lib/lp/registry/model/product.py'
253--- lib/lp/registry/model/product.py 2012-08-01 07:10:54 +0000
254+++ lib/lp/registry/model/product.py 2012-08-03 00:28:29 +0000
255@@ -571,6 +571,13 @@
256 types.discard(InformationType.PROPRIETARY)
257 return types
258
259+ def getDefaultBugInformationType(self):
260+ """See `IDistribution.`"""
261+ if self.private_bugs:
262+ return InformationType.USERDATA
263+ else:
264+ return InformationType.PUBLIC
265+
266 def _ensurePolicies(self, information_types):
267 # Ensure that the product has access policies for the specified
268 # information types.
269
270=== modified file 'lib/lp/registry/tests/test_distribution.py'
271--- lib/lp/registry/tests/test_distribution.py 2012-07-26 04:36:03 +0000
272+++ lib/lp/registry/tests/test_distribution.py 2012-08-03 00:28:29 +0000
273@@ -282,7 +282,13 @@
274 self.assertContentEqual(
275 [InformationType.PUBLIC, InformationType.PUBLICSECURITY,
276 InformationType.PRIVATESECURITY, InformationType.USERDATA],
277- self.factory.makeProduct().getAllowedBugInformationTypes())
278+ self.factory.makeDistribution().getAllowedBugInformationTypes())
279+
280+ def test_getDefaultBugInformationType(self):
281+ # The default information type for distributions is always PUBLIC.
282+ self.assertEqual(
283+ InformationType.PUBLIC,
284+ self.factory.makeDistribution().getDefaultBugInformationType())
285
286
287 class TestDistributionCurrentSourceReleases(
288
289=== modified file 'lib/lp/registry/tests/test_product.py'
290--- lib/lp/registry/tests/test_product.py 2012-07-26 04:36:03 +0000
291+++ lib/lp/registry/tests/test_product.py 2012-08-03 00:28:29 +0000
292@@ -409,6 +409,18 @@
293 InformationType.PRIVATESECURITY, InformationType.USERDATA],
294 self.factory.makeProduct().getAllowedBugInformationTypes())
295
296+ def test_getDefaultBugInformationType_public(self):
297+ # The default information type for normal projects is PUBLIC.
298+ product = self.factory.makeProduct()
299+ self.assertEqual(
300+ InformationType.PUBLIC, product.getDefaultBugInformationType())
301+
302+ def test_getDefaultBugInformationType_private(self):
303+ # private_bugs overrides the default information type to USERDATA.
304+ product = self.factory.makeProduct(private_bugs=True)
305+ self.assertEqual(
306+ InformationType.USERDATA, product.getDefaultBugInformationType())
307+
308
309 class TestProductFiles(TestCase):
310 """Tests for downloadable product files."""
311
312=== modified file 'lib/lp/testing/factory.py'
313--- lib/lp/testing/factory.py 2012-07-31 01:36:44 +0000
314+++ lib/lp/testing/factory.py 2012-08-03 00:28:29 +0000
315@@ -43,6 +43,7 @@
316 import sys
317 from textwrap import dedent
318 from types import InstanceType
319+import uuid
320 import warnings
321
322 from bzrlib.plugins.builder.recipe import BaseRecipeBranch
323@@ -76,6 +77,7 @@
324 )
325 from lp.blueprints.interfaces.specification import ISpecificationSet
326 from lp.blueprints.interfaces.sprint import ISprintSet
327+from lp.bugs.interfaces.apportjob import IProcessApportBlobJobSource
328 from lp.bugs.interfaces.bug import (
329 CreateBugParams,
330 IBugSet,
331@@ -90,6 +92,7 @@
332 CveStatus,
333 ICveSet,
334 )
335+from lp.bugs.model.bug import FileBugData
336 from lp.buildmaster.enums import (
337 BuildFarmJobType,
338 BuildStatus,
339@@ -244,6 +247,9 @@
340 from lp.services.temporaryblobstorage.interfaces import (
341 ITemporaryStorageManager,
342 )
343+from lp.services.temporaryblobstorage.model import (
344+ TemporaryBlobStorage,
345+ )
346 from lp.services.utils import AutoDecorate
347 from lp.services.webapp.dbpolicy import MasterDatabasePolicy
348 from lp.services.webapp.interfaces import (
349@@ -4155,6 +4161,20 @@
350
351 return getUtility(ITemporaryStorageManager).fetch(new_uuid)
352
353+ def makeProcessedApportBlob(self, metadata):
354+ """Create a processed ApportJob with the specified metadata dict.
355+
356+ It doesn't actually run the job. It fakes it, and uses a fake
357+ librarian file so as to work without the librarian.
358+ """
359+ blob = TemporaryBlobStorage(uuid=str(uuid.uuid1()), file_alias=1)
360+ job = getUtility(IProcessApportBlobJobSource).create(blob)
361+ job.job.start()
362+ removeSecurityProxy(job).metadata = {
363+ 'processed_data': FileBugData(**metadata).asDict()}
364+ job.job.complete()
365+ return blob
366+
367 def makeLaunchpadService(self, person=None, version="devel"):
368 if person is None:
369 person = self.makePerson()