summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mailman/interfaces/rest.py10
-rw-r--r--src/mailman/rest/adapters.py17
-rw-r--r--src/mailman/rest/configure.zcml20
-rw-r--r--src/mailman/rest/docs/membership.txt61
4 files changed, 97 insertions, 11 deletions
diff --git a/src/mailman/interfaces/rest.py b/src/mailman/interfaces/rest.py
index 83a458c1d..4fbb0f5ef 100644
--- a/src/mailman/interfaces/rest.py
+++ b/src/mailman/interfaces/rest.py
@@ -21,12 +21,22 @@ from __future__ import absolute_import, unicode_literals
__metaclass__ = type
__all__ = [
+ 'APIValueError',
'IResolvePathNames',
]
+from lazr.restful.declarations import error_status
from zope.interface import Interface
+from mailman.core.errors import MailmanError
+
+
+
+@error_status(400)
+class APIValueError(MailmanError, ValueError):
+ """A `ValueError` from the REST API."""
+
class IResolvePathNames(Interface):
diff --git a/src/mailman/rest/adapters.py b/src/mailman/rest/adapters.py
index ab0279d6e..afa371c5d 100644
--- a/src/mailman/rest/adapters.py
+++ b/src/mailman/rest/adapters.py
@@ -38,7 +38,7 @@ from mailman.interfaces.domain import IDomainCollection, IDomainManager
from mailman.interfaces.listmanager import IListManager, NoSuchListError
from mailman.interfaces.member import DeliveryMode, NotAMemberError
from mailman.interfaces.membership import ISubscriptionService
-from mailman.interfaces.rest import IResolvePathNames
+from mailman.interfaces.rest import APIValueError, IResolvePathNames
@@ -102,12 +102,15 @@ class SubscriptionService:
mlist = getUtility(IListManager).get(fqdn_listname)
if mlist is None:
raise NoSuchListError(fqdn_listname)
- # Convert from string to enum. Allow ValueError exceptions to be
- # returned by the API. XXX 2009-12-30 Does lazr.restful intelligently
- # handle ValueError?
- mode = (DeliveryMode.regular
- if delivery_mode is None
- else DeliveryMode(delivery_mode))
+ # Convert from string to enum. Turn Python's ValueErrors into one
+ # suitable for the REST API.
+ try:
+ mode = (DeliveryMode.regular
+ if delivery_mode is None
+ else DeliveryMode(delivery_mode))
+ except ValueError:
+ raise APIValueError(
+ 'Invalid delivery_mode: {0}'.format(delivery_mode))
if real_name is None:
real_name, at, domain = address.partition('@')
if len(at) == 0:
diff --git a/src/mailman/rest/configure.zcml b/src/mailman/rest/configure.zcml
index cf08b331e..b12d42f46 100644
--- a/src/mailman/rest/configure.zcml
+++ b/src/mailman/rest/configure.zcml
@@ -11,6 +11,7 @@
<webservice:register module="mailman.interfaces.domain" />
<webservice:register module="mailman.interfaces.listmanager" />
<webservice:register module="mailman.interfaces.membership" />
+ <webservice:register module="mailman.interfaces.rest" />
<webservice:register module="mailman.interfaces.system" />
<adapter factory="zope.publisher.http.HTTPCharsets" />
@@ -52,11 +53,12 @@
/>
<!--
- XXX 2009-12-28 Why is this necessary? NotAMemberError is decorated with
- @error_status(400) so it /should/ already be adaptable to
- WebServiceExceptionView. For some reason though rest/membership.txt fails
- without this.
+ XXX 2009-12-28 Why are these necessary? NotAMemberError and
+ AlreadySubscribedError are decorated with @error_status(400) so they
+ /should/ already be adaptable to WebServiceExceptionView. For some reason
+ though rest/membership.txt fails without these.
-->
+
<adapter
for="mailman.interfaces.member.NotAMemberError
lazr.restful.simple.Request"
@@ -65,6 +67,16 @@
name="index.html"
/>
+ <adapter
+ for="mailman.interfaces.member.AlreadySubscribedError
+ lazr.restful.simple.Request"
+ provides="zope.interface.Interface"
+ factory="lazr.restful.error.WebServiceExceptionView"
+ name="index.html"
+ />
+
+ <!-- Utilities -->
+
<utility
factory="mailman.rest.configuration.AdminWebServiceConfiguration"
provides="lazr.restful.interfaces.IWebServiceConfiguration"
diff --git a/src/mailman/rest/docs/membership.txt b/src/mailman/rest/docs/membership.txt
index 4628e6af2..f14bb0203 100644
--- a/src/mailman/rest/docs/membership.txt
+++ b/src/mailman/rest/docs/membership.txt
@@ -231,6 +231,32 @@ Elly is no longer a member of the mailing list.
set([])
+Digest delivery
+===============
+
+Fred joins the alpha mailing list but wants MIME digest delivery.
+
+ >>> transaction.abort()
+ >>> dump_json('http://localhost:8001/3.0/members', {
+ ... 'ws.op': 'join',
+ ... 'fqdn_listname': 'alpha@example.com',
+ ... 'address': 'fperson@example.com',
+ ... 'real_name': 'Fred Person',
+ ... 'delivery_mode': 'mime_digests',
+ ... })
+ http_etag: ...
+ resource_type_link: http://localhost:8001/3.0/#member
+ self_link: http://localhost:8001/3.0/lists/alpha@example.com/member/fperson@example.com
+
+ >>> fred = user_manager.get_user('fperson@example.com')
+ >>> memberships = list(fred.memberships.members)
+ >>> len(memberships)
+ 1
+ >>> memberships[0]
+ <Member: Fred Person <fperson@example.com>
+ on alpha@example.com as MemberRole.member>
+
+
Corner cases
============
@@ -258,6 +284,17 @@ Then, she tries to leave a mailing list that does not exist.
...
HTTPError: HTTP Error 400: Bad Request
+She then tries to leave a mailing list with a bogus address.
+
+ >>> dump_json('http://localhost:8001/3.0/members', {
+ ... 'ws.op': 'leave',
+ ... 'fqdn_listname': 'alpha@example.com',
+ ... 'address': 'elly',
+ ... })
+ Traceback (most recent call last):
+ ...
+ HTTPError: HTTP Error 400: Bad Request
+
For some reason, Elly tries to leave the mailing list again, but she's already
been unsubscribed.
@@ -269,3 +306,27 @@ been unsubscribed.
Traceback (most recent call last):
...
HTTPError: HTTP Error 400: Bad Request
+
+Anna tries to join a mailing list she's already a member of.
+
+ >>> dump_json('http://localhost:8001/3.0/members', {
+ ... 'ws.op': 'join',
+ ... 'fqdn_listname': 'alpha@example.com',
+ ... 'address': 'aperson@example.com',
+ ... })
+ Traceback (most recent call last):
+ ...
+ HTTPError: HTTP Error 400: Bad Request
+
+Gwen tries to join the alpha mailing list using an invalid delivery mode.
+
+ >>> dump_json('http://localhost:8001/3.0/members', {
+ ... 'ws.op': 'join',
+ ... 'fqdn_listname': 'alpha@example.com',
+ ... 'address': 'gperson@example.com',
+ ... 'real_name': 'Gwen Person',
+ ... 'delivery_mode': 'in_digests',
+ ... })
+ Traceback (most recent call last):
+ ...
+ HTTPError: HTTP Error 400: Bad Request