diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/docs/NEWS.rst | 2 | ||||
| -rw-r--r-- | src/mailman/rest/docs/addresses.rst (renamed from src/mailman/rest/docs/addresses.txt) | 5 | ||||
| -rw-r--r-- | src/mailman/rest/docs/membership.rst | 97 | ||||
| -rw-r--r-- | src/mailman/rest/members.py | 26 | ||||
| -rw-r--r-- | src/mailman/rest/tests/test_membership.py | 12 |
5 files changed, 130 insertions, 12 deletions
diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst index d2b6a8ccb..38dc35ab4 100644 --- a/src/mailman/docs/NEWS.rst +++ b/src/mailman/docs/NEWS.rst @@ -39,6 +39,8 @@ REST * New REST resource http://.../members/find can be POSTed to in order to find member records. Optional arguments are `subscriber` (email address to search for), `fqdn_listname`, and `role` (i.e. MemberRole). (LP: #799612) + * You can now query or change a member's `delivery_mode` attribute through + the REST API (LP: #833132). Given by Stephen A. Goss. * New REST resource http://.../<domain>/lists can be GETed in order to find all the mailing lists in a specific domain (LP: #829765). Given by Stephen A. Goss. diff --git a/src/mailman/rest/docs/addresses.txt b/src/mailman/rest/docs/addresses.rst index d82396e6b..71ab1f0f3 100644 --- a/src/mailman/rest/docs/addresses.txt +++ b/src/mailman/rest/docs/addresses.rst @@ -169,6 +169,7 @@ Elle can get her memberships for each of her email addresses. ... 'elle@example.com/memberships') entry 0: address: elle@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: "..." role: member @@ -176,6 +177,7 @@ Elle can get her memberships for each of her email addresses. user: http://localhost:9001/3.0/users/2 entry 1: address: elle@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: "..." role: member @@ -204,6 +206,7 @@ does not show up in the list of memberships for his other address. ... 'elle@example.com/memberships') entry 0: address: elle@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: "..." role: member @@ -211,6 +214,7 @@ does not show up in the list of memberships for his other address. user: http://localhost:9001/3.0/users/2 entry 1: address: elle@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: "..." role: member @@ -224,6 +228,7 @@ does not show up in the list of memberships for his other address. ... 'eperson@example.com/memberships') entry 0: address: eperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: "..." role: member diff --git a/src/mailman/rest/docs/membership.rst b/src/mailman/rest/docs/membership.rst index c09cd10a1..e339291aa 100644 --- a/src/mailman/rest/docs/membership.rst +++ b/src/mailman/rest/docs/membership.rst @@ -42,6 +42,7 @@ the REST interface. >>> dump_json('http://localhost:9001/3.0/members') entry 0: address: bperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -55,6 +56,7 @@ Bart's specific membership can be accessed directly: >>> dump_json('http://localhost:9001/3.0/members/1') address: bperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -68,6 +70,7 @@ the REST interface. >>> dump_json('http://localhost:9001/3.0/members') entry 0: address: bperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -75,6 +78,7 @@ the REST interface. user: http://localhost:9001/3.0/users/1 entry 1: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -93,6 +97,7 @@ subscribes, she is returned first. >>> dump_json('http://localhost:9001/3.0/members') entry 0: address: aperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -100,6 +105,7 @@ subscribes, she is returned first. user: http://localhost:9001/3.0/users/3 entry 1: address: bperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -107,6 +113,7 @@ subscribes, she is returned first. user: http://localhost:9001/3.0/users/1 entry 2: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -129,6 +136,7 @@ User ids are different than member ids. >>> dump_json('http://localhost:9001/3.0/members') entry 0: address: aperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: member @@ -136,6 +144,7 @@ User ids are different than member ids. user: http://localhost:9001/3.0/users/3 entry 1: address: cperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: member @@ -143,6 +152,7 @@ User ids are different than member ids. user: http://localhost:9001/3.0/users/2 entry 2: address: aperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -150,6 +160,7 @@ User ids are different than member ids. user: http://localhost:9001/3.0/users/3 entry 3: address: bperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -157,6 +168,7 @@ User ids are different than member ids. user: http://localhost:9001/3.0/users/1 entry 4: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -172,6 +184,7 @@ We can also get just the members of a single mailing list. ... 'http://localhost:9001/3.0/lists/ant@example.com/roster/member') entry 0: address: aperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: member @@ -179,6 +192,7 @@ We can also get just the members of a single mailing list. user: http://localhost:9001/3.0/users/3 entry 1: address: cperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: member @@ -222,6 +236,7 @@ mailing list. >>> dump_json('http://localhost:9001/3.0/members') entry 0: address: dperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: moderator @@ -229,6 +244,7 @@ mailing list. user: http://localhost:9001/3.0/users/4 entry 1: address: aperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: member @@ -236,6 +252,7 @@ mailing list. user: http://localhost:9001/3.0/users/3 entry 2: address: cperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: member @@ -243,6 +260,7 @@ mailing list. user: http://localhost:9001/3.0/users/2 entry 3: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: owner @@ -250,6 +268,7 @@ mailing list. user: http://localhost:9001/3.0/users/2 entry 4: address: aperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -257,6 +276,7 @@ mailing list. user: http://localhost:9001/3.0/users/3 entry 5: address: bperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -264,6 +284,7 @@ mailing list. user: http://localhost:9001/3.0/users/1 entry 6: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -279,6 +300,7 @@ We can access all the owners of a list. ... 'http://localhost:9001/3.0/lists/bee@example.com/roster/owner') entry 0: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: owner @@ -297,6 +319,7 @@ A specific member can always be referenced by their role and address. >>> dump_json('http://localhost:9001/3.0/lists/' ... 'bee@example.com/owner/cperson@example.com') address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: owner @@ -311,6 +334,7 @@ example, we can search for all the memberships of a particular address. ... }) entry 0: address: aperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: member @@ -318,6 +342,7 @@ example, we can search for all the memberships of a particular address. user: http://localhost:9001/3.0/users/3 entry 1: address: aperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -334,6 +359,7 @@ Or, we can find all the memberships for a particular mailing list. ... }) entry 0: address: aperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -341,6 +367,7 @@ Or, we can find all the memberships for a particular mailing list. user: http://localhost:9001/3.0/users/3 entry 1: address: bperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -348,6 +375,7 @@ Or, we can find all the memberships for a particular mailing list. user: http://localhost:9001/3.0/users/1 entry 2: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -355,12 +383,13 @@ Or, we can find all the memberships for a particular mailing list. user: http://localhost:9001/3.0/users/2 entry 3: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: owner self_link: http://localhost:9001/3.0/members/7 user: http://localhost:9001/3.0/users/2 - http_etag: "66836d0f23bed36fa9e0cda1e5dec7e5b0797743" + http_etag: "..." start: 0 total_size: 4 @@ -373,6 +402,7 @@ list. ... }) entry 0: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -380,6 +410,7 @@ list. user: http://localhost:9001/3.0/users/2 entry 1: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: owner @@ -397,6 +428,7 @@ Or, we can find all the memberships for an address with a specific role. ... }) entry 0: address: cperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: member @@ -404,6 +436,7 @@ Or, we can find all the memberships for an address with a specific role. user: http://localhost:9001/3.0/users/2 entry 1: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -422,6 +455,7 @@ Finally, we can search for a specific member given all three criteria. ... }) entry 0: address: cperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: ... role: member @@ -469,6 +503,7 @@ Elly is now a known user, and a member of the mailing list. ... entry 3: address: eperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: ... role: member @@ -509,6 +544,7 @@ list with her preferred address. ... entry 4: address: gwen@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: "..." role: member @@ -531,6 +567,7 @@ the new address. ... entry 4: address: gwen.person@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: "..." role: member @@ -584,9 +621,63 @@ Fred joins the `ant` mailing list but wants MIME digest delivery. >>> memberships = list(fred.memberships.members) >>> len(memberships) 1 + +Fred is getting MIME deliveries. + >>> memberships[0] <Member: Fred Person <fperson@example.com> on ant@example.com as MemberRole.member> + >>> print memberships[0].delivery_mode + DeliveryMode.mime_digests + + >>> dump_json('http://localhost:9001/3.0/members/10') + address: fperson@example.com + delivery_mode: mime_digests + fqdn_listname: ant@example.com + http_etag: "..." + role: member + self_link: http://localhost:9001/3.0/members/10 + user: http://localhost:9001/3.0/users/7 + +Fred wants to change his delivery from MIME digest back to regular delivery. +This can be done by PATCH'ing his member with the `delivery_mode` parameter. +:: + + >>> transaction.abort() + >>> dump_json('http://localhost:9001/3.0/members/10', { + ... 'delivery_mode': 'regular', + ... }, method='PATCH') + content-length: 0 + date: ... + server: ... + status: 204 + + >>> dump_json('http://localhost:9001/3.0/members/10') + address: fperson@example.com + delivery_mode: regular + fqdn_listname: ant@example.com + http_etag: "..." + role: member + self_link: http://localhost:9001/3.0/members/10 + user: http://localhost:9001/3.0/users/7 + +If a PATCH request changes no attributes, nothing happens. +:: + + >>> dump_json('http://localhost:9001/3.0/members/10', method='PATCH') + content-length: 0 + date: ... + server: ... + status: 204 + + >>> dump_json('http://localhost:9001/3.0/members/10') + address: fperson@example.com + delivery_mode: regular + fqdn_listname: ant@example.com + http_etag: "..." + role: member + self_link: http://localhost:9001/3.0/members/10 + user: http://localhost:9001/3.0/users/7 Changing delivery address @@ -623,6 +714,7 @@ addresses. ... entry 5: address: herb@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: "..." role: member @@ -631,6 +723,7 @@ addresses. ... entry 10: address: herb@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: "..." role: member @@ -686,6 +779,7 @@ his membership ids have not changed. ... 'hperson@example.com/memberships') entry 0: address: hperson@example.com + delivery_mode: regular fqdn_listname: ant@example.com http_etag: "..." role: member @@ -693,6 +787,7 @@ his membership ids have not changed. user: http://localhost:9001/3.0/users/8 entry 1: address: hperson@example.com + delivery_mode: regular fqdn_listname: bee@example.com http_etag: "..." role: member diff --git a/src/mailman/rest/members.py b/src/mailman/rest/members.py index 8f447ac75..f15a2204a 100644 --- a/src/mailman/rest/members.py +++ b/src/mailman/rest/members.py @@ -65,6 +65,7 @@ class _MemberBase(resource.Resource, CollectionMixin): role=role, user=path_to('users/{0}'.format(user_id)), self_link=path_to('members/{0}'.format(member_id)), + delivery_mode=member.delivery_mode, ) def _get_collection(self, request): @@ -141,18 +142,23 @@ class AMember(_MemberBase): if self._member is None: return http.not_found() try: - values = Validator(address=unicode)(request) + values = Validator( + address=unicode, + delivery_mode=enum_validator(DeliveryMode), + _optional=('address', 'delivery_mode'))(request) except ValueError as error: return http.bad_request([], str(error)) - assert len(values) == 1, 'Unexpected values' - email = values['address'] - address = getUtility(IUserManager).get_address(email) - if address is None: - return http.bad_request([], b'Address not registered') - try: - self._member.address = address - except (MembershipError, UnverifiedAddressError) as error: - return http.bad_request([], str(error)) + if 'address' in values: + email = values['address'] + address = getUtility(IUserManager).get_address(email) + if address is None: + return http.bad_request([], b'Address not registered') + try: + self._member.address = address + except (MembershipError, UnverifiedAddressError) as error: + return http.bad_request([], str(error)) + if 'delivery_mode' in values: + self._member.preferences.delivery_mode = values['delivery_mode'] return no_content() diff --git a/src/mailman/rest/tests/test_membership.py b/src/mailman/rest/tests/test_membership.py index 8a315e359..2d2351fca 100644 --- a/src/mailman/rest/tests/test_membership.py +++ b/src/mailman/rest/tests/test_membership.py @@ -203,7 +203,17 @@ class TestMembership(unittest.TestCase): else: raise AssertionError('Expected HTTPError') - def test_patch_bogus_member_attribute_400(self): + def test_patch_nonexistent_member(self): + # /members/<missing> PATCH returns 404 + try: + # For Python 2.6 + call_api('http://localhost:9001/3.0/members/801', method='PATCH') + except HTTPError as exc: + self.assertEqual(exc.code, 404) + else: + raise AssertionError('Expected HTTPError') + + def test_patch_member_bogus_attribute(self): # /members/<id> PATCH 'bogus' returns 400 anne = self._usermanager.create_address('anne@example.com') self._mlist.subscribe(anne) |
