Merge lp:~postfuturist/mailman/rest-api-mods_owners into lp:mailman

Proposed by Stephen A. Goss
Status: Merged
Approved by: Barry Warsaw
Approved revision: 7043
Merged at revision: 7043
Proposed branch: lp:~postfuturist/mailman/rest-api-mods_owners
Merge into: lp:mailman
Diff against target: 201 lines (+50/-21)
6 files modified
src/mailman/app/membership.py (+4/-6)
src/mailman/app/subscriptions.py (+7/-10)
src/mailman/app/tests/test_membership.py (+13/-1)
src/mailman/interfaces/subscriptions.py (+3/-1)
src/mailman/rest/docs/membership.rst (+21/-2)
src/mailman/rest/members.py (+2/-1)
To merge this branch: bzr merge lp:~postfuturist/mailman/rest-api-mods_owners
Reviewer Review Type Date Requested Status
Barry Warsaw Approve
Review via email: mp+72977@code.launchpad.net

Description of the change

Added ability to add moderators and owners through existing REST API endpoints. Tests were updated as well.

To post a comment you must log in.
Revision history for this message
Stephen A. Goss (postfuturist) wrote :

The AlreadySubscribedError I removed is superfluous, as that error will get raised regardless in the call to `subscribe` if that is the case.

Revision history for this message
Barry Warsaw (barry) wrote :

Thanks for the code! It looks pretty good in general. I'm going to make a few stylistic fixes (indentation, parameter order, multiline import formatting), and I'll add a few more "bad path" tests to ensure the exception still gets raised when expected. Then I'll merge and commit.

