From fdc9519917a7977e73225e3e5a66e2f0538e592c Mon Sep 17 00:00:00 2001 From: =?utf8?q?Aur=C3=A9lien=20Bompard?= Date: Fri, 10 Jun 2016 19:55:25 +0200 Subject: [PATCH] REST: allow setting a member's moderation_action to None --- src/mailman/rest/docs/membership.rst | 22 ++++++++++++++++++++++ src/mailman/rest/members.py | 2 +- src/mailman/rest/tests/test_validator.py | 13 ++++++++++++- src/mailman/rest/validator.py | 5 ++++- src/mailman/rest/wsgiapp.py | 2 ++ 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 -- 2.11.4.GIT