Merge lp:~lifeless/launchpad/features into lp:launchpad

Proposed by Robert Collins
Status: Merged
Approved by: Robert Collins
Approved revision: no longer in the source branch.
Merged at revision: 11863
Proposed branch: lp:~lifeless/launchpad/features
Merge into: lp:launchpad
Diff against target: 147 lines (+65/-4)
5 files modified
lib/lp/registry/interfaces/person.py (+5/-2)
lib/lp/registry/model/person.py (+3/-0)
lib/lp/registry/tests/test_person.py (+7/-0)
lib/lp/services/features/tests/test_webapp.py (+25/-2)
lib/lp/services/features/webapp.py (+25/-0)
To merge this branch: bzr merge lp:~lifeless/launchpad/features
Reviewer Review Type Date Requested Status
Jeroen T. Vermeulen (community) code Approve
Review via email: mp+40049@code.launchpad.net

Commit message

Add a team: scope.

Description of the change

Not much to say. Team based scope. \o/. Drive by fix in inTeam to handle nonexistant teams more gracefully. (We could raise, but the existing handling for e.g. teams that are Persons is to return False, so I followed that style, for now).

To post a comment you must log in.
Revision history for this message
Jeroen T. Vermeulen (jtv) wrote :

As discussed on IRC. Note that existing behaviour is for teams that are Persons is not actually to return False as opposed to raising an exception; it is to return False if the "team" is not the same person as self (which is the only circumstance under which there could be a membership).

Since you made a change in the interface where a mistake that would previously raise an error will now be quietly accepted, you're adding a test and documentation for the change.

