Merge lp:~adeuring/launchpad/specifications-sharing-service into lp:launchpad

Proposed by Abel Deuring
Status: Merged
Approved by: Abel Deuring
Approved revision: no longer in the source branch.
Merged at revision: 15976
Proposed branch: lp:~adeuring/launchpad/specifications-sharing-service
Merge into: lp:launchpad
Diff against target: 580 lines (+201/-44)
8 files modified
lib/lp/bugs/model/bug.py (+1/-1)
lib/lp/code/browser/branchsubscription.py (+1/-1)
lib/lp/code/model/branch.py (+1/-1)
lib/lp/code/model/tests/test_branchsubscription.py (+2/-2)
lib/lp/registry/browser/pillar.py (+1/-1)
lib/lp/registry/interfaces/sharingservice.py (+17/-1)
lib/lp/registry/services/sharingservice.py (+76/-8)
lib/lp/registry/services/tests/test_sharingservice.py (+102/-29)
To merge this branch: bzr merge lp:~adeuring/launchpad/specifications-sharing-service
Reviewer Review Type Date Requested Status
Richard Harding (community) Approve
Review via email: mp+124978@code.launchpad.net

Commit message

SharingService.getSharedArtifacts() and SharingServcie.getVisibleArtifacts() return specs in addition to bugs and branches.

Description of the change

This branch updates several methods of the class SharingService so that
they can deal with specifications in addition to bugs and branches.

changed methods:

    getSharedArtifacts()
    getVisibleArtifacts()

new method:

    getSharedSpecifications()

The diff contains some "noise" because some changed methods returned
a tuples (bugs, branches) before this change, and now return a tuple
(bugs, branches, specifications). This requires of course changes in
all callsites.

The method getVisibleArtifacts() uses IBugTaskSet.search() to find
"the right" bugs, and IAllBranches to find "the right" branches; we
do not have yet anything similar for specs, so I iimpkemented the
SQL query for the permission check directly in the class SharingService.

I am a bit concerned about the performance of the SQL query, but
it will for now only be used in a test: my main goal right now is to
make it possible to revoke access grants for specifications, and
I need the changes of getVisibleArtifacts() for a test of
SharingService.revokeAccessGrants().

test:

./bin/test -vvt lp.registry.services.tests.test_sharingservice

no lint

To post a comment you must log in.
Revision history for this message
Richard Harding (rharding) wrote :

Per IRC there's some code aroud #96 and on that reference Branches vs specfications that needs cleaning up.

