diff options
Diffstat (limited to '')
| -rw-r--r-- | src/mailman/database/listmanager.py | 6 | ||||
| -rw-r--r-- | src/mailman/docs/listmanager.txt | 13 | ||||
| -rw-r--r-- | src/mailman/interfaces/domain.py | 8 | ||||
| -rw-r--r-- | src/mailman/interfaces/listmanager.py | 14 | ||||
| -rw-r--r-- | src/mailman/interfaces/mailinglist.py | 39 | ||||
| -rw-r--r-- | src/mailman/rest/adapters.py | 3 | ||||
| -rw-r--r-- | src/mailman/rest/configure.zcml | 3 | ||||
| -rw-r--r-- | src/mailman/rest/docs/lists.txt | 12 | ||||
| -rw-r--r-- | src/mailman/rest/publication.py | 2 | ||||
| -rw-r--r-- | src/mailman/rest/testing/__init__.py | 0 | ||||
| -rw-r--r-- | src/mailman/rest/testing/server.py | 63 | ||||
| -rw-r--r-- | src/mailman/rest/urls.py | 6 | ||||
| -rw-r--r-- | src/mailman/rest/webservice.py | 3 | ||||
| -rw-r--r-- | src/mailman/testing/layers.py | 24 |
14 files changed, 106 insertions, 90 deletions
diff --git a/src/mailman/database/listmanager.py b/src/mailman/database/listmanager.py index 790a2509a..887ec5e1f 100644 --- a/src/mailman/database/listmanager.py +++ b/src/mailman/database/listmanager.py @@ -40,6 +40,7 @@ class ListManager(object): implements(IListManager) + # pylint: disable-msg=R0201 def create(self, fqdn_listname): """See `IListManager`.""" listname, hostname = fqdn_listname.split('@', 1) @@ -80,3 +81,8 @@ class ListManager(object): """See `IListManager`.""" for mlist in config.db.store.find(MailingList): yield '{0}@{1}'.format(mlist.list_name, mlist.host_name) + + def get_mailing_lists(self): + """See `IListManager`.""" + # lazr.restful will not allow this to be a generator. + return list(self.mailing_lists) diff --git a/src/mailman/docs/listmanager.txt b/src/mailman/docs/listmanager.txt index 830f6d962..c082dc001 100644 --- a/src/mailman/docs/listmanager.txt +++ b/src/mailman/docs/listmanager.txt @@ -1,5 +1,6 @@ -Using the IListManager interface -================================ +======================== +The mailing list manager +======================== The IListManager is how you create, delete, and retrieve mailing list objects. The Mailman system instantiates an IListManager for you based on the @@ -13,7 +14,7 @@ on the global config object. Creating a mailing list ------------------------ +======================= Creating the list returns the newly created IMailList object. @@ -43,7 +44,7 @@ you will get an exception. Deleting a mailing list ------------------------ +======================= Use the list manager to delete a mailing list. @@ -59,7 +60,7 @@ After deleting the list, you can create it again. Retrieving a mailing list -------------------------- +========================= When a mailing list exists, you can ask the list manager for it and you will always get the same object back. @@ -75,7 +76,7 @@ If you try to get a list that doesn't existing yet, you get None. Iterating over all mailing lists --------------------------------- +================================ Once you've created a bunch of mailing lists, you can use the list manager to iterate over either the list objects, or the list names. diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py index 0692d21d5..1546f9487 100644 --- a/src/mailman/interfaces/domain.py +++ b/src/mailman/interfaces/domain.py @@ -97,5 +97,9 @@ class IDomainSet(Interface): export_as_webservice_collection(IDomain) @collection_default_content() - def __iter__(): - """Iterate over all domains.""" + def get_domains(): + """The list of all domains. + + :return: The list of all known domains. + :rtype: list of `IDomain` + """ diff --git a/src/mailman/interfaces/listmanager.py b/src/mailman/interfaces/listmanager.py index e7cdd9da7..c8f49f447 100644 --- a/src/mailman/interfaces/listmanager.py +++ b/src/mailman/interfaces/listmanager.py @@ -26,8 +26,12 @@ __all__ = [ ] +from lazr.restful.declarations import ( + collection_default_content, export_as_webservice_collection) from zope.interface import Interface, Attribute + from mailman.interfaces.errors import MailmanError +from mailman.interfaces.mailinglist import IMailingList @@ -49,6 +53,8 @@ class IListManager(Interface): `mylist@example.com`. """ + export_as_webservice_collection(IMailingList) + def create(fqdn_listname): """Create a mailing list with the given name. @@ -82,3 +88,11 @@ class IListManager(Interface): names = Attribute( """An iterator over the fully qualified list names of all mailing lists managed by this list manager.""") + + @collection_default_content() + def get_mailing_lists(): + """The list of all mailing lists. + + :return: The list of all known mailing lists. + :rtype: list of `IMailingList` + """ diff --git a/src/mailman/interfaces/mailinglist.py b/src/mailman/interfaces/mailinglist.py index e71a47d61..0717ab2e6 100644 --- a/src/mailman/interfaces/mailinglist.py +++ b/src/mailman/interfaces/mailinglist.py @@ -30,8 +30,13 @@ __all__ = [ ] +from lazr.restful.declarations import ( + export_as_webservice_entry, exported) from munepy import Enum from zope.interface import Interface, Attribute +from zope.schema import TextLine + +from mailman.i18n import _ @@ -66,31 +71,39 @@ class DigestFrequency(Enum): class IMailingList(Interface): """A mailing list.""" + export_as_webservice_entry() + # List identity - list_name = Attribute( - """The read-only short name of the mailing list. Note that where a + list_name = exported(TextLine( + title=_("The mailing list's short name"), + description=_("""\ + The read-only short name of the mailing list. Note that where a Mailman installation supports multiple domains, this short name may not be unique. Use the fqdn_listname attribute for a guaranteed unique id for the mailing list. This short name is always the local part of the posting email address. For example, if messages are posted to mylist@example.com, then the list_name is 'mylist'. - """) + """))) - host_name = Attribute( - """The read-only domain name 'hosting' this mailing list. This is - always the domain name part of the posting email address, and it may - bear no relationship to the web url used to access this mailing list. - For example, if messages are posted to mylist@example.com, then the + host_name = exported(TextLine( + title=_("The mailing list's host name"), + description=_("""\ + The read-only domain name 'hosting' this mailing list. This is always + the domain name part of the posting email address, and it may bear no + relationship to the web url used to access this mailing list. For + example, if messages are posted to mylist@example.com, then the host_name is 'example.com'. - """) + """))) - fqdn_listname = Attribute( - """The read-only fully qualified name of the mailing list. This is - the guaranteed unique id for the mailing list, and it is always the + fqdn_listname = exported(TextLine( + title=_("The mailing list's filly qualified name"), + description=_("""\ + The read-only fully qualified name of the mailing list. This is the + guaranteed unique id for the mailing list, and it is always the address to which messages are posted, e.g. mylist@example.com. It is always comprised of the list_name + '@' + host_name. - """) + """))) real_name = Attribute( """The short human-readable descriptive name for the mailing list. By diff --git a/src/mailman/rest/adapters.py b/src/mailman/rest/adapters.py index b74028eaa..d5ae01498 100644 --- a/src/mailman/rest/adapters.py +++ b/src/mailman/rest/adapters.py @@ -43,8 +43,9 @@ class DomainSet: def __init__(self, config): self._config = config - def __iter__(self): + def get_domains(self): """See `IDomainSet`.""" + # lazr.restful will not allow this to be a generator. domains = self._config.domains return [domains[domain] for domain in sorted(domains)] diff --git a/src/mailman/rest/configure.zcml b/src/mailman/rest/configure.zcml index 5d0a7c08f..164bbd445 100644 --- a/src/mailman/rest/configure.zcml +++ b/src/mailman/rest/configure.zcml @@ -8,8 +8,9 @@ <include package="lazr.restful" file="meta.zcml"/> <include package="lazr.restful" file="configure.zcml"/> - <webservice:register module="mailman.interfaces.system" /> <webservice:register module="mailman.interfaces.domain" /> + <webservice:register module="mailman.interfaces.listmanager" /> + <webservice:register module="mailman.interfaces.system" /> <adapter for="mailman.config.config.IConfiguration" diff --git a/src/mailman/rest/docs/lists.txt b/src/mailman/rest/docs/lists.txt new file mode 100644 index 000000000..5cdf05639 --- /dev/null +++ b/src/mailman/rest/docs/lists.txt @@ -0,0 +1,12 @@ +============= +Mailing lists +============= + +The REST API can be queried for the set of known mailing lists. There is a +top level collection that can return all the mailing lists. There aren't any +yet though. + + >>> dump_json('http://localhost:8001/3.0/lists') + resource_type_link: https://localhost:8001/3.0/#mailing_lists + start: None + total_size: 0 diff --git a/src/mailman/rest/publication.py b/src/mailman/rest/publication.py index a50976102..1b0e1ee00 100644 --- a/src/mailman/rest/publication.py +++ b/src/mailman/rest/publication.py @@ -41,6 +41,7 @@ from mailman.interfaces.rest import IResolvePathNames class Publication: """Very simple implementation of `IPublication`.""" + implements(IPublication) def __init__(self, application): @@ -94,6 +95,7 @@ class Publication: def endRequest(self, request, ob): """Ends the interaction.""" + config.db.commit() endInteraction() diff --git a/src/mailman/rest/testing/__init__.py b/src/mailman/rest/testing/__init__.py deleted file mode 100644 index e69de29bb..000000000 --- a/src/mailman/rest/testing/__init__.py +++ /dev/null diff --git a/src/mailman/rest/testing/server.py b/src/mailman/rest/testing/server.py deleted file mode 100644 index c4fe2f1ec..000000000 --- a/src/mailman/rest/testing/server.py +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright (C) 2009 by the Free Software Foundation, Inc. -# -# This file is part of GNU Mailman. -# -# GNU Mailman is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along with -# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. - -"""A testable REST server.""" - -from __future__ import absolute_import, unicode_literals - -__metaclass__ = type -__all__ = [ - 'TestableServer', - ] - - -import logging -import threading - -from urllib2 import urlopen - -from mailman.rest.webservice import make_server - - -log = logging.getLogger('mailman.http') - - - -class TestableServer: - """A REST server which polls for the stop action.""" - - def __init__(self): - self.server = make_server() - self.event = threading.Event() - self.thread = threading.Thread(target=self.loop) - self.thread.daemon = True - - def start(self): - """Start the server.""" - self.thread.start() - - def stop(self): - """Stop the server by firing the event.""" - self.event.set() - # Fire off one more request so the handle_request() will exit. - fp = urlopen('http://localhost:8001/3.0/system') - fp.close() - self.thread.join() - - def loop(self): - while not self.event.is_set(): - self.server.handle_request() diff --git a/src/mailman/rest/urls.py b/src/mailman/rest/urls.py index fe12a6ed8..38f676c7f 100644 --- a/src/mailman/rest/urls.py +++ b/src/mailman/rest/urls.py @@ -26,6 +26,8 @@ __all__ = [ ] +import logging + from zope.component import adapts from zope.interface import implements, Interface from zope.traversing.browser.interfaces import IAbsoluteURL @@ -35,6 +37,8 @@ from mailman.core.system import system from mailman.rest.configuration import AdminWebServiceConfiguration from mailman.rest.webservice import AdminWebServiceApplication +log = logging.getLogger('mailman.http') + class BasicURLMapper: @@ -72,11 +76,13 @@ class FallbackURLMapper(BasicURLMapper): :rtype: string :raises KeyError: if no path component can be found. """ + log.debug('generic url mapper lookup: %s', ob) # Special cases. if isinstance(ob, AdminWebServiceApplication): return '' urls = { system: 'system', + #config.db.list_manager: 'lists', } return urls[ob] diff --git a/src/mailman/rest/webservice.py b/src/mailman/rest/webservice.py index a76ad3863..0469e442b 100644 --- a/src/mailman/rest/webservice.py +++ b/src/mailman/rest/webservice.py @@ -49,6 +49,7 @@ log = logging.getLogger('mailman.http') +# pylint: disable-msg: W0232 class AdminWebServiceRequest(WebServiceRequestTraversal, BrowserRequest): """A request for the admin REST interface.""" @@ -81,9 +82,11 @@ class AdminWebServiceApplication: def get(self, name): """Maps root names to resources.""" + log.debug('Getting top level name: %s', name) top_level = dict( system=system, domains=IDomainSet(config), + lists=config.db.list_manager, ) return top_level.get(name) diff --git a/src/mailman/testing/layers.py b/src/mailman/testing/layers.py index 9e4364a7a..1765efc4e 100644 --- a/src/mailman/testing/layers.py +++ b/src/mailman/testing/layers.py @@ -32,20 +32,23 @@ import os import sys import shutil import logging +import datetime import tempfile from pkg_resources import resource_string from textwrap import dedent +from urllib2 import urlopen, URLError from mailman.config import config from mailman.core import initialize from mailman.core.logging import get_handler from mailman.i18n import _ -from mailman.testing.helpers import SMTPServer +from mailman.testing.helpers import SMTPServer, TestableMaster from mailman.utilities.datetime import factory from mailman.utilities.string import expand +TEST_TIMEOUT = datetime.timedelta(seconds=5) NL = '\n' @@ -230,12 +233,25 @@ class RESTLayer(SMTPLayer): server = None + @staticmethod + def _wait_for_rest_server(): + until = datetime.datetime.now() + TEST_TIMEOUT + while datetime.datetime.now() < until: + try: + fp = urlopen('http://localhost:8001/3.0/system') + except URLError: + pass + else: + fp.close() + break + else: + raise RuntimeError('REST server did not start up') + @classmethod def setUp(cls): assert cls.server is None, 'Layer already set up' - from mailman.rest.testing.server import TestableServer - cls.server = TestableServer() - cls.server.start() + cls.server = TestableMaster(cls._wait_for_rest_server) + cls.server.start('rest') @classmethod def tearDown(cls): |
