diff options
| -rw-r--r-- | src/mailman/rest/docs/domains.txt | 13 | ||||
| -rw-r--r-- | src/mailman/rest/domains.py | 107 | ||||
| -rw-r--r-- | src/mailman/rest/helpers.py | 4 | ||||
| -rw-r--r-- | src/mailman/rest/root.py | 14 | ||||
| -rw-r--r-- | src/mailman/rest/webservice.py | 81 |
5 files changed, 122 insertions, 97 deletions
diff --git a/src/mailman/rest/docs/domains.txt b/src/mailman/rest/docs/domains.txt index 0bce5fa54..86c868530 100644 --- a/src/mailman/rest/docs/domains.txt +++ b/src/mailman/rest/docs/domains.txt @@ -16,7 +16,7 @@ The REST API can be queried for the set of known domains, of which there are initially none. >>> dump_json('http://localhost:8001/3.0/domains') - resource_type_link: http://localhost:8001/3.0/#domains + http_etag: "f62b4cab6d4bfa731d5ff091dde29d318a3234a8" start: None total_size: 0 @@ -36,10 +36,9 @@ Once a domain is added though, it is accessible through the API. description: An example domain email_host: example.com http_etag: "..." - resource_type_link: http://localhost:8001/3.0/#domain self_link: http://localhost:8001/3.0/domains/example.com url_host: lists.example.com - resource_type_link: http://localhost:8001/3.0/#domains + http_etag: "..." start: 0 total_size: 1 @@ -68,7 +67,6 @@ At the top level, all domains are returned as separate entries. description: An example domain email_host: example.com http_etag: "..." - resource_type_link: http://localhost:8001/3.0/#domain self_link: http://localhost:8001/3.0/domains/example.com url_host: lists.example.com entry 1: @@ -77,7 +75,6 @@ At the top level, all domains are returned as separate entries. description: None email_host: example.org http_etag: "..." - resource_type_link: http://localhost:8001/3.0/#domain self_link: http://localhost:8001/3.0/domains/example.org url_host: mail.example.org entry 2: @@ -86,10 +83,9 @@ At the top level, all domains are returned as separate entries. description: Porkmasters email_host: lists.example.net http_etag: "..." - resource_type_link: http://localhost:8001/3.0/#domain self_link: http://localhost:8001/3.0/domains/lists.example.net url_host: example.net - resource_type_link: http://localhost:8001/3.0/#domains + http_etag: "..." start: 0 total_size: 3 @@ -106,7 +102,6 @@ self_links from the above collection. description: Porkmasters email_host: lists.example.net http_etag: "..." - resource_type_link: http://localhost:8001/3.0/#domain self_link: http://localhost:8001/3.0/domains/lists.example.net url_host: example.net @@ -139,7 +134,6 @@ Now the web service knows about our new domain. description: None email_host: lists.example.com http_etag: "..." - resource_type_link: http://localhost:8001/3.0/#domain self_link: http://localhost:8001/3.0/domains/lists.example.com url_host: lists.example.com @@ -173,7 +167,6 @@ address. description: My new domain email_host: my.example.com http_etag: "..." - resource_type_link: http://localhost:8001/3.0/#domain self_link: http://localhost:8001/3.0/domains/my.example.com url_host: allmy.example.com diff --git a/src/mailman/rest/domains.py b/src/mailman/rest/domains.py new file mode 100644 index 000000000..ed63834b9 --- /dev/null +++ b/src/mailman/rest/domains.py @@ -0,0 +1,107 @@ +# Copyright (C) 2010 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 for domains.""" + +from __future__ import absolute_import, unicode_literals + +__metaclass__ = type +__all__ = [ + 'ADomain', + 'AllDomains', + ] + + +from restish import http, resource +from zope.component import getUtility + +from mailman.interfaces.domain import ( + BadDomainSpecificationError, IDomainManager) +from mailman.rest.helpers import etag, path_to + + + +class _DomainBase(resource.Resource): + """Shared base class for domain representations.""" + + def _domain_data(self, domain): + """Return the domain data for a single domain.""" + return dict( + base_url=domain.base_url, + contact_address=domain.contact_address, + description=domain.description, + email_host=domain.email_host, + self_link=path_to('domains/{0}'.format(domain.email_host)), + url_host=domain.url_host, + ) + + def _format_domain(self, domain): + """Format the data for a single domain.""" + return etag(self._domain_data(domain)) + + +class ADomain(_DomainBase): + """A domain.""" + + def __init__(self, domain): + self._domain = domain + + @resource.GET() + def domain(self, request): + """Return a single domain end-point.""" + domain = getUtility(IDomainManager).get(self._domain) + if domain is None: + return http.not_found() + return http.ok([], self._format_domain(domain)) + + +class AllDomains(_DomainBase): + """The domains.""" + + @resource.POST() + def create(self, request): + """Create a new domain.""" + # XXX 2010-02-23 barry Sanity check the POST arguments by + # introspection of the target method, or via descriptors. + domain_manager = getUtility(IDomainManager) + try: + # webob gives this to us as a string, but we need unicodes. + kws = dict((key, unicode(value)) + for key, value in request.POST.items()) + domain = domain_manager.add(**kws) + except BadDomainSpecificationError: + return http.bad_request([], 'Domain exists') + location = path_to('domains/{0}'.format(domain.email_host)) + # Include no extra headers or body. + return http.created(location, [], None) + + @resource.GET() + def container(self, request): + """Return the /domains end-point.""" + domains = list(getUtility(IDomainManager)) + if len(domains) == 0: + resource = dict(start=None, total_size=0) + return http.ok([], etag(resource)) + domain_data = [self._domain_data(domain) for domain in domains] + # Tag this domain data, but ignore the results. + [etag(data) for data in domain_data] + resource = dict( + start=0, + total_size=len(domains), + entries=domain_data, + ) + return http.ok([], etag(resource)) diff --git a/src/mailman/rest/helpers.py b/src/mailman/rest/helpers.py index d1afdb0d9..fac4ae4f1 100644 --- a/src/mailman/rest/helpers.py +++ b/src/mailman/rest/helpers.py @@ -41,9 +41,9 @@ def path_to(resource): system base URI. :type resource: string :return: The full path to the resource. - :rtype: string + :rtype: bytes """ - return '{0}://{1}:{2}/{3}/{4}'.format( + return b'{0}://{1}:{2}/{3}/{4}'.format( ('https' if as_boolean(config.webservice.use_https) else 'http'), config.webservice.hostname, config.webservice.port, diff --git a/src/mailman/rest/root.py b/src/mailman/rest/root.py index e3ba1b62e..e524f3632 100644 --- a/src/mailman/rest/root.py +++ b/src/mailman/rest/root.py @@ -25,16 +25,14 @@ __all__ = [ ] -import json -import hashlib - from restish import http, resource from mailman.config import config from mailman.core.system import system +from mailman.rest.domains import ADomain, AllDomains from mailman.rest.helpers import etag, path_to from mailman.rest.webservice import ( - ADomain, AList, AllDomains, AllLists, AllMembers) + AList, AllLists, AllMembers) @@ -66,6 +64,9 @@ class TopLevel(resource.Resource): @resource.child() def domains(self, request, segments): + """/<api>/domains + /<api>/domains/<domain> + """ if len(segments) == 0: return AllDomains() elif len(segments) == 1: @@ -75,6 +76,10 @@ class TopLevel(resource.Resource): @resource.child() def lists(self, request, segments): + """/<api>/lists + /<api>/lists/<list> + /<api>/lists/<list>/... + """ if len(segments) == 0: return AllLists() else: @@ -83,6 +88,7 @@ class TopLevel(resource.Resource): @resource.child() def members(self, request, segments): + """/<api>/members""" if len(segments) == 0: return AllMembers() return http.bad_request() diff --git a/src/mailman/rest/webservice.py b/src/mailman/rest/webservice.py index 03c2097af..403948de8 100644 --- a/src/mailman/rest/webservice.py +++ b/src/mailman/rest/webservice.py @@ -52,87 +52,6 @@ log = logging.getLogger('mailman.http') -class _DomainBase(resource.Resource): - """Shared base class for domain representations.""" - - def _format_domain(self, domain): - """Format the data for a single domain.""" - domain_data = dict( - base_url=domain.base_url, - contact_address=domain.contact_address, - description=domain.description, - email_host=domain.email_host, - resource_type_link='http://localhost:8001/3.0/#domain', - self_link='http://localhost:8001/3.0/domains/{0}'.format( - domain.email_host), - url_host=domain.url_host, - ) - etag = hashlib.sha1(repr(domain_data)).hexdigest() - domain_data['http_etag'] = '"{0}"'.format(etag) - return domain_data - - -class ADomain(_DomainBase): - """A domain.""" - - def __init__(self, domain): - self._domain = domain - - @resource.GET() - def domain(self, request): - """Return a single domain end-point.""" - domain = getUtility(IDomainManager).get(self._domain) - if domain is None: - return http.not_found() - return http.ok([], json.dumps(self._format_domain(domain))) - - -class AllDomains(_DomainBase): - """The domains.""" - - @resource.POST() - def create(self, request): - """Create a new domain.""" - # XXX 2010-02-23 barry Sanity check the POST arguments by - # introspection of the target method, or via descriptors. - domain_manager = getUtility(IDomainManager) - try: - # webob gives this to us as a string, but we need unicodes. - kws = dict((key, unicode(value)) - for key, value in request.POST.items()) - domain = domain_manager.add(**kws) - except BadDomainSpecificationError: - return http.bad_request([], 'Domain exists') - # 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(location, [], None) - - @resource.GET() - def container(self, request): - """Return the /domains end-point.""" - domains = list(getUtility(IDomainManager)) - if len(domains) == 0: - return http.ok( - [], json.dumps(dict(resource_type_link= - 'http://localhost:8001/3.0/#domains', - start=None, - total_size=0))) - entries = [] - response = dict( - resource_type_link='http://localhost:8001/3.0/#domains', - start=0, - total_size=len(domains), - entries=entries, - ) - for domain in domains: - domain_data = self._format_domain(domain) - entries.append(domain_data) - return http.ok([], json.dumps(response)) - - - class _ListBase(resource.Resource): """Shared base class for mailing list representations.""" |
