summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBarry Warsaw2016-06-28 06:36:02 -0400
committerBarry Warsaw2016-06-28 06:36:02 -0400
commit0265a93b24f2061d765d8f3eb0fc27c9fd080510 (patch)
tree996cf5051ddd0144cea90336bbde6ef7c6ef700a /src
parentc71417b39bc9832b71f766e525a08addda6166cd (diff)
downloadmailman-0265a93b24f2061d765d8f3eb0fc27c9fd080510.tar.gz
mailman-0265a93b24f2061d765d8f3eb0fc27c9fd080510.tar.zst
mailman-0265a93b24f2061d765d8f3eb0fc27c9fd080510.zip
Diffstat (limited to 'src')
-rw-r--r--src/mailman/core/api.py2
-rw-r--r--src/mailman/interfaces/api.py6
-rw-r--r--src/mailman/rest/members.py4
-rw-r--r--src/mailman/rest/root.py4
-rw-r--r--src/mailman/rest/users.py24
-rw-r--r--src/mailman/rest/wsgiapp.py11
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