summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mailman/interfaces/domain.py26
-rw-r--r--src/mailman/queue/docs/rest.txt7
-rw-r--r--src/mailman/rest/adapters.py7
-rw-r--r--src/mailman/rest/configuration.py3
-rw-r--r--src/mailman/rest/configure.zcml2
-rw-r--r--src/mailman/rest/docs/basic.txt4
-rw-r--r--src/mailman/rest/docs/domains.txt66
-rw-r--r--src/mailman/rest/docs/lists.txt2
-rw-r--r--src/mailman/tests/test_documentation.py19
9 files changed, 112 insertions, 24 deletions
diff --git a/src/mailman/interfaces/domain.py b/src/mailman/interfaces/domain.py
index f9476fc3d..96ec1568d 100644
--- a/src/mailman/interfaces/domain.py
+++ b/src/mailman/interfaces/domain.py
@@ -29,7 +29,7 @@ __all__ = [
from lazr.restful.declarations import (
collection_default_content, export_as_webservice_collection,
- export_as_webservice_entry, exported)
+ export_as_webservice_entry, export_factory_operation, exported)
from zope.interface import Interface, Attribute
from zope.schema import TextLine
@@ -174,3 +174,27 @@ class IDomainCollection(Interface):
:return: The list of all known domains.
:rtype: list of `IDomain`
"""
+
+ @export_factory_operation(
+ IDomain,
+ ('email_host', 'description', 'base_url', 'contact_address'))
+ def new(email_host, description=None, base_url=None, contact_address=None):
+ """Add a new domain.
+
+ :param email_host: The email host name for the domain.
+ :type email_host: string
+ :param description: The description of the domain.
+ :type description: string
+ :param base_url: The base url, including the scheme for the web
+ interface of the domain. If not given, it defaults to
+ http://`email_host`/
+ :type base_url: string
+ :param contact_address: The email contact address for the human
+ managing the domain. If not given, defaults to
+ postmaster@`email_host`
+ :type contact_address: string
+ :return: The new domain object
+ :rtype: `IDomain`
+ :raises `BadDomainSpecificationError`: when the `email_host` is
+ already registered.
+ """
diff --git a/src/mailman/queue/docs/rest.txt b/src/mailman/queue/docs/rest.txt
index 62e61643f..b46f0f304 100644
--- a/src/mailman/queue/docs/rest.txt
+++ b/src/mailman/queue/docs/rest.txt
@@ -1,3 +1,4 @@
+===========
REST server
===========
@@ -13,11 +14,11 @@ The RESTful server can be used to access basic version information.
http_etag: "..."
mailman_version: GNU Mailman 3.0... (...)
python_version: ...
- resource_type_link: https://localhost:8001/3.0/#system
- self_link: https://localhost:8001/3.0/system
+ resource_type_link: http://localhost:8001/3.0/#system
+ self_link: http://localhost:8001/3.0/system
Clean up
---------
+========
>>> master.stop()
diff --git a/src/mailman/rest/adapters.py b/src/mailman/rest/adapters.py
index 50ef89b18..4dcf23e54 100644
--- a/src/mailman/rest/adapters.py
+++ b/src/mailman/rest/adapters.py
@@ -61,3 +61,10 @@ class DomainCollection:
if domain is None:
raise NotFound(self, name)
return domain
+
+ def new(self, email_host, description=None, base_url=None,
+ contact_address=None):
+ """See `IDomainCollection`."""
+ value = self._manager.add(
+ email_host, description, base_url, contact_address)
+ return value
diff --git a/src/mailman/rest/configuration.py b/src/mailman/rest/configuration.py
index 07debb617..1c7378010 100644
--- a/src/mailman/rest/configuration.py
+++ b/src/mailman/rest/configuration.py
@@ -25,6 +25,7 @@ __all__ = [
]
+from lazr.config import as_boolean
from lazr.restful.interfaces import IWebServiceConfiguration
from zope.interface import implements
@@ -51,7 +52,7 @@ class AdminWebServiceConfiguration:
@property
def use_https(self):
"""See `IWebServiceConfiguration`."""
- return config.webservice.use_https
+ return as_boolean(config.webservice.use_https)
# This should match the major.minor Mailman version.
service_version_uri_prefix = '{0.MAJOR_REV}.{0.MINOR_REV}'.format(version)
diff --git a/src/mailman/rest/configure.zcml b/src/mailman/rest/configure.zcml
index 8b9d6fb70..79e8ba0e7 100644
--- a/src/mailman/rest/configure.zcml
+++ b/src/mailman/rest/configure.zcml
@@ -12,6 +12,8 @@
<webservice:register module="mailman.interfaces.listmanager" />
<webservice:register module="mailman.interfaces.system" />
+ <adapter factory="zope.publisher.http.HTTPCharsets" />
+
<adapter
for="mailman.interfaces.domain.IDomainManager"
provides="mailman.interfaces.domain.IDomainCollection"
diff --git a/src/mailman/rest/docs/basic.txt b/src/mailman/rest/docs/basic.txt
index 06a507def..79abbfde1 100644
--- a/src/mailman/rest/docs/basic.txt
+++ b/src/mailman/rest/docs/basic.txt
@@ -16,8 +16,8 @@ returned.
http_etag: "..."
mailman_version: GNU Mailman 3.0... (...)
python_version: ...
- resource_type_link: https://localhost:8001/3.0/#system
- self_link: https://localhost:8001/3.0/system
+ resource_type_link: http://localhost:8001/3.0/#system
+ self_link: http://localhost:8001/3.0/system
Non-existent links
diff --git a/src/mailman/rest/docs/domains.txt b/src/mailman/rest/docs/domains.txt
index db1fe7258..b486515cf 100644
--- a/src/mailman/rest/docs/domains.txt
+++ b/src/mailman/rest/docs/domains.txt
@@ -14,7 +14,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: https://localhost:8001/3.0/#domains
+ resource_type_link: http://localhost:8001/3.0/#domains
start: None
total_size: 0
@@ -34,10 +34,10 @@ 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: https://localhost:8001/3.0/#domain
- self_link: https://localhost:8001/3.0/domains/example.com
+ 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: https://localhost:8001/3.0/#domains
+ resource_type_link: http://localhost:8001/3.0/#domains
start: 0
total_size: 1
@@ -64,8 +64,8 @@ At the top level, all domains are returned as separate entries.
description: An example domain
email_host: example.com
http_etag: "..."
- resource_type_link: https://localhost:8001/3.0/#domain
- self_link: https://localhost:8001/3.0/domains/example.com
+ 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:
base_url: http://mail.example.org
@@ -73,8 +73,8 @@ At the top level, all domains are returned as separate entries.
description: None
email_host: example.org
http_etag: "..."
- resource_type_link: https://localhost:8001/3.0/#domain
- self_link: https://localhost:8001/3.0/domains/example.org
+ 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:
base_url: http://example.net
@@ -82,10 +82,10 @@ At the top level, all domains are returned as separate entries.
description: Porkmasters
email_host: lists.example.net
http_etag: "..."
- resource_type_link: https://localhost:8001/3.0/#domain
- self_link: https://localhost:8001/3.0/domains/lists.example.net
+ 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: https://localhost:8001/3.0/#domains
+ resource_type_link: http://localhost:8001/3.0/#domains
start: 0
total_size: 3
@@ -102,8 +102,8 @@ self_links from the above collection.
description: Porkmasters
email_host: lists.example.net
http_etag: "..."
- resource_type_link: https://localhost:8001/3.0/#domain
- self_link: https://localhost:8001/3.0/domains/lists.example.net
+ resource_type_link: http://localhost:8001/3.0/#domain
+ self_link: http://localhost:8001/3.0/domains/lists.example.net
url_host: example.net
But we get a 404 for a non-existent domain.
@@ -112,3 +112,43 @@ But we get a 404 for a non-existent domain.
Traceback (most recent call last):
...
HTTPError: HTTP Error 404: Not Found
+
+
+Creating new domains
+====================
+
+New domains can be created by posting to the 'domains' url. However
+lazr.restful requires us to use a 'named operation' instead of posting
+directly to the URL.
+
+ >>> dump_json('http://localhost:8001/3.0/domains', {
+ ... 'ws.op': 'new',
+ ... 'email_host': 'lists.example.com',
+ ... })
+ URL: http://localhost:8001/3.0/domains
+ content-length: 0
+ content-type: text/plain
+ date: ...
+ location: http://localhost:8001/3.0/domains/lists.example.com
+ server: WSGIServer/... Python/...
+ x-content-type-warning: guessed from content
+ x-powered-by: Zope (www.zope.org), Python (www.python.org)
+
+Now the web service knows about our new domain.
+
+ >>> dump_json('http://localhost:8001/3.0/domains/lists.example.com')
+ base_url: http://lists.example.com
+ contact_address: postmaster@lists.example.com
+ description: None
+ email_host: lists.example.com
+ http_etag: "349365fdbf946e64fc1052b936b9192eaa94b850"
+ 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
+
+And the new domain is in our database.
+
+ >>> manager['lists.example.com']
+ <Domain lists.example.com,
+ base_url: http://lists.example.com,
+ contact_address: postmaster@lists.example.com>
diff --git a/src/mailman/rest/docs/lists.txt b/src/mailman/rest/docs/lists.txt
index 5cdf05639..64645e8e0 100644
--- a/src/mailman/rest/docs/lists.txt
+++ b/src/mailman/rest/docs/lists.txt
@@ -7,6 +7,6 @@ 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
+ resource_type_link: http://localhost:8001/3.0/#mailing_lists
start: None
total_size: 0
diff --git a/src/mailman/tests/test_documentation.py b/src/mailman/tests/test_documentation.py
index 0409e737c..81762c5cc 100644
--- a/src/mailman/tests/test_documentation.py
+++ b/src/mailman/tests/test_documentation.py
@@ -37,6 +37,7 @@ import doctest
import unittest
from email import message_from_string
+from urllib import urlencode
from urllib2 import urlopen
import mailman
@@ -109,16 +110,28 @@ def dump_msgdata(msgdata, *additional_skips):
print '{0:{2}}: {1}'.format(key, msgdata[key], longest)
-def dump_json(url):
+def dump_json(url, data=None):
"""Print the JSON dictionary read from a URL.
:param url: The url to open, read, and print.
:type url: string
+ :param data: Data to use to POST to a URL.
+ :type data: dict
"""
- fp = urlopen(url)
+ if data is None:
+ fp = urlopen(url)
+ else:
+ fp = urlopen(url, urlencode(data))
# fp does not support the context manager protocol.
try:
- data = json.load(fp)
+ raw_data = fp.read()
+ if len(raw_data) == 0:
+ print 'URL:', fp.geturl()
+ info = fp.info()
+ for header in sorted(info):
+ print '{0}: {1}'.format(header, info[header])
+ return
+ data = json.loads(raw_data)
finally:
fp.close()
for key in sorted(data):