Merge lp:~allenap/launchpad/structural-subscriptions-with-filters-4 into lp:launchpad/db-devel

Proposed by Gavin Panella
Status: Merged
Approved by: Aaron Bentley
Approved revision: no longer in the source branch.
Merged at revision: 9866
Proposed branch: lp:~allenap/launchpad/structural-subscriptions-with-filters-4
Merge into: lp:launchpad/db-devel
Prerequisite: lp:~allenap/launchpad/structural-subscriptions-with-filters-3
Diff against target: 545 lines (+300/-26)
10 files modified
lib/canonical/launchpad/interfaces/_schema_circular_imports.py (+6/-3)
lib/lp/bugs/configure.zcml (+12/-0)
lib/lp/bugs/interfaces/bugsubscriptionfilter.py (+81/-0)
lib/lp/bugs/model/bugsubscriptionfilter.py (+10/-0)
lib/lp/bugs/model/tests/test_bugsubscriptionfilter.py (+104/-0)
lib/lp/bugs/security.py (+12/-0)
lib/lp/registry/interfaces/structuralsubscription.py (+12/-1)
lib/lp/registry/model/structuralsubscription.py (+14/-0)
lib/lp/registry/tests/test_structuralsubscription.py (+41/-0)
lib/lp/registry/tests/test_structuralsubscriptiontarget.py (+8/-22)
To merge this branch: bzr merge lp:~allenap/launchpad/structural-subscriptions-with-filters-4
Reviewer Review Type Date Requested Status
Aaron Bentley (community) Approve
Review via email: mp+37467@code.launchpad.net

Description of the change

This branch:

- Adds an IBugSubscriptionFilter interface,

- A security adapter for it,

- A bug_filters property and a newBugFilter() method to IStructuralSubscription,

- Uses the convenience API introduced in the prerequisite branch to simplify testing.

To post a comment you must log in.
Revision history for this message
Aaron Bentley (abentley) wrote :

There are a few naming issues:
Some classes use destroySelf, others use delete. We should pick one project-wide, and stick to it.

I think that IBugSubscriptionFilterMethods vs IBugSubscriptionFilterAttributes is not a good distinction, because methods that are not mutators should be on the same interface as its attributes so that they have the same permissions.

Perhaps IBugSubscription and IBugSubscriptionMutator?

If you really want to keep IBugSubscriptionFilterMethods and IBugSubscriptionFilterAttributes, I can live with that.

review: Approve
Revision history for this message
Gavin Panella (allenap) wrote :

> There are a few naming issues:
> Some classes use destroySelf, others use delete. We should pick one project-
> wide, and stick to it.

I don't know which one to choose! destroySelf() is an SQLObject-ism so
I steered away from that, if only to avoid confusion.

> I think that IBugSubscriptionFilterMethods vs IBugSubscriptionFilterAttributes
> is not a good distinction, because methods that are not mutators should be on
> the same interface as its attributes so that they have the same permissions.
>
> Perhaps IBugSubscription and IBugSubscriptionMutator?

I couldn't think of a better solution to this, but I think something
like this might make sense:

  IBugSubscriptionFilterAttributes
  --> IBugSubscriptionFilterPublic

  IBugSubscriptionFilterMethods
  --> IBugSubscriptionFilter, and inherits from
      IBugSubscriptionFilterPublic

I've already sent this branch off, so I'll change it in the follow-up.

Thank you for the review :)

Revision history for this message
Gavin Panella (allenap) wrote :

Ach, Zope complains if I try to combine things in "odd" ways, so I'm
going to stick with Attributes and Methods. They're only referenced in
the same file and in one configure.zcml so anyone who has a better
approach can change them with ease.

Revision history for this message
Aaron Bentley (abentley) wrote :