review: Approve (code)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'lib/lp/registry/interfaces/person.py'
--- lib/lp/registry/interfaces/person.py 2010-11-04 00:18:34 +0000
+++ lib/lp/registry/interfaces/person.py 2010-11-04 07:27:52 +0000
@@ -1074,8 +1074,11 @@
1074 since it inherits from `IPerson`) is a member of himself1074 since it inherits from `IPerson`) is a member of himself
1075 (i.e. `person1.inTeam(person1)`).1075 (i.e. `person1.inTeam(person1)`).
10761076
1077 :param team: An object providing `IPerson`, the name of a1077 :param team: One of an object providing `IPerson`, the string name of a
1078 team, or `None` (in which case `False` is returned).1078 team or `None`. If a string was supplied the team is looked up.
1079 :return: A bool with the result of the membership lookup. When looking
1080 up the team from a string finds nothing or team was `None` then
1081 `False` is returned.
1079 """1082 """
10801083
1081 def clearInTeamCache():1084 def clearInTeamCache():
10821085
=== modified file 'lib/lp/registry/model/person.py'
--- lib/lp/registry/model/person.py 2010-11-03 20:25:27 +0000
+++ lib/lp/registry/model/person.py 2010-11-04 07:27:52 +0000
@@ -1191,6 +1191,9 @@
1191 # Translate the team name to an ITeam if we were passed a team.1191 # Translate the team name to an ITeam if we were passed a team.
1192 if isinstance(team, (str, unicode)):1192 if isinstance(team, (str, unicode)):
1193 team = PersonSet().getByName(team)1193 team = PersonSet().getByName(team)
1194 if team is None:
1195 # No team, no membership.
1196 return False
11941197
1195 if self.id == team.id:1198 if self.id == team.id:
1196 # A team is always a member of itself.1199 # A team is always a member of itself.
11971200
=== modified file 'lib/lp/registry/tests/test_person.py'
--- lib/lp/registry/tests/test_person.py 2010-11-03 13:58:34 +0000
+++ lib/lp/registry/tests/test_person.py 2010-11-04 07:27:52 +0000
@@ -220,6 +220,13 @@
220 {},220 {},
221 removeSecurityProxy(self.user)._inTeam_cache)221 removeSecurityProxy(self.user)._inTeam_cache)
222222
223 def test_inTeam_person_string_missing_team(self):
224 # If a check against a string is done, the team lookup is implicit:
225 # treat a missing team as an empty team so that any pages that choose
226 # to do this don't blow up unnecessarily. Similarly feature flags
227 # team: scopes depend on this.
228 self.assertFalse(self.user.inTeam('does-not-exist'))
229
223230
224class TestPerson(TestCaseWithFactory):231class TestPerson(TestCaseWithFactory):
225232
226233
=== modified file 'lib/lp/services/features/tests/test_webapp.py'
--- lib/lp/services/features/tests/test_webapp.py 2010-09-13 00:55:15 +0000
+++ lib/lp/services/features/tests/test_webapp.py 2010-11-04 07:27:52 +0000
@@ -7,7 +7,11 @@
77
8from canonical.testing import layers8from canonical.testing import layers
9from lp.services.features import webapp9from lp.services.features import webapp
10from lp.testing import TestCase10from lp.testing import (
11 login_as,
12 TestCase,
13 TestCaseWithFactory,
14 )
11from canonical.launchpad.webapp.servers import LaunchpadTestRequest15from canonical.launchpad.webapp.servers import LaunchpadTestRequest
1216
1317
@@ -38,4 +42,23 @@
38 scopes = webapp.ScopesFromRequest(request)42 scopes = webapp.ScopesFromRequest(request)
39 self.assertTrue(scopes.lookup('pageid:'))43 self.assertTrue(scopes.lookup('pageid:'))
40 self.assertFalse(scopes.lookup('pageid:foo'))44 self.assertFalse(scopes.lookup('pageid:foo'))
41 self.assertFalse(scopes.lookup('pageid:foo'))45 self.assertFalse(scopes.lookup('pageid:foo:bar'))
46
47
48class TestDBScopes(TestCaseWithFactory):
49
50 layer = layers.DatabaseFunctionalLayer
51
52 def test_team_scope_outside_team(self):
53 request = LaunchpadTestRequest()
54 scopes = webapp.ScopesFromRequest(request)
55 self.factory.loginAsAnyone()
56 self.assertFalse(scopes.lookup('team:nonexistent'))
57
58 def test_team_scope_in_team(self):
59 request = LaunchpadTestRequest()
60 scopes = webapp.ScopesFromRequest(request)
61 member = self.factory.makePerson()
62 team = self.factory.makeTeam(members=[member])
63 login_as(member)
64 self.assertTrue(scopes.lookup('team:%s' % team.name))
4265
=== modified file 'lib/lp/services/features/webapp.py'
--- lib/lp/services/features/webapp.py 2010-10-25 17:29:07 +0000
+++ lib/lp/services/features/webapp.py 2010-11-04 07:27:52 +0000
@@ -7,7 +7,10 @@
77
8__metaclass__ = type8__metaclass__ = type
99
10from zope.component import getUtility
11
10import canonical.config12import canonical.config
13from canonical.launchpad.webapp.interfaces import ILaunchBag
11from lp.services.features import per_thread14from lp.services.features import per_thread
12from lp.services.features.flags import FeatureController15from lp.services.features.flags import FeatureController
13from lp.services.features.rulesource import StormFeatureRuleSource16from lp.services.features.rulesource import StormFeatureRuleSource
@@ -34,11 +37,16 @@
34 - pageid:SomeType37 - pageid:SomeType
35 - pageid:SomeType:+view38 - pageid:SomeType:+view
36 - pageid:SomeType:+view#subselector39 - pageid:SomeType:+view#subselector
40 - team:
41 This scope looks up a team. For instance
42 - team:launchpad-beta-users
37 """43 """
38 if scope_name == 'default':44 if scope_name == 'default':
39 return True45 return True
40 if scope_name.startswith('pageid:'):46 if scope_name.startswith('pageid:'):
41 return self._lookup_pageid(scope_name[len('pageid:'):])47 return self._lookup_pageid(scope_name[len('pageid:'):])
48 if scope_name.startswith('team:'):
49 return self._lookup_team(scope_name[len('team:'):])
42 parts = scope_name.split('.')50 parts = scope_name.split('.')
43 if len(parts) == 2:51 if len(parts) == 2:
44 if parts[0] == 'server':52 if parts[0] == 'server':
@@ -65,6 +73,23 @@
65 return False73 return False
66 return True74 return True
6775
76 def _lookup_team(self, team_name):
77 """Lookup a team membership as a scope.
78
79 This will do a two queries, so we probably want to keep the number of
80 team based scopes in use to a small number. (Person.inTeam could be
81 fixed to reduce this to one query).
82
83 teamid scopes are written as 'team:' + the team name to match.
84
85 E.g. the scope 'team:launchpad-beta-users' will match members of
86 the team 'launchpad-beta-users'.
87 """
88 person = getUtility(ILaunchBag).user
89 if person is None:
90 return False
91 return person.inTeam(team_name)
92
68 def _pageid_to_namespace(self, pageid):93 def _pageid_to_namespace(self, pageid):
69 """Return a list of namespace elements for pageid."""94 """Return a list of namespace elements for pageid."""
70 # Normalise delimiters.95 # Normalise delimiters.