diff options
| author | Barry Warsaw | 2016-06-28 06:36:02 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2016-06-28 06:36:02 -0400 |
| commit | 0265a93b24f2061d765d8f3eb0fc27c9fd080510 (patch) | |
| tree | 996cf5051ddd0144cea90336bbde6ef7c6ef700a | |
| parent | c71417b39bc9832b71f766e525a08addda6166cd (diff) | |
| download | mailman-0265a93b24f2061d765d8f3eb0fc27c9fd080510.tar.gz mailman-0265a93b24f2061d765d8f3eb0fc27c9fd080510.tar.zst mailman-0265a93b24f2061d765d8f3eb0fc27c9fd080510.zip | |
| -rw-r--r-- | src/mailman/core/api.py | 2 | ||||
| -rw-r--r-- | src/mailman/interfaces/api.py | 6 | ||||
| -rw-r--r-- | src/mailman/rest/members.py | 4 | ||||
| -rw-r--r-- | src/mailman/rest/root.py | 4 | ||||
| -rw-r--r-- | src/mailman/rest/users.py | 24 | ||||
| -rw-r--r-- | src/mailman/rest/wsgiapp.py | 11 |
6 files changed, 32 insertions, 19 deletions
diff --git a/src/mailman/core/api.py b/src/mailman/core/api.py index b7841a9d8..e6e013af0 100644 --- a/src/mailman/core/api.py +++ b/src/mailman/core/api.py @@ -29,6 +29,7 @@ from zope.interface import implementer @implementer(IAPI) class API30: version = '3.0' + version_info = (3, 0) @classmethod def path_to(cls, resource): @@ -58,6 +59,7 @@ class API30: @implementer(IAPI) class API31: version = '3.1' + version_info = (3, 1) @classmethod def path_to(cls, resource): diff --git a/src/mailman/interfaces/api.py b/src/mailman/interfaces/api.py index 8096f40eb..3ba158b35 100644 --- a/src/mailman/interfaces/api.py +++ b/src/mailman/interfaces/api.py @@ -25,7 +25,11 @@ from zope.interface import Attribute, Interface class IAPI(Interface): """The REST web service context.""" - version = Attribute("""The REST API version.""") + version = Attribute( + """The REST API version as a string.""") + + version_info = Attribute( + """The REST API version as a tuple of integers.""") def path_to(resource): """Return the full REST URL to the given resource. diff --git a/src/mailman/rest/members.py b/src/mailman/rest/members.py index b6158dd3c..9ed12d98b 100644 --- a/src/mailman/rest/members.py +++ b/src/mailman/rest/members.py @@ -123,7 +123,7 @@ class AMember(_MemberBase): return NotFound(), [] if self._member is None: return NotFound(), [] - member_id = context['api'].from_uuid(self._member_id) + member_id = self.api.from_uuid(self._member_id) child = Preferences( self._member.preferences, 'members/{}'.format(member_id)) return child, [] @@ -135,7 +135,7 @@ class AMember(_MemberBase): return NotFound(), [] if self._member is None: return NotFound(), [] - member_id = context['api'].from_uuid(self._member_id) + member_id = self.api.from_uuid(self._member_id) child = ReadOnlyPreferences( self._member, 'members/{}/all'.format(member_id)) return child, [] diff --git a/src/mailman/rest/root.py b/src/mailman/rest/root.py index 3c38ded5d..ff459bbcb 100644 --- a/src/mailman/rest/root.py +++ b/src/mailman/rest/root.py @@ -220,7 +220,7 @@ class TopLevel: resource = FindMembers() else: try: - member_id = context['api'].to_uuid(segment) + member_id = self.api.to_uuid(segment) except ValueError: member_id = None resource = AMember(member_id) @@ -233,7 +233,7 @@ class TopLevel: return AllUsers() else: user_identifier = segments.pop(0) - return AUser(context['api'], user_identifier), segments + return AUser(user_identifier), segments @child() def owners(self, context, segments): diff --git a/src/mailman/rest/users.py b/src/mailman/rest/users.py index dfef466ca..ede435ad8 100644 --- a/src/mailman/rest/users.py +++ b/src/mailman/rest/users.py @@ -17,6 +17,7 @@ """REST for users.""" +from functools import lru_cache from lazr.config import as_boolean from mailman import public from mailman.config import config @@ -168,11 +169,9 @@ class AllUsers(_UserBase): class AUser(_UserBase): """A user.""" - def __init__(self, api, user_identifier): + def __init__(self, user_identifier): """Get a user by various type of identifiers. - :param api: The REST API object. - :type api: IAPI :param user_identifier: The identifier used to retrieve the user. The identifier may either be an email address controlled by the user or the UUID of the user. The type of identifier is auto-detected @@ -181,19 +180,26 @@ class AUser(_UserBase): API 3.0 are integers, while in 3.1 are hex. :type user_identifier: string """ - self.api = api + self._user_identifier = user_identifier + # Defer calculation of the user until the API object is set, since + # that will determine how to interpret the user identifier. For ease + # of code migration, use an _user caching property (see below). + + @property + @lru_cache(1) + def _user(self): user_manager = getUtility(IUserManager) - if '@' in user_identifier: - self._user = user_manager.get_user(user_identifier) + if '@' in self._user_identifier: + return user_manager.get_user(self._user_identifier) else: # The identifier is the string representation of a UUID, either an # int in API 3.0 or a hex in API 3.1. try: - user_id = api.to_uuid(user_identifier) + user_id = self.api.to_uuid(self._user_identifier) except ValueError: - self._user = None + return None else: - self._user = user_manager.get_user_by_id(user_id) + return user_manager.get_user_by_id(user_id) def on_get(self, request, response): """Return a single user end-point.""" diff --git a/src/mailman/rest/wsgiapp.py b/src/mailman/rest/wsgiapp.py index 67f920205..0aedff839 100644 --- a/src/mailman/rest/wsgiapp.py +++ b/src/mailman/rest/wsgiapp.py @@ -83,11 +83,6 @@ class Middleware: performed. """ def process_resource(self, request, response, resource, params): - # Set this attribute on the resource right before it is dispatched to. - # This can be used by the resource to provide different responses - # based on the API version, and for path_to() to provide an API - # version-specific path. - resource.api = params.pop('api') # Check the authorization credentials. authorized = False if request.auth is not None and request.auth.startswith('Basic '): @@ -122,6 +117,8 @@ class ObjectRouter: resource = self._root context = {} while True: + # Plumb the API through to all child resources. + api = getattr(resource, 'api', None) # See if any of the resource's child links match the next segment. for name in dir(resource): if name.startswith('__') and name.endswith('__'): @@ -172,6 +169,10 @@ class ObjectRouter: resource, segments = result else: resource = result + # See if the context set an API and set it on the next + # resource in the chain, falling back to the parent resource's + # API if there is one. + resource.api = context.pop('api', api) # The method could have truncated the remaining segments, # meaning, it's consumed all the path segments, or this is the # last path segment. In that case the resource we're left at |