Alright.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/canonical/launchpad/interfaces/_schema_circular_imports.py'
2--- lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-09-29 09:28:17 +0000
3+++ lib/canonical/launchpad/interfaces/_schema_circular_imports.py 2010-10-05 09:31:09 +0000
4@@ -40,6 +40,7 @@
5 )
6 from lp.bugs.interfaces.bugbranch import IBugBranch
7 from lp.bugs.interfaces.bugnomination import IBugNomination
8+from lp.bugs.interfaces.bugsubscriptionfilter import IBugSubscriptionFilter
9 from lp.bugs.interfaces.bugtarget import (
10 IBugTarget,
11 IHasBugs,
12@@ -111,9 +112,7 @@
13 ISourcePackagePublishingHistory,
14 ISourcePackagePublishingHistoryPublic,
15 )
16-from lp.soyuz.interfaces.queue import (
17- IPackageUpload,
18- )
19+from lp.soyuz.interfaces.queue import IPackageUpload
20 from lp.soyuz.interfaces.sourcepackagerelease import ISourcePackageRelease
21 from lp.translations.interfaces.pofile import IPOFile
22 from lp.translations.interfaces.potemplate import (
23@@ -397,13 +396,17 @@
24 patch_reference_property(IPackageUpload, 'archive', IArchive)
25
26 # IStructuralSubscription
27+patch_collection_property(
28+ IStructuralSubscription, 'bug_filters', IBugSubscriptionFilter)
29 patch_reference_property(
30 IStructuralSubscription, 'target', IStructuralSubscriptionTarget)
31
32+# IStructuralSubscriptionTarget
33 patch_reference_property(
34 IStructuralSubscriptionTarget, 'parent_subscription_target',
35 IStructuralSubscriptionTarget)
36
37+# ISourcePackageRelease
38 patch_reference_property(
39 ISourcePackageRelease, 'source_package_recipe_build',
40 ISourcePackageRecipeBuild)
41
42=== modified file 'lib/lp/bugs/configure.zcml'
43--- lib/lp/bugs/configure.zcml 2010-09-30 05:47:44 +0000
44+++ lib/lp/bugs/configure.zcml 2010-10-05 09:31:09 +0000
45@@ -609,6 +609,18 @@
46 permission="zope.Public"
47 set_schema="lp.bugs.interfaces.bugsubscription.IBugSubscription"/>
48 </class>
49+
50+ <!-- BugSubscriptionFilter -->
51+ <class
52+ class=".model.bugsubscriptionfilter.BugSubscriptionFilter">
53+ <allow
54+ interface=".interfaces.bugsubscriptionfilter.IBugSubscriptionFilterAttributes"/>
55+ <require
56+ permission="launchpad.Edit"
57+ interface=".interfaces.bugsubscriptionfilter.IBugSubscriptionFilterMethods"
58+ set_schema=".interfaces.bugsubscriptionfilter.IBugSubscriptionFilterAttributes" />
59+ </class>
60+
61 <facet
62 facet="bugs">
63
64
65=== added file 'lib/lp/bugs/interfaces/bugsubscriptionfilter.py'
66--- lib/lp/bugs/interfaces/bugsubscriptionfilter.py 1970-01-01 00:00:00 +0000
67+++ lib/lp/bugs/interfaces/bugsubscriptionfilter.py 2010-10-05 09:31:09 +0000
68@@ -0,0 +1,81 @@
69+# Copyright 2010 Canonical Ltd. This software is licensed under the
70+# GNU Affero General Public License version 3 (see the file LICENSE).
71+
72+"""Bug subscription filter interfaces."""
73+
74+__metaclass__ = type
75+__all__ = [
76+ "IBugSubscriptionFilter",
77+ ]
78+
79+
80+from lazr.restful.fields import Reference
81+from zope.interface import Interface
82+from zope.schema import (
83+ Bool,
84+ Choice,
85+ FrozenSet,
86+ Text,
87+ )
88+
89+from canonical.launchpad import _
90+from lp.bugs.interfaces.bugtask import (
91+ BugTaskImportance,
92+ BugTaskStatus,
93+ )
94+from lp.registry.interfaces.structuralsubscription import (
95+ IStructuralSubscription,
96+ )
97+from lp.services.fields import SearchTag
98+
99+
100+class IBugSubscriptionFilterAttributes(Interface):
101+ """Attributes of `IBugSubscriptionFilter`."""
102+
103+ structural_subscription = Reference(
104+ IStructuralSubscription,
105+ title=_("Structural subscription"),
106+ required=True, readonly=True)
107+
108+ find_all_tags = Bool(
109+ title=_("All given tags must be found, or any."),
110+ required=True, default=False)
111+ include_any_tags = Bool(
112+ title=_("Include any tags."),
113+ required=True, default=False)
114+ exclude_any_tags = Bool(
115+ title=_("Exclude all tags."),
116+ required=True, default=False)
117+
118+ description = Text(
119+ title=_("Description of this filter."),
120+ required=False)
121+
122+ statuses = FrozenSet(
123+ title=_("The statuses to filter on."),
124+ required=True, default=frozenset(),
125+ value_type=Choice(
126+ title=_('Status'), vocabulary=BugTaskStatus))
127+
128+ importances = FrozenSet(
129+ title=_("The importances to filter on."),
130+ required=True, default=frozenset(),
131+ value_type=Choice(
132+ title=_('Importance'), vocabulary=BugTaskImportance))
133+
134+ tags = FrozenSet(
135+ title=_("The tags to filter on."),
136+ required=True, default=frozenset(),
137+ value_type=SearchTag())
138+
139+
140+class IBugSubscriptionFilterMethods(Interface):
141+ """Methods of `IBugSubscriptionFilter`."""
142+
143+ def delete():
144+ """Delete this bug subscription filter."""
145+
146+
147+class IBugSubscriptionFilter(
148+ IBugSubscriptionFilterAttributes, IBugSubscriptionFilterMethods):
149+ """A bug subscription filter."""
150
151=== modified file 'lib/lp/bugs/model/bugsubscriptionfilter.py'
152--- lib/lp/bugs/model/bugsubscriptionfilter.py 2010-10-05 09:31:08 +0000
153+++ lib/lp/bugs/model/bugsubscriptionfilter.py 2010-10-05 09:31:09 +0000
154@@ -13,11 +13,14 @@
155 Bool,
156 Int,
157 Reference,
158+ Store,
159 Unicode,
160 )
161+from zope.interface import implements
162
163 from canonical.launchpad import searchbuilder
164 from canonical.launchpad.interfaces.lpstorm import IStore
165+from lp.bugs.interfaces.bugsubscriptionfilter import IBugSubscriptionFilter
166 from lp.bugs.model.bugsubscriptionfilterimportance import (
167 BugSubscriptionFilterImportance,
168 )
169@@ -30,6 +33,8 @@
170 class BugSubscriptionFilter(Storm):
171 """A filter to specialize a *structural* subscription."""
172
173+ implements(IBugSubscriptionFilter)
174+
175 __storm_table__ = "BugSubscriptionFilter"
176
177 id = Int(primary=True)
178@@ -178,3 +183,8 @@
179 tags = property(
180 _get_tags, _set_tags, doc=(
181 "A frozenset of tags filtered on."))
182+
183+ def delete(self):
184+ """See `IBugSubscriptionFilter`."""
185+ self.importances = self.statuses = self.tags = ()
186+ Store.of(self).remove(self)
187
188=== modified file 'lib/lp/bugs/model/tests/test_bugsubscriptionfilter.py'
189--- lib/lp/bugs/model/tests/test_bugsubscriptionfilter.py 2010-10-05 09:31:08 +0000
190+++ lib/lp/bugs/model/tests/test_bugsubscriptionfilter.py 2010-10-05 09:31:09 +0000
191@@ -5,6 +5,10 @@
192
193 __metaclass__ = type
194
195+from storm.store import Store
196+from zope.security.interfaces import Unauthorized
197+from zope.security.proxy import ProxyFactory
198+
199 from canonical.launchpad import searchbuilder
200 from canonical.launchpad.interfaces.lpstorm import IStore
201 from canonical.testing import DatabaseFunctionalLayer
202@@ -14,7 +18,9 @@
203 )
204 from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilter
205 from lp.testing import (
206+ anonymous_logged_in,
207 login_person,
208+ person_logged_in,
209 TestCaseWithFactory,
210 )
211
212@@ -70,6 +76,24 @@
213 self.assertIs(None, bug_subscription_filter.other_parameters)
214 self.assertIs(None, bug_subscription_filter.description)
215
216+ def test_delete(self):
217+ """`BugSubscriptionFilter` objects can be deleted.
218+
219+ Child objects - like `BugSubscriptionFilterTags` - will also be
220+ deleted.
221+ """
222+ bug_subscription_filter = BugSubscriptionFilter()
223+ bug_subscription_filter.structural_subscription = self.subscription
224+ bug_subscription_filter.importances = [BugTaskImportance.LOW]
225+ bug_subscription_filter.statuses = [BugTaskStatus.NEW]
226+ bug_subscription_filter.tags = [u"foo"]
227+ IStore(bug_subscription_filter).flush()
228+ self.assertIsNot(None, Store.of(bug_subscription_filter))
229+ # Delete.
230+ bug_subscription_filter.delete()
231+ IStore(bug_subscription_filter).flush()
232+ self.assertIs(None, Store.of(bug_subscription_filter))
233+
234 def test_statuses(self):
235 # The statuses property is a frozenset of the statuses that are
236 # filtered upon.
237@@ -196,3 +220,83 @@
238 bug_subscription_filter.tags = searchbuilder.any(u"baz")
239 self.assertEqual(frozenset((u"baz",)), bug_subscription_filter.tags)
240 self.assertFalse(bug_subscription_filter.find_all_tags)
241+
242+
243+class TestBugSubscriptionFilterPermissions(TestCaseWithFactory):
244+
245+ layer = DatabaseFunctionalLayer
246+
247+ def setUp(self):
248+ super(TestBugSubscriptionFilterPermissions, self).setUp()
249+ self.target = self.factory.makeProduct()
250+ self.subscriber = self.target.owner
251+ with person_logged_in(self.subscriber):
252+ self.subscription = self.target.addBugSubscription(
253+ self.subscriber, self.subscriber)
254+
255+ def test_read_to_all(self):
256+ """`BugSubscriptionFilter`s can be read by anyone."""
257+ bug_subscription_filter = BugSubscriptionFilter()
258+ bug_subscription_filter.structural_subscription = self.subscription
259+ bug_subscription_filter = ProxyFactory(bug_subscription_filter)
260+ with person_logged_in(self.subscriber):
261+ bug_subscription_filter.find_all_tags
262+ with person_logged_in(self.factory.makePerson()):
263+ bug_subscription_filter.find_all_tags
264+ with anonymous_logged_in():
265+ bug_subscription_filter.find_all_tags
266+
267+ def test_write_to_subscribers(self):
268+ """`BugSubscriptionFilter`s can only be modifed by subscribers."""
269+ bug_subscription_filter = BugSubscriptionFilter()
270+ bug_subscription_filter.structural_subscription = self.subscription
271+ bug_subscription_filter = ProxyFactory(bug_subscription_filter)
272+ # The subscriber can edit the filter.
273+ with person_logged_in(self.subscriber):
274+ bug_subscription_filter.find_all_tags = True
275+ # Any other person is denied rights to edit the filter.
276+ with person_logged_in(self.factory.makePerson()):
277+ self.assertRaises(
278+ Unauthorized, setattr, bug_subscription_filter,
279+ "find_all_tags", True)
280+ # Anonymous users are also denied.
281+ with anonymous_logged_in():
282+ self.assertRaises(
283+ Unauthorized, setattr, bug_subscription_filter,
284+ "find_all_tags", True)
285+
286+ def test_delete_by_subscribers(self):
287+ """`BugSubscriptionFilter`s can only be deleted by subscribers."""
288+ bug_subscription_filter = BugSubscriptionFilter()
289+ bug_subscription_filter.structural_subscription = self.subscription
290+ bug_subscription_filter = ProxyFactory(bug_subscription_filter)
291+ # Anonymous users are denied rights to delete the filter.
292+ with anonymous_logged_in():
293+ self.assertRaises(
294+ Unauthorized, getattr, bug_subscription_filter, "delete")
295+ # Any other person is also denied.
296+ with person_logged_in(self.factory.makePerson()):
297+ self.assertRaises(
298+ Unauthorized, getattr, bug_subscription_filter, "delete")
299+ # The subscriber can delete the filter.
300+ with person_logged_in(self.subscriber):
301+ bug_subscription_filter.delete()
302+
303+ def test_write_to_any_user_when_no_subscription(self):
304+ """
305+ `BugSubscriptionFilter`s can be modifed by any logged-in user when
306+ there is no related subscription.
307+ """
308+ bug_subscription_filter = BugSubscriptionFilter()
309+ bug_subscription_filter = ProxyFactory(bug_subscription_filter)
310+ # The subscriber can edit the filter.
311+ with person_logged_in(self.subscriber):
312+ bug_subscription_filter.find_all_tags = True
313+ # Any other person can edit the filter.
314+ with person_logged_in(self.factory.makePerson()):
315+ bug_subscription_filter.find_all_tags = True
316+ # Anonymous users are denied rights to edit the filter.
317+ with anonymous_logged_in():
318+ self.assertRaises(
319+ Unauthorized, setattr, bug_subscription_filter,
320+ "find_all_tags", True)
321
322=== modified file 'lib/lp/bugs/security.py'
323--- lib/lp/bugs/security.py 2010-09-10 16:21:21 +0000
324+++ lib/lp/bugs/security.py 2010-10-05 09:31:09 +0000
325@@ -17,6 +17,7 @@
326 from lp.bugs.interfaces.bugbranch import IBugBranch
327 from lp.bugs.interfaces.bugnomination import IBugNomination
328 from lp.bugs.interfaces.bugsubscription import IBugSubscription
329+from lp.bugs.interfaces.bugsubscriptionfilter import IBugSubscriptionFilter
330 from lp.bugs.interfaces.bugtracker import IBugTracker
331 from lp.bugs.interfaces.bugwatch import IBugWatch
332
333@@ -171,3 +172,14 @@
334 return (
335 user.in_admin or
336 user.in_launchpad_developers)
337+
338+
339+class EditBugSubscriptionFilter(AuthorizationBase):
340+ """Bug subscription filters may only be modified by the subscriber."""
341+ permission = 'launchpad.Edit'
342+ usedfor = IBugSubscriptionFilter
343+
344+ def checkAuthenticated(self, user):
345+ return (
346+ self.obj.structural_subscription is None or
347+ user.inTeam(self.obj.structural_subscription.subscriber))
348
349=== modified file 'lib/lp/registry/interfaces/structuralsubscription.py'
350--- lib/lp/registry/interfaces/structuralsubscription.py 2010-10-05 09:31:08 +0000
351+++ lib/lp/registry/interfaces/structuralsubscription.py 2010-10-05 09:31:09 +0000
352@@ -32,7 +32,10 @@
353 operation_returns_entry,
354 REQUEST_USER,
355 )
356-from lazr.restful.fields import Reference
357+from lazr.restful.fields import (
358+ CollectionField,
359+ Reference,
360+ )
361 from zope.interface import (
362 Attribute,
363 Interface,
364@@ -126,6 +129,14 @@
365 required=True, readonly=True,
366 title=_("The structure to which this subscription belongs.")))
367
368+ bug_filters = CollectionField(
369+ title=_('List of bug filters that narrow this subscription.'),
370+ readonly=True, required=False,
371+ value_type=Reference(schema=Interface))
372+
373+ def newBugFilter():
374+ """Returns a new `BugSubscriptionFilter` for this subscription."""
375+
376
377 class IStructuralSubscriptionTargetRead(Interface):
378 """A Launchpad Structure allowing users to subscribe to it.
379
380=== modified file 'lib/lp/registry/model/structuralsubscription.py'
381--- lib/lp/registry/model/structuralsubscription.py 2010-10-05 09:31:08 +0000
382+++ lib/lp/registry/model/structuralsubscription.py 2010-10-05 09:31:09 +0000
383@@ -26,6 +26,7 @@
384 SQLBase,
385 )
386 from canonical.launchpad.interfaces.launchpad import ILaunchpadCelebrities
387+from canonical.launchpad.interfaces.lpstorm import IStore
388 from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilter
389 from lp.bugs.model.bugsubscriptionfilterimportance import (
390 BugSubscriptionFilterImportance,
391@@ -135,6 +136,19 @@
392 else:
393 raise AssertionError('StructuralSubscription has no target.')
394
395+ @property
396+ def bug_filters(self):
397+ """See `IStructuralSubscription`."""
398+ return IStore(BugSubscriptionFilter).find(
399+ BugSubscriptionFilter,
400+ BugSubscriptionFilter.structural_subscription == self)
401+
402+ def newBugFilter(self):
403+ """See `IStructuralSubscription`."""
404+ bug_filter = BugSubscriptionFilter()
405+ bug_filter.structural_subscription = self
406+ return bug_filter
407+
408
409 class DistroSeriesTargetHelper:
410 """A helper for `IDistroSeries`s."""
411
412=== added file 'lib/lp/registry/tests/test_structuralsubscription.py'
413--- lib/lp/registry/tests/test_structuralsubscription.py 1970-01-01 00:00:00 +0000
414+++ lib/lp/registry/tests/test_structuralsubscription.py 2010-10-05 09:31:09 +0000
415@@ -0,0 +1,41 @@
416+# Copyright 2010 Canonical Ltd. This software is licensed under the
417+# GNU Affero General Public License version 3 (see the file LICENSE).
418+
419+"""Tests for `StructuralSubscription`."""
420+
421+__metaclass__ = type
422+
423+from canonical.testing.layers import LaunchpadZopelessLayer
424+from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilter
425+from lp.testing import TestCaseWithFactory
426+
427+
428+class TestStructuralSubscription(TestCaseWithFactory):
429+
430+ layer = LaunchpadZopelessLayer
431+
432+ def test_bug_filters_empty(self):
433+ # StructuralSubscription.filters returns the BugSubscriptionFilter
434+ # records associated with this subscription. It's empty to begin with.
435+ product = self.factory.makeProduct()
436+ subscription = product.addSubscription(product.owner, product.owner)
437+ self.assertEqual([], list(subscription.bug_filters))
438+
439+ def test_bug_filters(self):
440+ # StructuralSubscription.filters returns the BugSubscriptionFilter
441+ # records associated with this subscription.
442+ product = self.factory.makeProduct()
443+ subscription = product.addSubscription(product.owner, product.owner)
444+ subscription_filter = BugSubscriptionFilter()
445+ subscription_filter.structural_subscription = subscription
446+ self.assertEqual([subscription_filter], list(subscription.bug_filters))
447+
448+ def test_newBugFilter(self):
449+ # Structural_Subscription.newBugFilter() creates a new subscription
450+ # filter linked to the subscription.
451+ product = self.factory.makeProduct()
452+ subscription = product.addSubscription(product.owner, product.owner)
453+ subscription_filter = subscription.newBugFilter()
454+ self.assertEqual(
455+ subscription, subscription_filter.structural_subscription)
456+ self.assertEqual([subscription_filter], list(subscription.bug_filters))
457
458=== modified file 'lib/lp/registry/tests/test_structuralsubscriptiontarget.py'
459--- lib/lp/registry/tests/test_structuralsubscriptiontarget.py 2010-10-05 09:31:08 +0000
460+++ lib/lp/registry/tests/test_structuralsubscriptiontarget.py 2010-10-05 09:31:09 +0000
461@@ -33,12 +33,6 @@
462 BugTaskStatus,
463 )
464 from lp.bugs.model.bugsubscriptionfilter import BugSubscriptionFilter
465-from lp.bugs.model.bugsubscriptionfilterimportance import (
466- BugSubscriptionFilterImportance,
467- )
468-from lp.bugs.model.bugsubscriptionfilterstatus import (
469- BugSubscriptionFilterStatus,
470- )
471 from lp.bugs.tests.test_bugtarget import bugtarget_filebug
472 from lp.registry.enum import BugNotificationLevel
473 from lp.registry.errors import (
474@@ -208,9 +202,7 @@
475 # Filter the subscription to bugs in the CONFIRMED state.
476 subscription_filter = BugSubscriptionFilter()
477 subscription_filter.structural_subscription = subscription
478- subscription_filter_status = BugSubscriptionFilterStatus()
479- subscription_filter_status.filter = subscription_filter
480- subscription_filter_status.status = BugTaskStatus.CONFIRMED
481+ subscription_filter.statuses = [BugTaskStatus.CONFIRMED]
482
483 # With the filter the subscription is not found.
484 subscriptions_for_bug = self.target.getSubscriptionsForBug(
485@@ -218,7 +210,7 @@
486 self.assertEqual([], list(subscriptions_for_bug))
487
488 # If the filter is adjusted, the subscription is found again.
489- subscription_filter_status.status = bugtask.status
490+ subscription_filter.statuses = [bugtask.status]
491 subscriptions_for_bug = self.target.getSubscriptionsForBug(
492 bugtask.bug, BugNotificationLevel.NOTHING)
493 self.assertEqual([subscription], list(subscriptions_for_bug))
494@@ -242,9 +234,7 @@
495 # Filter the subscription to bugs in the CRITICAL state.
496 subscription_filter = BugSubscriptionFilter()
497 subscription_filter.structural_subscription = subscription
498- subscription_filter_importance = BugSubscriptionFilterImportance()
499- subscription_filter_importance.filter = subscription_filter
500- subscription_filter_importance.importance = BugTaskImportance.CRITICAL
501+ subscription_filter.importances = [BugTaskImportance.CRITICAL]
502
503 # With the filter the subscription is not found.
504 subscriptions_for_bug = self.target.getSubscriptionsForBug(
505@@ -252,7 +242,7 @@
506 self.assertEqual([], list(subscriptions_for_bug))
507
508 # If the filter is adjusted, the subscription is found again.
509- subscription_filter_importance.importance = bugtask.importance
510+ subscription_filter.importances = [bugtask.importance]
511 subscriptions_for_bug = self.target.getSubscriptionsForBug(
512 bugtask.bug, BugNotificationLevel.NOTHING)
513 self.assertEqual([subscription], list(subscriptions_for_bug))
514@@ -271,12 +261,8 @@
515 # Filter the subscription to bugs in the CRITICAL state.
516 subscription_filter = BugSubscriptionFilter()
517 subscription_filter.structural_subscription = subscription
518- subscription_filter_status = BugSubscriptionFilterStatus()
519- subscription_filter_status.filter = subscription_filter
520- subscription_filter_status.status = BugTaskStatus.CONFIRMED
521- subscription_filter_importance = BugSubscriptionFilterImportance()
522- subscription_filter_importance.filter = subscription_filter
523- subscription_filter_importance.importance = BugTaskImportance.CRITICAL
524+ subscription_filter.statuses = [BugTaskStatus.CONFIRMED]
525+ subscription_filter.importances = [BugTaskImportance.CRITICAL]
526
527 # With the filter the subscription is not found.
528 subscriptions_for_bug = self.target.getSubscriptionsForBug(
529@@ -285,14 +271,14 @@
530
531 # If the filter is adjusted to match status but not importance, the
532 # subscription is still not found.
533- subscription_filter_status.status = bugtask.status
534+ subscription_filter.statuses = [bugtask.status]
535 subscriptions_for_bug = self.target.getSubscriptionsForBug(
536 bugtask.bug, BugNotificationLevel.NOTHING)
537 self.assertEqual([], list(subscriptions_for_bug))
538
539 # If the filter is adjusted to also match importance, the subscription
540 # is found again.
541- subscription_filter_importance.importance = bugtask.importance
542+ subscription_filter.importances = [bugtask.importance]
543 subscriptions_for_bug = self.target.getSubscriptionsForBug(
544 bugtask.bug, BugNotificationLevel.NOTHING)
545 self.assertEqual([subscription], list(subscriptions_for_bug))

Subscribers

People subscribed via source and target branches

to status/vote changes: