Merge lp:~stevenk/launchpad/teach-rasj-about-branches into lp:launchpad

Proposed by Steve Kowalik
Status: Merged
Approved by: William Grant
Approved revision: no longer in the source branch.
Merged at revision: 15601
Proposed branch: lp:~stevenk/launchpad/teach-rasj-about-branches
Merge into: lp:launchpad
Diff against target: 257 lines (+147/-18)
2 files modified
lib/lp/registry/model/sharingjob.py (+63/-17)
lib/lp/registry/tests/test_sharingjob.py (+84/-1)
To merge this branch: bzr merge lp:~stevenk/launchpad/teach-rasj-about-branches
Reviewer Review Type Date Requested Status
William Grant code Approve
Review via email: mp+114106@code.launchpad.net

Commit message

Teach RemoveArtifactSubscriptionsJob to search for inaccessible branches and also remove subscribers, like it does for bugs.

Description of the change

Teach RemoveArtifactSubscriptionsJob about branches.

To post a comment you must log in.
Revision history for this message
William Grant (wgrant) wrote :

51 @property
52 + def branch_ids(self):
53 + if not 'branch_ids' in self.metadata:
54 + return []
55 + return self.metadata['branch_ids']

Isn't that equivalent to "return self.metadata.get('branch_ids', [])"?