Cheers!

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'src/mailman/app/membership.py'
--- src/mailman/app/membership.py 2011-05-26 01:30:56 +0000
+++ src/mailman/app/membership.py 2011-08-25 23:41:30 +0000
@@ -43,7 +43,8 @@
4343
4444
4545
4646
47def add_member(mlist, email, realname, password, delivery_mode, language):47def add_member(mlist, email, realname, password, delivery_mode, language,
48 role=MemberRole.member):
48 """Add a member right now.49 """Add a member right now.
4950
50 The member's subscription must be approved by whatever policy the list51 The member's subscription must be approved by whatever policy the list
@@ -70,9 +71,6 @@
70 """71 """
71 # Let's be extra cautious.72 # Let's be extra cautious.
72 getUtility(IEmailValidator).validate(email)73 getUtility(IEmailValidator).validate(email)
73 if mlist.members.get_member(email) is not None:
74 raise AlreadySubscribedError(
75 mlist.fqdn_listname, email, MemberRole.member)
76 # Check to see if the email address is banned.74 # Check to see if the email address is banned.
77 if getUtility(IBanManager).is_banned(email, mlist.fqdn_listname):75 if getUtility(IBanManager).is_banned(email, mlist.fqdn_listname):
78 raise MembershipIsBannedError(mlist, email)76 raise MembershipIsBannedError(mlist, email)
@@ -99,7 +97,7 @@
99 # scheme is recorded in the hashed password string.97 # scheme is recorded in the hashed password string.
100 user.password = encrypt_password(password)98 user.password = encrypt_password(password)
101 user.preferences.preferred_language = language99 user.preferences.preferred_language = language
102 member = mlist.subscribe(address, MemberRole.member)100 member = mlist.subscribe(address, role)
103 member.preferences.delivery_mode = delivery_mode101 member.preferences.delivery_mode = delivery_mode
104 else:102 else:
105 # The user exists and is linked to the address.103 # The user exists and is linked to the address.
@@ -110,7 +108,7 @@
110 raise AssertionError(108 raise AssertionError(
111 'User should have had linked address: {0}'.format(address))109 'User should have had linked address: {0}'.format(address))
112 # Create the member and set the appropriate preferences.110 # Create the member and set the appropriate preferences.
113 member = mlist.subscribe(address, MemberRole.member)111 member = mlist.subscribe(address, role)
114 member.preferences.preferred_language = language112 member.preferences.preferred_language = language
115 member.preferences.delivery_mode = delivery_mode113 member.preferences.delivery_mode = delivery_mode
116 return member114 return member
117115
=== modified file 'src/mailman/app/subscriptions.py'
--- src/mailman/app/subscriptions.py 2011-08-17 23:22:45 +0000
+++ src/mailman/app/subscriptions.py 2011-08-25 23:41:30 +0000
@@ -37,7 +37,7 @@
37from mailman.interfaces.address import InvalidEmailAddressError37from mailman.interfaces.address import InvalidEmailAddressError
38from mailman.interfaces.listmanager import (38from mailman.interfaces.listmanager import (
39 IListManager, ListDeletedEvent, NoSuchListError)39 IListManager, ListDeletedEvent, NoSuchListError)
40from mailman.interfaces.member import DeliveryMode40from mailman.interfaces.member import DeliveryMode, MemberRole
41from mailman.interfaces.subscriptions import (41from mailman.interfaces.subscriptions import (
42 ISubscriptionService, MissingUserError)42 ISubscriptionService, MissingUserError)
43from mailman.interfaces.usermanager import IUserManager43from mailman.interfaces.usermanager import IUserManager
@@ -138,16 +138,12 @@
138 for member in self.get_members():138 for member in self.get_members():
139 yield member139 yield member
140140
141 def join(self, fqdn_listname, subscriber,141 def join(self, fqdn_listname, subscriber, real_name= None,
142 real_name= None, delivery_mode=None):142 delivery_mode=DeliveryMode.regular, role=MemberRole.member):
143 """See `ISubscriptionService`."""143 """See `ISubscriptionService`."""
144 mlist = getUtility(IListManager).get(fqdn_listname)144 mlist = getUtility(IListManager).get(fqdn_listname)
145 if mlist is None:145 if mlist is None:
146 raise NoSuchListError(fqdn_listname)146 raise NoSuchListError(fqdn_listname)
147 # Convert from string to enum.
148 mode = (DeliveryMode.regular
149 if delivery_mode is None
150 else delivery_mode)
151 # Is the subscriber a user or email address?147 # Is the subscriber a user or email address?
152 if '@' in subscriber:148 if '@' in subscriber:
153 # It's an email address, so we'll want a real name.149 # It's an email address, so we'll want a real name.
@@ -163,14 +159,15 @@
163 # it can't be retrieved. Note that none of these are used unless159 # it can't be retrieved. Note that none of these are used unless
164 # the address is completely new to us.160 # the address is completely new to us.
165 password = make_user_friendly_password()161 password = make_user_friendly_password()
166 return add_member(mlist, subscriber, real_name, password, mode,162 return add_member(mlist, subscriber, real_name, password,
167 system_preferences.preferred_language)163 delivery_mode,
164 system_preferences.preferred_language, role)
168 else:165 else:
169 # We have to assume it's a user id.166 # We have to assume it's a user id.
170 user = getUtility(IUserManager).get_user_by_id(subscriber)167 user = getUtility(IUserManager).get_user_by_id(subscriber)
171 if user is None:168 if user is None:
172 raise MissingUserError(subscriber)169 raise MissingUserError(subscriber)
173 return mlist.subscribe(user)170 return mlist.subscribe(user, role)
174171
175 def leave(self, fqdn_listname, address):172 def leave(self, fqdn_listname, address):
176 """See `ISubscriptionService`."""173 """See `ISubscriptionService`."""
177174
=== modified file 'src/mailman/app/tests/test_membership.py'
--- src/mailman/app/tests/test_membership.py 2011-04-09 00:09:13 +0000
+++ src/mailman/app/tests/test_membership.py 2011-08-25 23:41:30 +0000
@@ -34,7 +34,8 @@
34from mailman.config import config34from mailman.config import config
35from mailman.core.constants import system_preferences35from mailman.core.constants import system_preferences
36from mailman.interfaces.bans import IBanManager36from mailman.interfaces.bans import IBanManager
37from mailman.interfaces.member import DeliveryMode, MembershipIsBannedError37from mailman.interfaces.member import (DeliveryMode, MembershipIsBannedError,
38 MemberRole)
38from mailman.interfaces.usermanager import IUserManager39from mailman.interfaces.usermanager import IUserManager
39from mailman.testing.helpers import reset_the_world40from mailman.testing.helpers import reset_the_world
40from mailman.testing.layers import ConfigLayer41from mailman.testing.layers import ConfigLayer
@@ -58,6 +59,7 @@
58 system_preferences.preferred_language)59 system_preferences.preferred_language)
59 self.assertEqual(member.address.email, 'aperson@example.com')60 self.assertEqual(member.address.email, 'aperson@example.com')
60 self.assertEqual(member.mailing_list, 'test@example.com')61 self.assertEqual(member.mailing_list, 'test@example.com')
62 self.assertEqual(member.role, MemberRole.member)
6163
62 def test_add_member_existing_user(self):64 def test_add_member_existing_user(self):
63 # Test subscribing a user to a mailing list when the email address has65 # Test subscribing a user to a mailing list when the email address has
@@ -124,6 +126,16 @@
124 system_preferences.preferred_language)126 system_preferences.preferred_language)
125 self.assertEqual(member.address.email, 'anne@example.com')127 self.assertEqual(member.address.email, 'anne@example.com')
126128
129 def test_add_member_moderator(self):
130 # Test adding a moderator to a mailing list
131 member = add_member(self._mlist, 'aperson@example.com',
132 'Anne Person', '123', DeliveryMode.regular,
133 system_preferences.preferred_language,
134 MemberRole.moderator)
135 self.assertEqual(member.address.email, 'aperson@example.com')
136 self.assertEqual(member.mailing_list, 'test@example.com')
137 self.assertEqual(member.role, MemberRole.moderator)
138
127139
128140
129141
130class AddMemberPasswordTest(unittest.TestCase):142class AddMemberPasswordTest(unittest.TestCase):
131143
=== modified file 'src/mailman/interfaces/subscriptions.py'
--- src/mailman/interfaces/subscriptions.py 2011-08-17 23:10:39 +0000
+++ src/mailman/interfaces/subscriptions.py 2011-08-25 23:41:30 +0000
@@ -28,6 +28,7 @@
28from zope.interface import Interface28from zope.interface import Interface
2929
30from mailman.interfaces.errors import MailmanError30from mailman.interfaces.errors import MailmanError
31from mailman.interfaces.member import DeliveryMode, MemberRole
3132
3233
3334
3435
@@ -91,7 +92,8 @@
91 def __iter__():92 def __iter__():
92 """See `get_members()`."""93 """See `get_members()`."""
9394
94 def join(fqdn_listname, subscriber, real_name=None, delivery_mode=None):95 def join(fqdn_listname, subscriber, real_name=None,
96 delivery_mode=DeliveryMode.regular, role=MemberRole.member):
95 """Subscribe to a mailing list.97 """Subscribe to a mailing list.
9698
97 A user for the address is created if it is not yet known to Mailman,99 A user for the address is created if it is not yet known to Mailman,
98100
=== modified file 'src/mailman/rest/docs/membership.rst'
--- src/mailman/rest/docs/membership.rst 2011-08-17 23:10:39 +0000
+++ src/mailman/rest/docs/membership.rst 2011-08-25 23:41:30 +0000
@@ -197,8 +197,27 @@
197mailing list.197mailing list.
198::198::
199199
200 >>> subscribe(ant, 'Dave', MemberRole.moderator)200 >>> dump_json('http://localhost:9001/3.0/members', {
201 >>> subscribe(bee, 'Cris', MemberRole.owner)201 ... 'fqdn_listname': 'ant@example.com',
202 ... 'subscriber': 'dperson@example.com',
203 ... 'role': 'moderator',
204 ... })
205 content-length: 0
206 date: ...
207 location: http://localhost:9001/3.0/members/6
208 server: ...
209 status: 201
210
211 >>> dump_json('http://localhost:9001/3.0/members', {
212 ... 'fqdn_listname': 'bee@example.com',
213 ... 'subscriber': 'cperson@example.com',
214 ... 'role': 'owner',
215 ... })
216 content-length: 0
217 date: ...
218 location: http://localhost:9001/3.0/members/7
219 server: ...
220 status: 201
202221
203 >>> dump_json('http://localhost:9001/3.0/members')222 >>> dump_json('http://localhost:9001/3.0/members')
204 entry 0:223 entry 0:
205224
=== modified file 'src/mailman/rest/members.py'
--- src/mailman/rest/members.py 2011-08-17 23:10:39 +0000
+++ src/mailman/rest/members.py 2011-08-25 23:41:30 +0000
@@ -151,7 +151,8 @@
151 subscriber=unicode,151 subscriber=unicode,
152 real_name=unicode,152 real_name=unicode,
153 delivery_mode=enum_validator(DeliveryMode),153 delivery_mode=enum_validator(DeliveryMode),
154 _optional=('delivery_mode', 'real_name'))154 role=enum_validator(MemberRole),
155 _optional=('delivery_mode', 'real_name', 'role'))
155 member = service.join(**validator(request))156 member = service.join(**validator(request))
156 except AlreadySubscribedError:157 except AlreadySubscribedError:
157 return http.conflict([], b'Member already subscribed')158 return http.conflict([], b'Member already subscribed')