I also wonder, if we know the query will need to be replaced by some flattened table, if we should have a bug and an XXX on the current query to make can find it easily and come back around to it during out polish work.

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/model/bug.py'
2--- lib/lp/bugs/model/bug.py 2012-09-07 20:25:51 +0000
3+++ lib/lp/bugs/model/bug.py 2012-09-18 16:43:35 +0000
4@@ -811,7 +811,7 @@
5 # there is at least one bugtask for which access can be checked.
6 if self.default_bugtask:
7 service = getUtility(IService, 'sharing')
8- bugs, ignored = service.getVisibleArtifacts(
9+ bugs, ignored, ignored = service.getVisibleArtifacts(
10 person, bugs=[self], ignore_permissions=True)
11 if not bugs:
12 service.ensureAccessGrants(
13
14=== modified file 'lib/lp/code/browser/branchsubscription.py'
15--- lib/lp/code/browser/branchsubscription.py 2012-09-05 21:51:06 +0000
16+++ lib/lp/code/browser/branchsubscription.py 2012-09-18 16:43:35 +0000
17@@ -294,7 +294,7 @@
18 url = canonical_url(self.branch)
19 # If the subscriber can no longer see the branch, redirect them away.
20 service = getUtility(IService, 'sharing')
21- ignored, branches = service.getVisibleArtifacts(
22+ ignored, branches, ignored = service.getVisibleArtifacts(
23 self.person, branches=[self.branch], ignore_permissions=True)
24 if not branches:
25 url = canonical_url(self.branch.target)
26
27=== modified file 'lib/lp/code/model/branch.py'
28--- lib/lp/code/model/branch.py 2012-09-18 04:13:42 +0000
29+++ lib/lp/code/model/branch.py 2012-09-18 16:43:35 +0000
30@@ -896,7 +896,7 @@
31 subscription.review_level = code_review_level
32 # Grant the subscriber access if they can't see the branch.
33 service = getUtility(IService, 'sharing')
34- ignored, branches = service.getVisibleArtifacts(
35+ ignored, branches, ignored = service.getVisibleArtifacts(
36 person, branches=[self], ignore_permissions=True)
37 if not branches:
38 service.ensureAccessGrants(
39
40=== modified file 'lib/lp/code/model/tests/test_branchsubscription.py'
41--- lib/lp/code/model/tests/test_branchsubscription.py 2012-07-18 10:44:24 +0000
42+++ lib/lp/code/model/tests/test_branchsubscription.py 2012-09-18 16:43:35 +0000
43@@ -133,7 +133,7 @@
44 None, CodeReviewNotificationLevel.NOEMAIL, owner)
45 # The stacked on branch should be visible.
46 service = getUtility(IService, 'sharing')
47- ignored, visible_branches = service.getVisibleArtifacts(
48+ ignored, visible_branches, ignored = service.getVisibleArtifacts(
49 grantee, branches=[private_stacked_on_branch])
50 self.assertContentEqual(
51 [private_stacked_on_branch], visible_branches)
52@@ -161,7 +161,7 @@
53 grantee, BranchSubscriptionNotificationLevel.NOEMAIL,
54 None, CodeReviewNotificationLevel.NOEMAIL, owner)
55 # The stacked on branch should not be visible.
56- ignored, visible_branches = service.getVisibleArtifacts(
57+ ignored, visible_branches, ignored = service.getVisibleArtifacts(
58 grantee, branches=[private_stacked_on_branch])
59 self.assertContentEqual([], visible_branches)
60 self.assertIn(
61
62=== modified file 'lib/lp/registry/browser/pillar.py'
63--- lib/lp/registry/browser/pillar.py 2012-09-14 01:03:41 +0000
64+++ lib/lp/registry/browser/pillar.py 2012-09-18 16:43:35 +0000
65@@ -421,7 +421,7 @@
66 def _loadSharedArtifacts(self):
67 # As a concrete can by linked via more than one policy, we use sets to
68 # filter out dupes.
69- self.bugtasks, self.branches = (
70+ self.bugtasks, self.branches, self.specifications = (
71 self.sharing_service.getSharedArtifacts(
72 self.pillar, self.person, self.user))
73 bug_ids = set([bugtask.bug.id for bugtask in self.bugtasks])
74
75=== modified file 'lib/lp/registry/interfaces/sharingservice.py'
76--- lib/lp/registry/interfaces/sharingservice.py 2012-09-16 12:49:11 +0000
77+++ lib/lp/registry/interfaces/sharingservice.py 2012-09-18 16:43:35 +0000
78@@ -28,6 +28,7 @@
79
80 from lp import _
81 from lp.app.interfaces.services import IService
82+from lp.blueprints.interfaces.specification import ISpecification
83 from lp.bugs.interfaces.bug import IBug
84 from lp.code.interfaces.branch import IBranch
85 from lp.registry.enums import (
86@@ -79,7 +80,7 @@
87
88 :param user: the user making the request. Only artifacts visible to the
89 user will be included in the result.
90- :return: a (bugtasks, branches) tuple
91+ :return: a (bugtasks, branches, specifications) tuple
92 """
93
94 @export_read_operation()
95@@ -116,6 +117,21 @@
96 :return: a collection of branches
97 """
98
99+ @export_read_operation()
100+ @call_with(user=REQUEST_USER)
101+ @operation_parameters(
102+ pillar=Reference(IPillar, title=_('Pillar'), required=True),
103+ person=Reference(IPerson, title=_('Person'), required=True))
104+ @operation_returns_collection_of(ISpecification)
105+ @operation_for_version('devel')
106+ def getSharedSpecifications(pillar, person, user):
107+ """Return the specifications shared between the pillar and person.
108+
109+ :param user: the user making the request. Only branches visible to the
110+ user will be included in the result.
111+ :return: a collection of specifications.
112+ """
113+
114 def getVisibleArtifacts(person, branches=None, bugs=None):
115 """Return the artifacts shared with person.
116
117
118=== modified file 'lib/lp/registry/services/sharingservice.py'
119--- lib/lp/registry/services/sharingservice.py 2012-09-14 13:25:18 +0000
120+++ lib/lp/registry/services/sharingservice.py 2012-09-18 16:43:35 +0000
121@@ -17,15 +17,18 @@
122 Count,
123 In,
124 Join,
125+ LeftJoin,
126 Or,
127 Select,
128 )
129+from storm.store import Store
130 from zope.component import getUtility
131 from zope.interface import implements
132 from zope.security.interfaces import Unauthorized
133 from zope.traversing.browser.absoluteurl import absoluteURL
134
135 from lp.app.browser.tales import ObjectImageDisplayAPI
136+from lp.blueprints.model.specification import Specification
137 from lp.bugs.interfaces.bugtask import IBugTaskSet
138 from lp.bugs.interfaces.bugtasksearch import BugTaskSearchParams
139 from lp.code.interfaces.branchcollection import IAllBranches
140@@ -51,13 +54,16 @@
141 )
142 from lp.registry.interfaces.sharingservice import ISharingService
143 from lp.registry.model.accesspolicy import (
144+ AccessArtifact,
145 AccessArtifactGrant,
146 AccessPolicy,
147 AccessPolicyArtifact,
148 AccessPolicyGrant,
149+ AccessPolicyGrantFlat,
150 )
151 from lp.registry.model.person import Person
152 from lp.registry.model.teammembership import TeamParticipation
153+from lp.services.database.bulk import load
154 from lp.services.database.lpstorm import IStore
155 from lp.services.database.stormexpr import ColumnSelect
156 from lp.services.searchbuilder import any
157@@ -115,17 +121,20 @@
158
159 @available_with_permission('launchpad.Driver', 'pillar')
160 def getSharedArtifacts(self, pillar, person, user, include_bugs=True,
161- include_branches=True):
162+ include_branches=True, include_specifications=True):
163 """See `ISharingService`."""
164 policies = getUtility(IAccessPolicySource).findByPillar([pillar])
165 flat_source = getUtility(IAccessPolicyGrantFlatSource)
166 bug_ids = set()
167 branch_ids = set()
168+ specification_ids = set()
169 for artifact in flat_source.findArtifactsByGrantee(person, policies):
170 if artifact.bug_id and include_bugs:
171 bug_ids.add(artifact.bug_id)
172 elif artifact.branch_id and include_branches:
173 branch_ids.add(artifact.branch_id)
174+ elif artifact.specification_id and include_specifications:
175+ specification_ids.add(artifact.specification_id)
176
177 # Load the bugs.
178 bugtasks = []
179@@ -140,25 +149,72 @@
180 wanted_branches = all_branches.visibleByUser(user).withIds(
181 *branch_ids)
182 branches = list(wanted_branches.getBranches())
183+ specifications = []
184+ if specification_ids:
185+ specifications = load(Specification, specification_ids)
186
187- return bugtasks, branches
188+ return bugtasks, branches, specifications
189
190 @available_with_permission('launchpad.Driver', 'pillar')
191 def getSharedBugs(self, pillar, person, user):
192 """See `ISharingService`."""
193- bugtasks, ignore = self.getSharedArtifacts(
194- pillar, person, user, include_branches=False)
195+ bugtasks, ignore, ignore = self.getSharedArtifacts(
196+ pillar, person, user, include_branches=False,
197+ include_specifications=False)
198 return bugtasks
199
200 @available_with_permission('launchpad.Driver', 'pillar')
201 def getSharedBranches(self, pillar, person, user):
202 """See `ISharingService`."""
203- ignore, branches = self.getSharedArtifacts(
204- pillar, person, user, include_bugs=False)
205+ ignore, branches, ignore = self.getSharedArtifacts(
206+ pillar, person, user, include_bugs=False,
207+ include_specifications=False)
208 return branches
209
210+ @available_with_permission('launchpad.Driver', 'pillar')
211+ def getSharedSpecifications(self, pillar, person, user):
212+ """See `ISharingService`."""
213+ ignore, ignore, specifications = self.getSharedArtifacts(
214+ pillar, person, user, include_bugs=False,
215+ include_branches=False)
216+ return specifications
217+
218+ def _getVisiblePrivateSpecificationIDs(self, person, specifications):
219+ store = Store.of(specifications[0])
220+ tables = (
221+ Specification,
222+ Join(
223+ AccessPolicy,
224+ And(
225+ Or(
226+ Specification.distributionID ==
227+ AccessPolicy.distribution_id,
228+ Specification.productID ==
229+ AccessPolicy.product_id),
230+ AccessPolicy.type == Specification.information_type)),
231+ Join(
232+ AccessPolicyGrantFlat,
233+ AccessPolicy.id == AccessPolicyGrantFlat.policy_id
234+ ),
235+ LeftJoin(
236+ AccessArtifact,
237+ AccessArtifact.id ==
238+ AccessPolicyGrantFlat.abstract_artifact_id),
239+ Join(
240+ TeamParticipation,
241+ TeamParticipation.teamID ==
242+ AccessPolicyGrantFlat.grantee_id))
243+ spec_ids = [spec.id for spec in specifications]
244+ return set(store.using(*tables).find(
245+ Specification.id,
246+ Or(
247+ AccessPolicyGrantFlat.abstract_artifact_id == None,
248+ AccessArtifact.specification == Specification.id),
249+ TeamParticipation.personID == person.id,
250+ In(Specification.id, spec_ids)))
251+
252 def getVisibleArtifacts(self, person, branches=None, bugs=None,
253- ignore_permissions=False):
254+ specifications=None, ignore_permissions=False):
255 """See `ISharingService`."""
256 bugs_by_id = {}
257 branches_by_id = {}
258@@ -172,6 +228,10 @@
259 and not check_permission('launchpad.View', branch)):
260 raise Unauthorized
261 branches_by_id[branch.id] = branch
262+ for spec in specifications or []:
263+ if (not ignore_permissions
264+ and not check_permission('launchpad.View', spec)):
265+ raise Unauthorized
266
267 # Load the bugs.
268 visible_bug_ids = []
269@@ -189,7 +249,15 @@
270 *branches_by_id.keys())
271 visible_branches = list(wanted_branches.getBranches())
272
273- return visible_bugs, visible_branches
274+ visible_specs = []
275+ if specifications:
276+ visible_private_spec_ids = self._getVisiblePrivateSpecificationIDs(
277+ person, specifications)
278+ visible_specs = [
279+ spec for spec in specifications
280+ if spec.id in visible_private_spec_ids or not spec.private]
281+
282+ return visible_bugs, visible_branches, visible_specs
283
284 def getInvisibleArtifacts(self, person, branches=None, bugs=None):
285 """See `ISharingService`."""
286
287=== modified file 'lib/lp/registry/services/tests/test_sharingservice.py'
288--- lib/lp/registry/services/tests/test_sharingservice.py 2012-09-16 12:49:11 +0000
289+++ lib/lp/registry/services/tests/test_sharingservice.py 2012-09-18 16:43:35 +0000
290@@ -980,8 +980,8 @@
291
292 # Check that grantees have expected access grants and subscriptions.
293 for person in [team_grantee, person_grantee]:
294- visible_bugs, visible_branches = self.service.getVisibleArtifacts(
295- person, branches, bugs)
296+ visible_bugs, visible_branches, visible_specs = (
297+ self.service.getVisibleArtifacts(person, branches, bugs))
298 self.assertContentEqual(bugs or [], visible_bugs)
299 self.assertContentEqual(branches or [], visible_branches)
300 for person in [team_grantee, person_grantee]:
301@@ -1004,8 +1004,8 @@
302 for person in [team_grantee, person_grantee]:
303 for bug in bugs or []:
304 self.assertNotIn(person, bug.getDirectSubscribers())
305- visible_bugs, visible_branches = self.service.getVisibleArtifacts(
306- person, branches, bugs)
307+ visible_bugs, visible_branches, visible_specs = (
308+ self.service.getVisibleArtifacts(person, branches, bugs))
309 self.assertContentEqual([], visible_bugs)
310 self.assertContentEqual([], visible_branches)
311
312@@ -1120,8 +1120,8 @@
313 owner = self.factory.makePerson()
314 product = self.factory.makeProduct(
315 owner=owner,
316- specification_sharing_policy=
317- SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY)
318+ specification_sharing_policy=(
319+ SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
320 login_person(owner)
321 specification = self.factory.makeSpecification(
322 product=product, owner=owner)
323@@ -1232,6 +1232,12 @@
324 product=product, owner=product.owner,
325 information_type=InformationType.USERDATA)
326 branches.append(branch)
327+ specs = []
328+ for x in range(0, 10):
329+ spec = self.factory.makeSpecification(
330+ product=product, owner=product.owner,
331+ information_type=InformationType.PROPRIETARY)
332+ specs.append(spec)
333
334 # Grant access to grantee as well as the person who will be doing the
335 # query. The person who will be doing the query is not granted access
336@@ -1251,32 +1257,39 @@
337 grant_access(bug, i == 9)
338 for i, branch in enumerate(branches):
339 grant_access(branch, i == 9)
340- return bug_tasks, branches
341+ getUtility(IService, 'sharing').ensureAccessGrants(
342+ [grantee], product.owner, specifications=specs[:9])
343+ return bug_tasks, branches, specs
344
345 def test_getSharedArtifacts(self):
346 # Test the getSharedArtifacts method.
347 owner = self.factory.makePerson()
348- product = self.factory.makeProduct(owner=owner)
349+ product = self.factory.makeProduct(
350+ owner=owner, specification_sharing_policy=(
351+ SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
352 login_person(owner)
353 grantee = self.factory.makePerson()
354 user = self.factory.makePerson()
355- bug_tasks, branches = self.create_shared_artifacts(
356+ bug_tasks, branches, specs = self.create_shared_artifacts(
357 product, grantee, user)
358
359 # Check the results.
360- shared_bugtasks, shared_branches = self.service.getSharedArtifacts(
361- product, grantee, user)
362+ shared_bugtasks, shared_branches, shared_specs = (
363+ self.service.getSharedArtifacts(product, grantee, user))
364 self.assertContentEqual(bug_tasks[:9], shared_bugtasks)
365 self.assertContentEqual(branches[:9], shared_branches)
366+ self.assertContentEqual(specs[:9], shared_specs)
367
368 def test_getSharedBugs(self):
369 # Test the getSharedBugs method.
370 owner = self.factory.makePerson()
371- product = self.factory.makeProduct(owner=owner)
372+ product = self.factory.makeProduct(
373+ owner=owner, specification_sharing_policy=(
374+ SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
375 login_person(owner)
376 grantee = self.factory.makePerson()
377 user = self.factory.makePerson()
378- bug_tasks, ignored = self.create_shared_artifacts(
379+ bug_tasks, ignored, ignored = self.create_shared_artifacts(
380 product, grantee, user)
381
382 # Check the results.
383@@ -1286,11 +1299,13 @@
384 def test_getSharedBranches(self):
385 # Test the getSharedBranches method.
386 owner = self.factory.makePerson()
387- product = self.factory.makeProduct(owner=owner)
388+ product = self.factory.makeProduct(
389+ owner=owner, specification_sharing_policy=(
390+ SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
391 login_person(owner)
392 grantee = self.factory.makePerson()
393 user = self.factory.makePerson()
394- ignored, branches = self.create_shared_artifacts(
395+ ignored, branches, ignored = self.create_shared_artifacts(
396 product, grantee, user)
397
398 # Check the results.
399@@ -1298,6 +1313,23 @@
400 product, grantee, user)
401 self.assertContentEqual(branches[:9], shared_branches)
402
403+ def test_getSharedSpecifications(self):
404+ # Test the getSharedSpecifications method.
405+ owner = self.factory.makePerson()
406+ product = self.factory.makeProduct(
407+ owner=owner, specification_sharing_policy=(
408+ SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
409+ login_person(owner)
410+ grantee = self.factory.makePerson()
411+ user = self.factory.makePerson()
412+ ignored, ignored, specifications = self.create_shared_artifacts(
413+ product, grantee, user)
414+
415+ # Check the results.
416+ shared_specifications = self.service.getSharedSpecifications(
417+ product, grantee, user)
418+ self.assertContentEqual(specifications[:9], shared_specifications)
419+
420 def test_getPeopleWithAccessBugs(self):
421 # Test the getPeopleWithoutAccess method with bugs.
422 owner = self.factory.makePerson()
423@@ -1354,7 +1386,10 @@
424 def _make_Artifacts(self):
425 # Make artifacts for test (in)visible artifact methods.
426 owner = self.factory.makePerson()
427- product = self.factory.makeProduct(owner=owner)
428+ product = self.factory.makeProduct(
429+ owner=owner,
430+ specification_sharing_policy=(
431+ SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
432 grantee = self.factory.makePerson()
433 login_person(owner)
434
435@@ -1371,6 +1406,13 @@
436 information_type=InformationType.USERDATA)
437 branches.append(branch)
438
439+ specifications = []
440+ for x in range(0, 10):
441+ spec = self.factory.makeSpecification(
442+ product=product, owner=owner,
443+ information_type=InformationType.PROPRIETARY)
444+ specifications.append(spec)
445+
446 def grant_access(artifact):
447 access_artifact = self.factory.makeAccessArtifact(
448 concrete=artifact)
449@@ -1383,20 +1425,33 @@
450 grant_access(bug)
451 for branch in branches[:5]:
452 grant_access(branch)
453- return grantee, branches, bugs
454+ for spec in specifications[:5]:
455+ grant_access(spec)
456+ return grantee, owner, branches, bugs, specifications
457
458 def test_getVisibleArtifacts(self):
459 # Test the getVisibleArtifacts method.
460- grantee, branches, bugs = self._make_Artifacts()
461+ grantee, ignore, branches, bugs, specs = self._make_Artifacts()
462 # Check the results.
463- shared_bugs, shared_branches = self.service.getVisibleArtifacts(
464- grantee, branches, bugs)
465+ shared_bugs, shared_branches, shared_specs = (
466+ self.service.getVisibleArtifacts(grantee, branches, bugs, specs))
467 self.assertContentEqual(bugs[:5], shared_bugs)
468 self.assertContentEqual(branches[:5], shared_branches)
469+ self.assertContentEqual(specs[:5], shared_specs)
470+
471+ def test_getVisibleArtifacts_grant_on_pillar(self):
472+ # getVisibleArtifacts() returns private specifications if
473+ # user has a policy grant for the pillar of the specification.
474+ ignore, owner, branches, bugs, specs = self._make_Artifacts()
475+ shared_bugs, shared_branches, shared_specs = (
476+ self.service.getVisibleArtifacts(owner, branches, bugs, specs))
477+ self.assertContentEqual(bugs, shared_bugs)
478+ self.assertContentEqual(branches, shared_branches)
479+ self.assertContentEqual(specs, shared_specs)
480
481 def test_getInvisibleArtifacts(self):
482 # Test the getInvisibleArtifacts method.
483- grantee, branches, bugs = self._make_Artifacts()
484+ grantee, ignore, branches, bugs, specs = self._make_Artifacts()
485 # Check the results.
486 not_shared_bugs, not_shared_branches = (
487 self.service.getInvisibleArtifacts(grantee, branches, bugs))
488@@ -1423,16 +1478,16 @@
489 information_type=InformationType.USERDATA)
490 bugs.append(bug)
491
492- shared_bugs, shared_branches = self.service.getVisibleArtifacts(
493- grantee, bugs=bugs)
494+ shared_bugs, shared_branches, shared_specs = (
495+ self.service.getVisibleArtifacts(grantee, bugs=bugs))
496 self.assertContentEqual(bugs, shared_bugs)
497
498 # Change some bugs.
499 for x in range(0, 5):
500 change_callback(bugs[x], owner)
501 # Check the results.
502- shared_bugs, shared_branches = self.service.getVisibleArtifacts(
503- grantee, bugs=bugs)
504+ shared_bugs, shared_branches, shared_specs = (
505+ self.service.getVisibleArtifacts(grantee, bugs=bugs))
506 self.assertContentEqual(bugs[5:], shared_bugs)
507
508 def test_getVisibleArtifacts_bug_policy_change(self):
509@@ -1519,7 +1574,9 @@
510 def setUp(self):
511 super(ApiTestMixin, self).setUp()
512 self.owner = self.factory.makePerson(name='thundercat')
513- self.pillar = self.factory.makeProduct(owner=self.owner)
514+ self.pillar = self.factory.makeProduct(
515+ owner=self.owner, specification_sharing_policy=(
516+ SpecificationSharingPolicy.PUBLIC_OR_PROPRIETARY))
517 self.grantee = self.factory.makePerson(name='grantee')
518 self.grantor = self.factory.makePerson()
519 self.grantee_uri = canonical_url(self.grantee, force_local_path=True)
520@@ -1530,11 +1587,16 @@
521 self.branch = self.factory.makeBranch(
522 owner=self.owner, product=self.pillar,
523 information_type=InformationType.PRIVATESECURITY)
524+ self.spec = self.factory.makeSpecification(
525+ product=self.pillar, owner=self.owner,
526+ information_type=InformationType.PROPRIETARY)
527 login_person(self.owner)
528 self.bug.subscribe(self.grantee, self.owner)
529 self.branch.subscribe(
530 self.grantee, BranchSubscriptionNotificationLevel.NOEMAIL,
531 None, CodeReviewNotificationLevel.NOEMAIL, self.owner)
532+ getUtility(IService, 'sharing').ensureAccessGrants(
533+ [self.grantee], self.grantor, specifications=[self.spec])
534 transaction.commit()
535
536 def test_getPillarGranteeData(self):
537@@ -1546,7 +1608,8 @@
538 self.assertEqual(
539 {InformationType.USERDATA.name: SharingPermission.ALL.name,
540 InformationType.PRIVATESECURITY.name:
541- SharingPermission.SOME.name},
542+ SharingPermission.SOME.name,
543+ InformationType.PROPRIETARY.name: SharingPermission.SOME.name},
544 grantee_data['permissions'])
545
546
547@@ -1628,7 +1691,7 @@
548 self.assertEqual(bugtasks[0].title, self.bug.default_bugtask.title)
549
550 def test_getSharedBranches(self):
551- # Test the exported getSharedArtifacts() method.
552+ # Test the exported getSharedBranches() method.
553 ws_pillar = ws_object(self.launchpad, self.pillar)
554 ws_grantee = ws_object(self.launchpad, self.grantee)
555 branches = self.service.getSharedBranches(
556@@ -1636,13 +1699,23 @@
557 self.assertEqual(1, len(branches))
558 self.assertEqual(branches[0].unique_name, self.branch.unique_name)
559
560+ def test_getSharedSpecifications(self):
561+ # Test the exported getSharedSpecifications() method.
562+ ws_pillar = ws_object(self.launchpad, self.pillar)
563+ ws_grantee = ws_object(self.launchpad, self.grantee)
564+ specifications = self.service.getSharedSpecifications(
565+ pillar=ws_pillar, person=ws_grantee)
566+ self.assertEqual(1, len(specifications))
567+ self.assertEqual(specifications[0].name, self.spec.name)
568+
569 def test_getSharedArtifacts(self):
570 # Test the exported getSharedArtifacts() method.
571 ws_pillar = ws_object(self.launchpad, self.pillar)
572 ws_grantee = ws_object(self.launchpad, self.grantee)
573- (bugtasks, branches) = self.service.getSharedArtifacts(
574+ (bugtasks, branches, specs) = self.service.getSharedArtifacts(
575 pillar=ws_pillar, person=ws_grantee)
576 self.assertEqual(1, len(bugtasks))
577 self.assertEqual(1, len(branches))
578+ self.assertEqual(1, len(specs))
579 self.assertEqual(bugtasks[0]['title'], self.bug.default_bugtask.title)
580 self.assertEqual(branches[0]['unique_name'], self.branch.unique_name)