diff options
| -rw-r--r-- | src/mailman/interfaces/domain.py | 4 | ||||
| -rw-r--r-- | src/mailman/interfaces/listmanager.py | 42 | ||||
| -rw-r--r-- | src/mailman/model/listmanager.py | 15 | ||||
| -rw-r--r-- | src/mailman/rest/docs/lists.txt | 5 | ||||
| -rw-r--r-- | src/mailman/rest/webservice.py | 102 |
5 files changed, 138 insertions, 30 deletions
diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py index a8770b664..b7fc1c91f 100644 --- a/src/mailman/interfaces/domain.py +++ b/src/mailman/interfaces/domain.py @@ -37,6 +37,10 @@ from mailman.core.i18n import _ class BadDomainSpecificationError(MailmanError): """The specification of a virtual domain is invalid or duplicated.""" + def __init__(self, domain): + super(BadDomainSpecificationError, self).__init__(domain) + self.domain = domain + class IDomain(Interface): diff --git a/src/mailman/interfaces/listmanager.py b/src/mailman/interfaces/listmanager.py index f47fe658e..f83791079 100644 --- a/src/mailman/interfaces/listmanager.py +++ b/src/mailman/interfaces/listmanager.py @@ -73,6 +73,25 @@ class IListManager(Interface): :raise `ListAlreadyExistsError` if the named list already exists. """ + + def new(fqdn_listname): + """Add a new mailing list. + + The mailing may not exist yet, but the domain specified in + `fqdn_listname` must exist. This is a higher level interface than + create() and should generally be used instead of that method. + + :param fqdn_listname: The fully qualified name for the new + mailing list. + :type fqdn_listname: string + :return: The new mailing list + :rtype: `IMailingList` + :raises `BadDomainSpecificationError`: when the hostname part of + `fqdn_listname` does not exist. + :raises `ListAlreadyExistsError`: when the mailing list already + exists. + """ + def get(fqdn_listname): """Return the mailing list with the given name, if it exists. @@ -93,6 +112,12 @@ class IListManager(Interface): """An iterator over all the mailing list objects managed by this list manager.""") + def __iter__(): + """An iterator over all the mailing lists. + + :return: iterator over `IMailingList`. + """ + names = Attribute( """An iterator over the fully qualified list names of all mailing lists managed by this list manager.""") @@ -103,20 +128,3 @@ class IListManager(Interface): :return: The list of all known mailing lists. :rtype: list of `IMailingList` """ - - def new(fqdn_listname): - """Add a new maling list. - - The mailing may not exist yet, but the domain specified in - `fqdn_listname` must exist. - - :param fqdn_listname: The fully qualified name for the new - mailing list. - :type fqdn_listname: string - :return: The new mailing list - :rtype: `IMailingList` - :raises `BadDomainSpecificationError`: when the hostname part of - `fqdn_listname` does not exist. - :raises `ListAlreadyExistsError`: when the mailing list already - exists. - """ diff --git a/src/mailman/model/listmanager.py b/src/mailman/model/listmanager.py index b8bfa85ee..1bb2366b5 100644 --- a/src/mailman/model/listmanager.py +++ b/src/mailman/model/listmanager.py @@ -59,6 +59,11 @@ class ListManager: config.db.store.add(mlist) return mlist + def new(self, fqdn_listname): + """See `IListManager.""" + from mailman.app.lifecycle import create_list + return create_list(fqdn_listname) + def get(self, fqdn_listname): """See `IListManager`.""" listname, at, hostname = fqdn_listname.partition('@') @@ -80,6 +85,11 @@ class ListManager: for fqdn_listname in self.names: yield self.get(fqdn_listname) + def __iter__(self): + """See `IListManager`.""" + for fqdn_listname in self.names: + yield self.get(fqdn_listname) + @property def names(self): """See `IListManager`.""" @@ -90,8 +100,3 @@ class ListManager: """See `IListManager`.""" # lazr.restful will not allow this to be a generator. return list(self.mailing_lists) - - def new(self, fqdn_listname): - """See `IListManager.""" - from mailman.app.lifecycle import create_list - return create_list(fqdn_listname) diff --git a/src/mailman/rest/docs/lists.txt b/src/mailman/rest/docs/lists.txt index 460ca3585..bb0824f7a 100644 --- a/src/mailman/rest/docs/lists.txt +++ b/src/mailman/rest/docs/lists.txt @@ -44,12 +44,9 @@ instead of posting directly to the URL. ... }) URL: http://localhost:8001/3.0/lists content-length: 0 - content-type: text/plain;charset=utf-8 date: ... location: http://localhost:8001/3.0/lists/test-two@example.com - server: WSGIServer/... Python/... - x-content-type-warning: guessed from content - x-powered-by: Zope (www.zope.org), Python (www.python.org) + ... The mailing list exists in the database. diff --git a/src/mailman/rest/webservice.py b/src/mailman/rest/webservice.py index 8a1e919bf..40e4a5ef6 100644 --- a/src/mailman/rest/webservice.py +++ b/src/mailman/rest/webservice.py @@ -43,7 +43,7 @@ from mailman.config import config from mailman.core.system import system from mailman.interfaces.domain import ( BadDomainSpecificationError, IDomain, IDomainManager) -from mailman.interfaces.listmanager import IListManager +from mailman.interfaces.listmanager import ListAlreadyExistsError, IListManager from mailman.interfaces.mailinglist import IMailingList from mailman.interfaces.member import IMember from mailman.interfaces.membership import ISubscriptionService @@ -88,6 +88,15 @@ class TopLevel(resource.Resource): else: return http.bad_request() + @resource.child() + def lists(self, request, segments): + if len(segments) == 0: + return AllLists() + elif len(segments) == 1: + return AList(segments[0]), [] + else: + return http.bad_request() + class _DomainBase(resource.Resource): """Shared base class for domain representations.""" @@ -143,11 +152,11 @@ class AllDomains(_DomainBase): domain = domain_manager.add(**kws) except BadDomainSpecificationError: return http.bad_request([], 'Domain exists') - # wsgiref wants headers to be strings, not unicodes. - location = 'http://localhost:8001/3.0/domains/{0}'.format( + # wsgiref wants headers to be bytes, not unicodes. + location = b'http://localhost:8001/3.0/domains/{0}'.format( domain.email_host) # Include no extra headers or body. - return http.created(str(location), [], None) + return http.created(location, [], None) @resource.GET() def container(self, request): @@ -172,6 +181,91 @@ class AllDomains(_DomainBase): return http.ok([], json.dumps(response)) +class _ListBase(resource.Resource): + """Shared base class for mailing list representations.""" + + def _format_list(self, mlist): + """Format the mailing list for a single domain.""" + list_data = dict( + fqdn_listname=mlist.fqdn_listname, + host_name=mlist.host_name, + list_name=mlist.list_name, + real_name=mlist.real_name, + resource_type_link='http://localhost:8001/3.0/#list', + self_link='http://localhost:8001/3.0/lists/{0}'.format( + mlist.fqdn_listname), + ) + etag = hashlib.sha1(repr(list_data)).hexdigest() + list_data['http_etag'] = '"{0}"'.format(etag) + return list_data + + +class AList(_ListBase): + """A mailing list.""" + + def __init__(self, mlist): + self._mlist = mlist + + @resource.GET() + def mailing_list(self, request): + """Return a single mailing list end-point.""" + mlist = getUtility(IListManager).get(self._mlist) + if mlist is None: + return http.not_found() + return http.ok([], json.dumps(self._format_list(mlist))) + + +class AllLists(_ListBase): + """The mailing lists.""" + + @resource.POST() + def create(self, request): + """Create a new mailing list.""" + # XXX 2010-02-23 barry Sanity check the POST arguments by + # introspection of the target method, or via descriptors. + list_manager = getUtility(IListManager) + try: + # Hmmm... webob gives this to us as a string, but we need + # unicodes. For backward compatibility with lazr.restful style + # requests, ignore any ws.op parameter. + kws = dict((key, unicode(value)) + for key, value in request.POST.items() + if key != 'ws.op') + mlist = list_manager.new(**kws) + except ListAlreadyExistsError: + return http.bad_request([], b'Mailing list exists') + except BadDomainSpecificationError as error: + return http.bad_request([], b'Domain does not exist {0}'.format( + error.domain)) + # wsgiref wants headers to be bytes, not unicodes. + location = b'http://localhost:8001/3.0/lists/{0}'.format( + mlist.fqdn_listname) + # Include no extra headers or body. + return http.created(location, [], None) + + @resource.GET() + def container(self, request): + """Return the /lists end-point.""" + mlists = list(getUtility(IListManager)) + if len(mlists) == 0: + return http.ok( + [], json.dumps(dict(resource_type_link= + 'http://localhost:8001/3.0/#lists', + start=None, + total_size=0))) + entries = [] + response = dict( + resource_type_link='http://localhost:8001/3.0/#lists', + start=0, + total_size=len(mlists), + entries=entries, + ) + for mlist in mlists: + list_data = self._format_list(mlist) + entries.append(list_data) + return http.ok([], json.dumps(response)) + + ## class AdminWebServiceRootResource(RootResource): ## """The lazr.restful non-versioned root resource.""" |
