summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/mailman/rest/docs/membership.rst22
-rw-r--r--src/mailman/rest/members.py2
-rw-r--r--src/mailman/rest/tests/test_validator.py13
-rw-r--r--src/mailman/rest/validator.py5
-rw-r--r--src/mailman/rest/wsgiapp.py2
5 files changed, 41 insertions, 3 deletions
diff --git a/src/mailman/rest/docs/membership.rst b/src/mailman/rest/docs/membership.rst
index df49621f0..b0b5e1254 100644
--- a/src/mailman/rest/docs/membership.rst
+++ b/src/mailman/rest/docs/membership.rst
@@ -962,6 +962,28 @@ the attribute to the member's resource.
moderation_action: hold
...
+It can be reset to the list default by patching an empty value.
+::
+
+ >>> dump_json('http://localhost:9001/3.0/members/10', {
+ ... 'moderation_action': '',
+ ... }, method='PATCH')
+ content-length: 0
+ date: ...
+ server: ...
+ status: 204
+
+ >>> dump_json('http://localhost:9001/3.0/members/10')
+ address: http://localhost:9001/3.0/addresses/hperson@example.com
+ delivery_mode: regular
+ email: hperson@example.com
+ http_etag: "..."
+ list_id: ant.example.com
+ member_id: 10
+ role: member
+ self_link: http://localhost:9001/3.0/members/10
+ user: http://localhost:9001/3.0/users/7
+
Handling the list of banned addresses
=====================================
diff --git a/src/mailman/rest/members.py b/src/mailman/rest/members.py
index 9ed12d98b..197e2232b 100644
--- a/src/mailman/rest/members.py
+++ b/src/mailman/rest/members.py
@@ -167,7 +167,7 @@ class AMember(_MemberBase):
values = Validator(
address=str,
delivery_mode=enum_validator(DeliveryMode),
- moderation_action=enum_validator(Action),
+ moderation_action=enum_validator(Action, allow_none=True),
_optional=('address', 'delivery_mode', 'moderation_action'),
)(request)
except ValueError as error:
diff --git a/src/mailman/rest/tests/test_validator.py b/src/mailman/rest/tests/test_validator.py
index 5da4349d2..ff62a4239 100644
--- a/src/mailman/rest/tests/test_validator.py
+++ b/src/mailman/rest/tests/test_validator.py
@@ -20,9 +20,10 @@
import unittest
from mailman.core.api import API30, API31
+from mailman.interfaces.action import Action
from mailman.interfaces.usermanager import IUserManager
from mailman.rest.validator import (
- list_of_strings_validator, subscriber_validator)
+ list_of_strings_validator, subscriber_validator, enum_validator)
from mailman.testing.layers import RESTLayer
from zope.component import getUtility
@@ -80,3 +81,13 @@ class TestValidators(unittest.TestCase):
def test_subscriber_validator_email_address_API31(self):
self.assertEqual(subscriber_validator(API31)('anne@example.com'),
'anne@example.com')
+
+ def test_enum_validator_valid(self):
+ self.assertEqual(enum_validator(Action)('hold'), Action.hold)
+
+ def test_enum_validator_invalid(self):
+ self.assertRaises(ValueError,
+ enum_validator(Action), 'not-a-thing')
+
+ def test_enum_validator_none(self):
+ self.assertEqual(enum_validator(Action, allow_none=True)(''), None)
diff --git a/src/mailman/rest/validator.py b/src/mailman/rest/validator.py
index 861b869de..0a6184850 100644
--- a/src/mailman/rest/validator.py
+++ b/src/mailman/rest/validator.py
@@ -52,12 +52,15 @@ class ReadOnlyPATCHRequestError(RESTError):
class enum_validator:
"""Convert an enum value name into an enum value."""
- def __init__(self, enum_class):
+ def __init__(self, enum_class, allow_none=False):
self._enum_class = enum_class
+ self._allow_none = allow_none
def __call__(self, enum_value):
# This will raise a KeyError if the enum value is unknown. The
# Validator API requires turning this into a ValueError.
+ if self._allow_none and not enum_value:
+ return None
try:
return self._enum_class[enum_value]
except KeyError as exception:
diff --git a/src/mailman/rest/wsgiapp.py b/src/mailman/rest/wsgiapp.py
index 0aedff839..bfe17af73 100644
--- a/src/mailman/rest/wsgiapp.py
+++ b/src/mailman/rest/wsgiapp.py
@@ -200,6 +200,8 @@ class RootedAPI(API):
# Let Falcon parse the form data into the request object's
# .params attribute.
self.req_options.auto_parse_form_urlencoded = True
+ # Don't ignore empty query parameters.
+ self.req_options.keep_blank_qs_values = True
# Override the base class implementation to wrap a transactional
# handler around the call, so that the current transaction is