We really need to rearchitect the tests for that job, but I guess it's OK for now. Otherwise this looks good, thanks.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'lib/lp/registry/model/sharingjob.py'
2--- lib/lp/registry/model/sharingjob.py 2012-06-29 02:27:31 +0000
3+++ lib/lp/registry/model/sharingjob.py 2012-07-11 01:27:22 +0000
4@@ -41,10 +41,20 @@
5 implements,
6 )
7
8-from lp.bugs.interfaces.bug import IBugSet
9+from lp.bugs.interfaces.bug import (
10+ IBug,
11+ IBugSet,
12+ )
13 from lp.bugs.model.bugsubscription import BugSubscription
14 from lp.bugs.model.bugtaskflat import BugTaskFlat
15 from lp.bugs.model.bugtasksearch import get_bug_privacy_filter_terms
16+from lp.code.interfaces.branch import IBranch
17+from lp.code.interfaces.branchlookup import IBranchLookup
18+from lp.code.model.branch import (
19+ Branch,
20+ get_branch_privacy_filter,
21+ )
22+from lp.code.model.branchsubscription import BranchSubscription
23 from lp.registry.enums import InformationType
24 from lp.registry.interfaces.person import IPersonSet
25 from lp.registry.interfaces.product import IProduct
26@@ -253,12 +263,20 @@
27 information_types=None):
28 """See `IRemoveArtifactSubscriptionsJob`."""
29
30- bug_ids = [bug.id for bug in artifacts or []]
31+ bug_ids = []
32+ branch_ids = []
33+ if artifacts:
34+ for artifact in artifacts:
35+ if IBug.providedBy(artifact):
36+ bug_ids.append(artifact.id)
37+ elif IBranch.providedBy(artifact):
38+ branch_ids.append(artifact.id)
39 information_types = [
40 info_type.value for info_type in information_types or []
41 ]
42 metadata = {
43 'bug_ids': bug_ids,
44+ 'branch_ids': branch_ids,
45 'information_types': information_types,
46 'requestor.id': requestor.id
47 }
48@@ -275,15 +293,21 @@
49
50 @property
51 def bug_ids(self):
52- if not 'bug_ids' in self.metadata:
53- return []
54- return self.metadata['bug_ids']
55+ return self.metadata.get('bug_ids', [])
56
57 @property
58 def bugs(self):
59 return getUtility(IBugSet).getByNumbers(self.bug_ids)
60
61 @property
62+ def branch_ids(self):
63+ return self.metadata.get('branch_ids', [])
64+
65+ @property
66+ def branches(self):
67+ return [getUtility(IBranchLookup).get(id) for id in self.branch_ids]
68+
69+ @property
70 def information_types(self):
71 if not 'information_types' in self.metadata:
72 return []
73@@ -307,6 +331,7 @@
74 'information_types': [t.name for t in self.information_types],
75 'requestor': self.requestor.name,
76 'bug_ids': self.bug_ids,
77+ 'branch_ids': self.branch_ids,
78 'pillar': getattr(self.pillar, 'name', None),
79 'grantee': getattr(self.grantee, 'name', None)
80 }
81@@ -321,33 +346,54 @@
82
83 # Find all bug subscriptions for which the subscriber cannot see the
84 # bug.
85- invisible_filter = (
86- Not(Or(*get_bug_privacy_filter_terms(BugSubscription.person_id))))
87- filters = [invisible_filter]
88+ bug_invisible_filter = Not(
89+ Or(*get_bug_privacy_filter_terms(BugSubscription.person_id)))
90+ bug_filters = [bug_invisible_filter]
91+ branch_invisible_filter = Not(
92+ Or(*get_branch_privacy_filter(BranchSubscription.personID)))
93+ branch_filters = [branch_invisible_filter]
94
95 if self.bug_ids:
96- filters.append(BugTaskFlat.bug_id.is_in(self.bug_ids))
97+ bug_filters.append(BugTaskFlat.bug_id.is_in(self.bug_ids))
98+ elif self.branch_ids:
99+ branch_filters.append(Branch.id.is_in(self.branch_ids))
100 else:
101 if self.information_types:
102- filters.append(
103- BugTaskFlat.information_type.is_in(self.information_types))
104+ bug_filters.append(
105+ BugTaskFlat.information_type.is_in(
106+ self.information_types))
107+ branch_filters.append(
108+ Branch.information_type.is_in(self.information_types))
109 if self.product:
110- filters.append(
111+ bug_filters.append(
112 BugTaskFlat.product == self.product)
113+ branch_filters.append(Branch.product == self.product)
114 if self.distro:
115- filters.append(
116+ bug_filters.append(
117 BugTaskFlat.distribution == self.distro)
118+ branch_filters.append(Branch.distribution == self.distro)
119
120 if self.grantee:
121- filters.append(
122+ bug_filters.append(
123 In(BugSubscription.person_id,
124 Select(
125 TeamParticipation.personID,
126 where=TeamParticipation.team == self.grantee)))
127- subscriptions = IStore(BugSubscription).using(
128+ branch_filters.append(
129+ In(BranchSubscription.personID,
130+ Select(
131+ TeamParticipation.personID,
132+ where=TeamParticipation.team == self.grantee)))
133+
134+ bug_subscriptions = IStore(BugSubscription).using(
135 BugSubscription,
136 Join(BugTaskFlat, BugTaskFlat.bug_id == BugSubscription.bug_id)
137- ).find(BugSubscription, *filters).config(distinct=True)
138- for sub in subscriptions:
139+ ).find(BugSubscription, *bug_filters).config(distinct=True)
140+ branch_subscriptions = IStore(BranchSubscription).find(
141+ BranchSubscription, *branch_filters).config(distinct=True)
142+ for sub in bug_subscriptions:
143 sub.bug.unsubscribe(
144 sub.person, self.requestor, ignore_permissions=True)
145+ for sub in branch_subscriptions:
146+ sub.branch.unsubscribe(
147+ sub.person, self.requestor, ignore_permissions=True)
148
149=== modified file 'lib/lp/registry/tests/test_sharingjob.py'
150--- lib/lp/registry/tests/test_sharingjob.py 2012-06-29 02:15:05 +0000
151+++ lib/lp/registry/tests/test_sharingjob.py 2012-07-11 01:27:22 +0000
152@@ -101,7 +101,7 @@
153 self.requestor, artifacts=[self.bug])
154 return job
155
156- def test_repr(self):
157+ def test_repr_bugs(self):
158 job = self._makeJob()
159 self.assertEqual(
160 '<REMOVE_ARTIFACT_SUBSCRIPTIONS job reconciling subscriptions '
161@@ -109,6 +109,16 @@
162 % (self.bug.id, self.requestor.name),
163 repr(job))
164
165+ def test_repr_branches(self):
166+ requestor = self.factory.makePerson()
167+ branch = self.factory.makeBranch()
168+ job = getUtility(IRemoveArtifactSubscriptionsJobSource).create(
169+ requestor, artifacts=[branch])
170+ self.assertEqual(
171+ '<REMOVE_ARTIFACT_SUBSCRIPTIONS job reconciling subscriptions '
172+ 'for branch_ids=[%d], requestor=%s>'
173+ % (branch.id, requestor.name), repr(job))
174+
175 def test_create_success(self):
176 # Create an instance of SharingJobDerived that delegates to SharingJob.
177 self.assertIs(True, ISharingJobSource.providedBy(SharingJobDerived))
178@@ -323,6 +333,79 @@
179 self.assertIn(artifact_team_grantee, subscribers)
180 self.assertIn(artifact_indirect_grantee, subscribers)
181
182+ def _assert_branch_change_unsubscribes(self, change_callback):
183+ product = self.factory.makeProduct()
184+ owner = self.factory.makePerson()
185+ [policy] = getUtility(IAccessPolicySource).find(
186+ [(product, InformationType.USERDATA)])
187+ # The policy grantees will lose access.
188+ policy_indirect_grantee = self.factory.makePerson()
189+ policy_team_grantee = self.factory.makeTeam(
190+ subscription_policy=TeamSubscriptionPolicy.RESTRICTED,
191+ members=[policy_indirect_grantee])
192+
193+ self.factory.makeAccessPolicyGrant(policy, policy_team_grantee, owner)
194+ login_person(owner)
195+ branch = self.factory.makeBranch(
196+ owner=owner, product=product,
197+ information_type=InformationType.USERDATA)
198+
199+ # The artifact grantees will not lose access when the job is run.
200+ artifact_indirect_grantee = self.factory.makePerson()
201+ artifact_team_grantee = self.factory.makeTeam(
202+ subscription_policy=TeamSubscriptionPolicy.RESTRICTED,
203+ members=[artifact_indirect_grantee])
204+
205+ branch.subscribe(
206+ policy_team_grantee, BranchSubscriptionNotificationLevel.NOEMAIL,
207+ None, CodeReviewNotificationLevel.NOEMAIL, owner)
208+ branch.subscribe(
209+ policy_indirect_grantee,
210+ BranchSubscriptionNotificationLevel.NOEMAIL, None,
211+ CodeReviewNotificationLevel.NOEMAIL, owner)
212+ branch.subscribe(
213+ artifact_team_grantee,
214+ BranchSubscriptionNotificationLevel.NOEMAIL, None,
215+ CodeReviewNotificationLevel.NOEMAIL, owner)
216+ branch.subscribe(
217+ artifact_indirect_grantee,
218+ BranchSubscriptionNotificationLevel.NOEMAIL, None,
219+ CodeReviewNotificationLevel.NOEMAIL, owner)
220+ # Subscribing policy_team_grantee has created an artifact grant so we
221+ # need to revoke that to test the job.
222+ getUtility(IAccessArtifactGrantSource).revokeByArtifact(
223+ getUtility(IAccessArtifactSource).find(
224+ [branch]), [policy_team_grantee])
225+
226+ # policy grantees are subscribed because the job has not been run yet.
227+ #subscribers = removeSecurityProxy(branch).subscribers
228+ self.assertIn(policy_team_grantee, branch.subscribers)
229+ self.assertIn(policy_indirect_grantee, branch.subscribers)
230+
231+ # Change branch attributes so that it can become inaccessible for
232+ # some users.
233+ change_callback(branch)
234+ reconcile_access_for_artifact(
235+ branch, branch.information_type, [branch.product])
236+
237+ getUtility(IRemoveArtifactSubscriptionsJobSource).create(
238+ owner, [branch])
239+ with block_on_job(self):
240+ transaction.commit()
241+
242+ # Check the result. Policy grantees will be unsubscribed.
243+ self.assertNotIn(policy_team_grantee, branch.subscribers)
244+ self.assertNotIn(policy_indirect_grantee, branch.subscribers)
245+ self.assertIn(artifact_team_grantee, branch.subscribers)
246+ self.assertIn(artifact_indirect_grantee, branch.subscribers)
247+
248+ def test_change_information_type_branch(self):
249+ def change_information_type(branch):
250+ removeSecurityProxy(branch).information_type = (
251+ InformationType.EMBARGOEDSECURITY)
252+
253+ self._assert_branch_change_unsubscribes(change_information_type)
254+
255 def test_change_information_type(self):
256 # Changing the information type of a bug unsubscribes users who can no
257 # longer see the bug.