summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mailman/docs/NEWS.rst3
-rw-r--r--src/mailman/interfaces/domain.py3
-rw-r--r--src/mailman/model/docs/domains.rst24
-rw-r--r--src/mailman/model/domain.py10
-rw-r--r--src/mailman/rest/docs/domains.rst34
-rw-r--r--src/mailman/rest/docs/lists.rst20
-rw-r--r--src/mailman/rest/domains.py12
-rw-r--r--src/mailman/rest/lists.py19
-rw-r--r--src/mailman/rest/root.py5
-rw-r--r--src/mailman/rest/tests/test_domains.py72
10 files changed, 198 insertions, 4 deletions
diff --git a/src/mailman/docs/NEWS.rst b/src/mailman/docs/NEWS.rst
index 54b05f028..597387b40 100644
--- a/src/mailman/docs/NEWS.rst
+++ b/src/mailman/docs/NEWS.rst
@@ -39,6 +39,9 @@ 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)
+ * 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.
* Fixed /lists/<fqdn_listname>/<role>/<email> (LP: #825570)
* Remove role plurals from /lists/<fqdn_listname/rosters/<role>
* Fixed incorrect error code for /members/<bogus> (LP: #821020). Given by
diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py
index 0e0ea72ae..e0423f73b 100644
--- a/src/mailman/interfaces/domain.py
+++ b/src/mailman/interfaces/domain.py
@@ -64,6 +64,9 @@ class IDomain(Interface):
The contact address for the human at this domain.
E.g. postmaster@example.com""")
+ mailing_lists = Attribute(
+ 'All mailing lists for this domain.')
+
def confirm_url(token=''):
"""The url used for various forms of confirmation.
diff --git a/src/mailman/model/docs/domains.rst b/src/mailman/model/docs/domains.rst
index 4fa39aa2f..878e5835a 100644
--- a/src/mailman/model/docs/domains.rst
+++ b/src/mailman/model/docs/domains.rst
@@ -71,6 +71,30 @@ Domains can have explicit descriptions and contact addresses.
base_url: http://lists.example.net,
contact_address: postmaster@example.com>
+Domains can list all associated mailing lists with the mailing_lists property.
+::
+
+ >>> def show_lists(domain):
+ ... mlists = list(domain.mailing_lists)
+ ... for mlist in mlists:
+ ... print mlist
+ ... if len(mlists) == 0:
+ ... print 'no lists'
+
+ >>> net_domain = manager['example.net']
+ >>> com_domain = manager['example.com']
+ >>> show_lists(net_domain)
+ no lists
+
+ >>> create_list('test@example.net')
+ <mailing list "test@example.net" at ...>
+ >>> transaction.commit()
+ >>> show_lists(net_domain)
+ <mailing list "test@example.net" at ...>
+
+ >>> show_lists(com_domain)
+ no lists
+
In the global domain manager, domains are indexed by their email host name.
::
diff --git a/src/mailman/model/domain.py b/src/mailman/model/domain.py
index ad3e84fc7..c752d4f0b 100644
--- a/src/mailman/model/domain.py
+++ b/src/mailman/model/domain.py
@@ -33,6 +33,7 @@ from mailman.config import config
from mailman.database.model import Model
from mailman.interfaces.domain import (
BadDomainSpecificationError, IDomain, IDomainManager)
+from mailman.model.mailinglist import MailingList
@@ -85,6 +86,15 @@ class Domain(Model):
"""See `IDomain`."""
return urlparse(self.base_url).scheme
+ @property
+ def mailing_lists(self):
+ """See `IDomain`."""
+ mailing_lists = config.db.store.find(
+ MailingList,
+ MailingList.mail_host == self.mail_host)
+ for mlist in mailing_lists:
+ yield mlist
+
def confirm_url(self, token=''):
"""See `IDomain`."""
return urljoin(self.base_url, 'confirm/' + token)
diff --git a/src/mailman/rest/docs/domains.rst b/src/mailman/rest/docs/domains.rst
index 15638c38d..a8a4fd027 100644
--- a/src/mailman/rest/docs/domains.rst
+++ b/src/mailman/rest/docs/domains.rst
@@ -117,6 +117,40 @@ But we get a 404 for a non-existent domain.
...
HTTPError: HTTP Error 404: 404 Not Found
+You can also list all the mailing lists for a given domain. At first, the
+example.com domain does not contain any mailing lists.
+::
+
+ >>> dump_json('http://localhost:9001/3.0/domains/example.com/lists')
+ http_etag: "..."
+ start: 0
+ total_size: 0
+
+ >>> dump_json('http://localhost:9001/3.0/lists', {
+ ... 'fqdn_listname': 'test-domains@example.com',
+ ... })
+ content-length: 0
+ date: ...
+ location: http://localhost:9001/3.0/lists/test-domains@example.com
+ ...
+
+ >>> dump_json('http://localhost:9001/3.0/domains/example.com/lists')
+ entry 0:
+ fqdn_listname: test-domains@example.com
+ http_etag: "..."
+ ...
+ self_link: http://localhost:9001/3.0/lists/test-domains@example.com
+ http_etag: "..."
+ start: 0
+ total_size: 1
+
+Other domains continue to contain no mailing lists.
+
+ >>> dump_json('http://localhost:9001/3.0/domains/lists.example.net/lists')
+ http_etag: "..."
+ start: 0
+ total_size: 0
+
Creating new domains
====================
diff --git a/src/mailman/rest/docs/lists.rst b/src/mailman/rest/docs/lists.rst
index fd96507c3..c412f6edc 100644
--- a/src/mailman/rest/docs/lists.rst
+++ b/src/mailman/rest/docs/lists.rst
@@ -30,6 +30,26 @@ Create a mailing list in a domain and it's accessible via the API.
start: 0
total_size: 1
+You can also query for lists from a particular domain.
+::
+
+ >>> dump_json('http://localhost:9001/3.0/domains/example.com/lists')
+ entry 0:
+ fqdn_listname: test-one@example.com
+ http_etag: "..."
+ list_name: test-one
+ mail_host: example.com
+ real_name: Test-one
+ self_link: http://localhost:9001/3.0/lists/test-one@example.com
+ http_etag: "..."
+ start: 0
+ total_size: 1
+
+ >>> dump_json('http://localhost:9001/3.0/domains/no.example.org/lists')
+ Traceback (most recent call last):
+ ...
+ HTTPError: HTTP Error 404: 404 Not Found
+
Creating lists via the API
==========================
diff --git a/src/mailman/rest/domains.py b/src/mailman/rest/domains.py
index d42497157..da353ca58 100644
--- a/src/mailman/rest/domains.py
+++ b/src/mailman/rest/domains.py
@@ -32,6 +32,7 @@ from zope.component import getUtility
from mailman.interfaces.domain import (
BadDomainSpecificationError, IDomainManager)
from mailman.rest.helpers import CollectionMixin, etag, no_content, path_to
+from mailman.rest.lists import ListsForDomain
from mailman.rest.validator import Validator
@@ -79,6 +80,17 @@ class ADomain(_DomainBase):
return http.not_found()
return no_content()
+ @resource.child()
+ def lists(self, request, segments):
+ """/domains/<domain>/lists"""
+ if len(segments) == 0:
+ domain = getUtility(IDomainManager).get(self._domain)
+ if domain is None:
+ return http.not_found()
+ return ListsForDomain(domain)
+ else:
+ return http.bad_request()
+
class AllDomains(_DomainBase):
"""The domains."""
diff --git a/src/mailman/rest/lists.py b/src/mailman/rest/lists.py
index 2c2f58da1..6a6388320 100644
--- a/src/mailman/rest/lists.py
+++ b/src/mailman/rest/lists.py
@@ -24,6 +24,7 @@ __all__ = [
'AList',
'AllLists',
'ListConfiguration',
+ 'ListsForDomain',
]
@@ -207,4 +208,20 @@ class MembersOfList(MemberCollection):
roster = self._mlist.get_roster(self._role)
address_of_member = attrgetter('address.email')
return list(sorted(roster.members, key=address_of_member))
-
+
+
+class ListsForDomain(_ListBase):
+ """The mailing lists for a particular domain."""
+
+ def __init__(self, domain):
+ self._domain = domain
+
+ @resource.GET()
+ def collection(self, request):
+ """/domains/<domain>/lists"""
+ resource = self._make_collection(request)
+ return http.ok([], etag(resource))
+
+ def _get_collection(self, request):
+ """See `CollectionMixin`."""
+ return list(self._domain.mailing_lists)
diff --git a/src/mailman/rest/root.py b/src/mailman/rest/root.py
index cd4c8ceef..dc9717de4 100644
--- a/src/mailman/rest/root.py
+++ b/src/mailman/rest/root.py
@@ -98,10 +98,9 @@ class TopLevel(resource.Resource):
"""
if len(segments) == 0:
return AllDomains()
- elif len(segments) == 1:
- return ADomain(segments[0]), []
else:
- return http.bad_request()
+ domain = segments.pop(0)
+ return ADomain(domain), segments
@resource.child()
def lists(self, request, segments):
diff --git a/src/mailman/rest/tests/test_domains.py b/src/mailman/rest/tests/test_domains.py
new file mode 100644
index 000000000..e12d340ae
--- /dev/null
+++ b/src/mailman/rest/tests/test_domains.py
@@ -0,0 +1,72 @@
+# Copyright (C) 2011 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/>.
+
+"""REST domain tests."""
+
+from __future__ import absolute_import, unicode_literals
+
+__metaclass__ = type
+__all__ = [
+ 'test_suite',
+ ]
+
+
+import unittest
+
+from urllib2 import HTTPError
+
+from mailman.app.lifecycle import create_list
+from mailman.config import config
+from mailman.testing.helpers import call_api
+from mailman.testing.layers import RESTLayer
+
+
+
+class TestDomains(unittest.TestCase):
+ layer = RESTLayer
+
+ def setUp(self):
+ self._mlist = create_list('test@example.com')
+ config.db.commit()
+
+ def test_bogus_endpoint_extension(self):
+ # /domains/<domain>/lists/<anything> is not a valid endpoint.
+ try:
+ # For Python 2.6.
+ call_api('http://localhost:9001/3.0/domains/example.com'
+ '/lists/wrong')
+ except HTTPError as exc:
+ self.assertEqual(exc.code, 400)
+ else:
+ raise AssertionError('Expected HTTPError')
+
+ def test_bogus_endpoint(self):
+ # /domains/<domain>/<!lists> does not exist.
+ try:
+ # For Python 2.6.
+ call_api('http://localhost:9001/3.0/domains/example.com/wrong')
+ except HTTPError as exc:
+ self.assertEqual(exc.code, 404)
+ else:
+ raise AssertionError('Expected HTTPError')
+
+
+
+def test_suite():
+ suite = unittest.TestSuite()
+ suite.addTest(unittest.makeSuite(TestDomains))
+ return suite