summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWilliam Wolf <throughnothing@gmail.com>2011-07-20 13:12:18 -0400
committerWilliam Wolf <throughnothing@gmail.com>2011-07-20 13:12:18 -0400
commita35a70fbaef0ef6634213308d5a68ee60bd714f2 (patch)
tree494e8edfdcc13476ac00c7a46c32e6bac2083962
parent27f326c712e485322003ccdc13acfd04a6fdb119 (diff)
downloadnova-a35a70fbaef0ef6634213308d5a68ee60bd714f2.tar.gz
nova-a35a70fbaef0ef6634213308d5a68ee60bd714f2.tar.xz
nova-a35a70fbaef0ef6634213308d5a68ee60bd714f2.zip
initial changes for application/atom+xml for versions
-rw-r--r--nova/api/openstack/versions.py98
-rw-r--r--nova/api/openstack/wsgi.py19
-rw-r--r--nova/tests/api/openstack/test_versions.py42
3 files changed, 145 insertions, 14 deletions
diff --git a/nova/api/openstack/versions.py b/nova/api/openstack/versions.py
index 1b1fc41ab..a83472e15 100644
--- a/nova/api/openstack/versions.py
+++ b/nova/api/openstack/versions.py
@@ -23,7 +23,11 @@ import nova.api.openstack.views.versions
from nova.api.openstack import wsgi
+ATOM_XMLNS = "http://www.w3.org/2005/Atom"
+
+
class Versions(wsgi.Resource):
+
def __init__(self):
metadata = {
"attributes": {
@@ -33,11 +37,19 @@ class Versions(wsgi.Resource):
}
body_serializers = {
+ 'application/atom+xml': VersionsAtomSerializer(metadata=metadata),
'application/xml': VersionsXMLSerializer(metadata=metadata),
}
serializer = wsgi.ResponseSerializer(body_serializers)
- wsgi.Resource.__init__(self, None, serializer=serializer)
+ supported_content_types = ('application/json',
+ 'application/xml',
+ 'application/atom+xml')
+ deserializer = wsgi.RequestDeserializer(
+ supported_content_types=supported_content_types)
+
+ wsgi.Resource.__init__(self, None, serializer=serializer,
+ deserializer=deserializer)
def dispatch(self, request, *args):
"""Respond to a request for all OpenStack API versions."""
@@ -61,11 +73,8 @@ class Versions(wsgi.Resource):
return dict(versions=versions)
class VersionsXMLSerializer(wsgi.XMLDictSerializer):
- def __init__(self, metadata=None, xmlns=None):
- super(VersionsXMLSerializer, self).__init__(metadata, xmlns)
-
def _versions_to_xml(self, versions):
- root = self.xml_doc.createElement('versions')
+ root = self._xml_doc.createElement('versions')
for version in versions:
root.appendChild(self._create_version_node(version))
@@ -73,13 +82,13 @@ class VersionsXMLSerializer(wsgi.XMLDictSerializer):
return root
def _create_version_node(self, version):
- version_node = self.xml_doc.createElement('version')
+ version_node = self._xml_doc.createElement('version')
version_node.setAttribute('id', version['id'])
version_node.setAttribute('status', version['status'])
version_node.setAttribute('updated', version['updated'])
for link in version['links']:
- link_node = self.xml_doc.createElement('atom:link')
+ link_node = self._xml_doc.createElement('atom:link')
link_node.setAttribute('rel', link['rel'])
link_node.setAttribute('href', link['href'])
version_node.appendChild(link_node)
@@ -88,7 +97,80 @@ class VersionsXMLSerializer(wsgi.XMLDictSerializer):
def default(self, data):
- self.xml_doc = minidom.Document()
+ self._xml_doc = minidom.Document()
node = self._versions_to_xml(data['versions'])
return self.to_xml_string(node)
+
+class VersionsAtomSerializer(wsgi.XMLDictSerializer):
+ def __init__(self, metadata=None, xmlns=None):
+ if not xmlns:
+ self.xmlns = ATOM_XMLNS
+ else:
+ self.xmlns = xmlns
+
+ def _create_text_elem(self, name, text, type=None):
+ elem = self._xml_doc.createElement(name)
+ if type:
+ elem.setAttribute('type', type)
+ elem_text = self._xml_doc.createTextNode(text)
+ elem.appendChild(elem_text)
+ return elem
+
+ def _create_meta(self, root):
+ title = self._create_text_elem('title', 'Available API Versions',
+ type='text')
+ #TODO(wwolf): what should updated be?
+ updated = self._create_text_elem('updated', '2010-12-12T18:30:02.25Z')
+ #TODO(wwolf): get URI
+ id = self._create_text_elem('id', '')
+ #TODO(wwolf): get link info
+ link = self._xml_doc.createElement('link')
+ link.setAttribute('rel', 'rel')
+ link.setAttribute('href', 'href')
+
+ author = self._xml_doc.createElement('author')
+ author_name = self._create_text_elem('name', 'Rackspace')
+ author_uri = self._create_text_elem('uri', 'http://www.rackspace.com')
+ author.appendChild(author_name)
+ author.appendChild(author_uri)
+
+ root.appendChild(title)
+ root.appendChild(updated)
+ root.appendChild(id)
+ root.appendChild(link)
+ root.appendChild(author)
+
+ def _create_version_entries(self, root, versions):
+ for version in versions:
+ entry = self._xml_doc.createElement('entry')
+ #TODO(wwolf) GET URI
+ id = self._create_text_elem('id', 'URI')
+ title = self._create_text_elem('title',
+ 'Version %s' % version['id'],
+ type='text')
+ updated = self._create_text_elem('updated', version['updated'])
+ #TODO(wwolf): get link info
+ link = self._xml_doc.createElement('link')
+ link.setAttribute('rel', 'rel')
+ link.setAttribute('href', 'href')
+ content = self._create_text_elem('content',
+ 'Version %s %s (%s)' %
+ (version['id'],
+ version['status'],
+ version['updated']))
+
+ entry.appendChild(id)
+ entry.appendChild(title)
+ entry.appendChild(updated)
+ entry.appendChild(link)
+ entry.appendChild(content)
+ root.appendChild(entry)
+
+ def default(self, data):
+ self._xml_doc = minidom.Document()
+ node = self._xml_doc.createElementNS(self.xmlns, 'feed')
+ self._create_meta(node)
+ self._create_version_entries(node, data['versions'])
+
+ return self.to_xml_string(node)
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index c3f841aa5..9df6fd058 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -20,21 +20,22 @@ LOG = logging.getLogger('nova.api.openstack.wsgi')
class Request(webob.Request):
"""Add some Openstack API-specific logic to the base webob.Request."""
- def best_match_content_type(self):
+ def best_match_content_type(self, supported_content_types=None):
"""Determine the requested response content-type.
Based on the query extension then the Accept header.
"""
- supported = ('application/json', 'application/xml')
+ supported_content_types = supported_content_types or \
+ ('application/json', 'application/xml')
parts = self.path.rsplit('.', 1)
if len(parts) > 1:
ctype = 'application/{0}'.format(parts[1])
- if ctype in supported:
+ if ctype in supported_content_types:
return ctype
- bm = self.accept.best_match(supported)
+ bm = self.accept.best_match(supported_content_types)
# default to application/json if we don't find a preference
return bm or 'application/json'
@@ -151,7 +152,12 @@ class RequestHeadersDeserializer(ActionDispatcher):
class RequestDeserializer(object):
"""Break up a Request object into more useful pieces."""
- def __init__(self, body_deserializers=None, headers_deserializer=None):
+ def __init__(self, body_deserializers=None, headers_deserializer=None,
+ supported_content_types=None):
+
+ self.supported_content_types = supported_content_types or \
+ ('application/json', 'application/xml')
+
self.body_deserializers = {
'application/xml': XMLDeserializer(),
'application/json': JSONDeserializer(),
@@ -213,7 +219,7 @@ class RequestDeserializer(object):
raise exception.InvalidContentType(content_type=content_type)
def get_expected_content_type(self, request):
- return request.best_match_content_type()
+ return request.best_match_content_type(self.supported_content_types)
def get_action_args(self, request_environment):
"""Parse dictionary created by routes library."""
@@ -412,6 +418,7 @@ class Resource(wsgi.Application):
serialized by requested content type.
"""
+
def __init__(self, controller, deserializer=None, serializer=None):
"""
:param controller: object that implement methods created by routes lib
diff --git a/nova/tests/api/openstack/test_versions.py b/nova/tests/api/openstack/test_versions.py
index 32c57057a..275cfa9ef 100644
--- a/nova/tests/api/openstack/test_versions.py
+++ b/nova/tests/api/openstack/test_versions.py
@@ -84,6 +84,48 @@ class VersionsTest(test.TestCase):
self.assertEqual(expected, actual)
+ def test_get_version_list_atom(self):
+ req = webob.Request.blank('/')
+ req.accept = "application/atom+xml"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res.content_type, "application/atom+xml")
+
+ expected = """
+ <feed xmlns="http://www.w3.org/2005/Atom">
+ <title type="text">Available API Versions</title>
+ <updated>2010-12-12T18:30:02.25Z</updated>
+ <id>http://servers.api.openstack.org/</id>
+ <author>
+ <name>Rackspace</name>
+ <uri>http://www.rackspace.com/</uri>
+ </author>
+ <link rel="self" href="http://servers.api.openstack.org/"/>
+ <entry>
+ <id>http://servers.api.openstack.org/v1.1/</id>
+ <title type="text">Version v1.1</title>
+ <updated>2010-12-12T18:30:02.25Z</updated>
+ <link rel="self" href="http://servers.api.openstack.org/v1.1/"/>
+ <content type="text">
+ Version v1.1 CURRENT (2010-12-12T18:30:02.25Z)
+ </content>
+ </entry>
+ <entry>
+ <id>http://servers.api.openstack.org/v1.0/</id>
+ <title type="text">Version v1.0</title>
+ <updated>2009-10-09T11:30:00Z</updated>
+ <link rel="self" href="http://servers.api.openstack.org/v1.0/"/>
+ <content type="text">
+ Version v1.0 DEPRECATED (2009-10-09T11:30:00Z)
+ </content>
+ </entry>
+ </feed>
+ """.replace(" ", "").replace("\n", "")
+
+ actual = res.body.replace(" ", "").replace("\n", "")
+
+ self.assertEqual(expected, actual)
+
def test_view_builder(self):
base_url = "http://example.org/"