summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJosh Kearney <josh@jk0.org>2011-08-02 17:24:12 -0500
committerJosh Kearney <josh@jk0.org>2011-08-02 17:24:12 -0500
commitaec281367e93fb9805236ff9be1a3a9671efccf8 (patch)
tree14ab33914cb5ac667799f3008b6e2dffb8d631be
parentffbd2b4e787f6b43e33933fdcd69f1459474f422 (diff)
parent3821904c962ac0826d42660898b20b8413c727f2 (diff)
Merged trunk.
-rw-r--r--nova/api/openstack/__init__.py5
-rw-r--r--nova/api/openstack/versions.py270
-rw-r--r--nova/api/openstack/views/versions.py56
-rw-r--r--nova/api/openstack/wsgi.py3
-rw-r--r--nova/tests/api/openstack/test_versions.py862
5 files changed, 1074 insertions, 122 deletions
diff --git a/nova/api/openstack/__init__.py b/nova/api/openstack/__init__.py
index 868b98a31..96a2f20e0 100644
--- a/nova/api/openstack/__init__.py
+++ b/nova/api/openstack/__init__.py
@@ -40,6 +40,7 @@ from nova.api.openstack import servers
from nova.api.openstack import server_metadata
from nova.api.openstack import shared_ip_groups
from nova.api.openstack import users
+from nova.api.openstack import versions
from nova.api.openstack import wsgi
from nova.api.openstack import zones
@@ -115,6 +116,10 @@ class APIRouter(base_wsgi.Router):
'select': 'POST',
'boot': 'POST'})
+ mapper.connect("versions", "/",
+ controller=versions.create_resource(version),
+ action='show')
+
mapper.resource("console", "consoles",
controller=consoles.create_resource(),
parent_resource=dict(member_name='server',
diff --git a/nova/api/openstack/versions.py b/nova/api/openstack/versions.py
index df7a94b7e..3ef72b7f6 100644
--- a/nova/api/openstack/versions.py
+++ b/nova/api/openstack/versions.py
@@ -24,7 +24,66 @@ import nova.api.openstack.views.versions
from nova.api.openstack import wsgi
-ATOM_XMLNS = "http://www.w3.org/2005/Atom"
+VERSIONS = {
+ "v1.0": {
+ "id": "v1.0",
+ "status": "DEPRECATED",
+ "updated": "2011-01-21T11:33:21Z",
+ "links": [
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.0/cs-devguide-20110125.pdf"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.0/application.wadl"
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.0+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.0+json"
+ }
+ ],
+ },
+ "v1.1": {
+ "id": "v1.1",
+ "status": "CURRENT",
+ "updated": "2011-01-21T11:33:21Z",
+ "links": [
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.1/cs-devguide-20110125.pdf"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.1/application.wadl"
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.1+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.1+json"
+ }
+ ],
+ },
+}
class Versions(wsgi.Resource):
@@ -36,16 +95,20 @@ class Versions(wsgi.Resource):
}
}
+ headers_serializer = VersionsHeadersSerializer()
+
body_serializers = {
'application/atom+xml': VersionsAtomSerializer(metadata=metadata),
'application/xml': VersionsXMLSerializer(metadata=metadata),
}
- serializer = wsgi.ResponseSerializer(body_serializers)
+ serializer = wsgi.ResponseSerializer(
+ body_serializers=body_serializers,
+ headers_serializer=headers_serializer)
supported_content_types = ('application/json',
'application/xml',
'application/atom+xml')
- deserializer = wsgi.RequestDeserializer(
+ deserializer = VersionsRequestDeserializer(
supported_content_types=supported_content_types)
wsgi.Resource.__init__(self, None, serializer=serializer,
@@ -53,60 +116,131 @@ class Versions(wsgi.Resource):
def dispatch(self, request, *args):
"""Respond to a request for all OpenStack API versions."""
- version_objs = [
- {
- "id": "v1.1",
- "status": "CURRENT",
- #TODO(wwolf) get correct value for these
- "updated": "2011-07-18T11:30:00Z",
- },
- {
- "id": "v1.0",
- "status": "DEPRECATED",
- #TODO(wwolf) get correct value for these
- "updated": "2010-10-09T11:30:00Z",
- },
- ]
-
builder = nova.api.openstack.views.versions.get_view_builder(request)
- versions = [builder.build(version) for version in version_objs]
- return dict(versions=versions)
+ if request.path == '/':
+ # List Versions
+ return builder.build_versions(VERSIONS)
+ else:
+ # Versions Multiple Choice
+ return builder.build_choices(VERSIONS, request)
+
+
+class VersionV10(object):
+ def show(self, req):
+ builder = nova.api.openstack.views.versions.get_view_builder(req)
+ return builder.build_version(VERSIONS['v1.0'])
+
+
+class VersionV11(object):
+ def show(self, req):
+ builder = nova.api.openstack.views.versions.get_view_builder(req)
+ return builder.build_version(VERSIONS['v1.1'])
+
+
+class VersionsRequestDeserializer(wsgi.RequestDeserializer):
+ def get_expected_content_type(self, request):
+ supported_content_types = list(self.supported_content_types)
+ if request.path != '/':
+ # Remove atom+xml accept type for 300 responses
+ if 'application/atom+xml' in supported_content_types:
+ supported_content_types.remove('application/atom+xml')
+
+ return request.best_match_content_type(supported_content_types)
+
+ def get_action_args(self, request_environment):
+ """Parse dictionary created by routes library."""
+ args = {}
+ if request_environment['PATH_INFO'] == '/':
+ args['action'] = 'index'
+ else:
+ args['action'] = 'multi'
+
+ return args
class VersionsXMLSerializer(wsgi.XMLDictSerializer):
- def _versions_to_xml(self, versions):
- root = self._xml_doc.createElement('versions')
+ #TODO(wwolf): this is temporary until we get rid of toprettyxml
+ # in the base class (XMLDictSerializer), which I plan to do in
+ # another branch
+ def to_xml_string(self, node, has_atom=False):
+ self._add_xmlns(node, has_atom)
+ return node.toxml(encoding='UTF-8')
+
+ def _versions_to_xml(self, versions, name="versions", xmlns=None):
+ root = self._xml_doc.createElement(name)
+ root.setAttribute("xmlns", wsgi.XMLNS_V11)
+ root.setAttribute("xmlns:atom", wsgi.XMLNS_ATOM)
for version in versions:
root.appendChild(self._create_version_node(version))
return root
- def _create_version_node(self, version):
+ def _create_media_types(self, media_types):
+ base = self._xml_doc.createElement('media-types')
+ for type in media_types:
+ node = self._xml_doc.createElement('media-type')
+ node.setAttribute('base', type['base'])
+ node.setAttribute('type', type['type'])
+ base.appendChild(node)
+
+ return base
+
+ def _create_version_node(self, version, create_ns=False):
version_node = self._xml_doc.createElement('version')
+ if create_ns:
+ xmlns = wsgi.XMLNS_V11
+ xmlns_atom = wsgi.XMLNS_ATOM
+ version_node.setAttribute('xmlns', xmlns)
+ version_node.setAttribute('xmlns:atom', xmlns_atom)
+
version_node.setAttribute('id', version['id'])
version_node.setAttribute('status', version['status'])
- version_node.setAttribute('updated', version['updated'])
+ if 'updated' in version:
+ version_node.setAttribute('updated', version['updated'])
+
+ if 'media-types' in version:
+ media_types = self._create_media_types(version['media-types'])
+ version_node.appendChild(media_types)
- for link in version['links']:
- 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)
+ link_nodes = self._create_link_nodes(self._xml_doc, version['links'])
+ for link in link_nodes:
+ version_node.appendChild(link)
return version_node
- def default(self, data):
+ def index(self, data):
self._xml_doc = minidom.Document()
node = self._versions_to_xml(data['versions'])
return self.to_xml_string(node)
+ def show(self, data):
+ self._xml_doc = minidom.Document()
+ node = self._create_version_node(data['version'], True)
+
+ return self.to_xml_string(node)
+
+ def multi(self, data):
+ self._xml_doc = minidom.Document()
+ node = self._versions_to_xml(data['choices'], 'choices',
+ xmlns=wsgi.XMLNS_V11)
+
+ return self.to_xml_string(node)
+
class VersionsAtomSerializer(wsgi.XMLDictSerializer):
+ #TODO(wwolf): this is temporary until we get rid of toprettyxml
+ # in the base class (XMLDictSerializer), which I plan to do in
+ # another branch
+ def to_xml_string(self, node, has_atom=False):
+ self._add_xmlns(node, has_atom)
+ return node.toxml(encoding='UTF-8')
+
def __init__(self, metadata=None, xmlns=None):
+ self.metadata = metadata or {}
if not xmlns:
- self.xmlns = ATOM_XMLNS
+ self.xmlns = wsgi.XMLNS_ATOM
else:
self.xmlns = xmlns
@@ -135,8 +269,33 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
link_href = link_href.rstrip('/')
return link_href.rsplit('/', 1)[0] + '/'
- def _create_meta(self, root, versions):
- title = self._create_text_elem('title', 'Available API Versions',
+ def _create_detail_meta(self, root, version):
+ title = self._create_text_elem('title', "About This Version",
+ type='text')
+
+ updated = self._create_text_elem('updated', version['updated'])
+
+ uri = version['links'][0]['href']
+ id = self._create_text_elem('id', uri)
+
+ link = self._xml_doc.createElement('link')
+ link.setAttribute('rel', 'self')
+ link.setAttribute('href', uri)
+
+ 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(author)
+ root.appendChild(link)
+
+ def _create_list_meta(self, root, versions):
+ title = self._create_text_elem('title', "Available API Versions",
type='text')
# Set this updated to the most recently updated version
recent = self._get_most_recent_update(versions)
@@ -144,6 +303,7 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
base_url = self._get_base_url(versions[0]['links'][0]['href'])
id = self._create_text_elem('id', base_url)
+
link = self._xml_doc.createElement('link')
link.setAttribute('rel', 'self')
link.setAttribute('href', base_url)
@@ -178,7 +338,10 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
link_node = self._xml_doc.createElement('link')
link_node.setAttribute('rel', link['rel'])
link_node.setAttribute('href', link['href'])
- entry.appendChild(link_node)
+ if 'type' in link:
+ link_node.setAttribute('type', link['type'])
+
+ entry.appendChild(link_node)
content = self._create_text_elem('content',
'Version %s %s (%s)' %
@@ -190,10 +353,45 @@ class VersionsAtomSerializer(wsgi.XMLDictSerializer):
entry.appendChild(content)
root.appendChild(entry)
- def default(self, data):
+ def index(self, data):
self._xml_doc = minidom.Document()
node = self._xml_doc.createElementNS(self.xmlns, 'feed')
- self._create_meta(node, data['versions'])
+ self._create_list_meta(node, data['versions'])
self._create_version_entries(node, data['versions'])
return self.to_xml_string(node)
+
+ def show(self, data):
+ self._xml_doc = minidom.Document()
+ node = self._xml_doc.createElementNS(self.xmlns, 'feed')
+ self._create_detail_meta(node, data['version'])
+ self._create_version_entries(node, [data['version']])
+
+ return self.to_xml_string(node)
+
+
+class VersionsHeadersSerializer(wsgi.ResponseHeadersSerializer):
+ def multi(self, response, data):
+ response.status_int = 300
+
+
+def create_resource(version='1.0'):
+ controller = {
+ '1.0': VersionV10,
+ '1.1': VersionV11,
+ }[version]()
+
+ body_serializers = {
+ 'application/xml': VersionsXMLSerializer(),
+ 'application/atom+xml': VersionsAtomSerializer(),
+ }
+ serializer = wsgi.ResponseSerializer(body_serializers)
+
+ supported_content_types = ('application/json',
+ 'application/xml',
+ 'application/atom+xml')
+ deserializer = wsgi.RequestDeserializer(
+ supported_content_types=supported_content_types)
+
+ return wsgi.Resource(controller, serializer=serializer,
+ deserializer=deserializer)
diff --git a/nova/api/openstack/views/versions.py b/nova/api/openstack/views/versions.py
index 9fa8f49dc..547289034 100644
--- a/nova/api/openstack/views/versions.py
+++ b/nova/api/openstack/views/versions.py
@@ -15,6 +15,7 @@
# License for the specific language governing permissions and limitations
# under the License.
+import copy
import os
@@ -31,16 +32,44 @@ class ViewBuilder(object):
"""
self.base_url = base_url
- def build(self, version_data):
- """Generic method used to generate a version entity."""
- version = {
- "id": version_data["id"],
- "status": version_data["status"],
- "updated": version_data["updated"],
- "links": self._build_links(version_data),
- }
+ def build_choices(self, VERSIONS, req):
+ version_objs = []
+ for version in VERSIONS:
+ version = VERSIONS[version]
+ version_objs.append({
+ "id": version['id'],
+ "status": version['status'],
+ "links": [
+ {
+ "rel": "self",
+ "href": self.generate_href(version['id'], req.path)
+ }
+ ],
+ "media-types": version['media-types']
+ })
- return version
+ return dict(choices=version_objs)
+
+ def build_versions(self, versions):
+ version_objs = []
+ for version in versions:
+ version = versions[version]
+ version_objs.append({
+ "id": version['id'],
+ "status": version['status'],
+ "updated": version['updated'],
+ "links": self._build_links(version),
+ })
+
+ return dict(versions=version_objs)
+
+ def build_version(self, version):
+ reval = copy.deepcopy(version)
+ reval['links'].insert(0, {
+ "rel": "self",
+ "href": self.base_url.rstrip('/') + '/',
+ })
+ return dict(version=reval)
def _build_links(self, version_data):
"""Generate a container of links that refer to the provided version."""
@@ -55,6 +84,11 @@ class ViewBuilder(object):
return links
- def generate_href(self, version_number):
+ def generate_href(self, version_number, path=None):
"""Create an url that refers to a specific version_number."""
- return os.path.join(self.base_url, version_number) + '/'
+ version_number = version_number.strip('/')
+ if path:
+ path = path.strip('/')
+ return os.path.join(self.base_url, version_number, path)
+ else:
+ return os.path.join(self.base_url, version_number) + '/'
diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py
index 53dab22e8..0eb47044e 100644
--- a/nova/api/openstack/wsgi.py
+++ b/nova/api/openstack/wsgi.py
@@ -13,6 +13,7 @@ from nova import wsgi
XMLNS_V10 = 'http://docs.rackspacecloud.com/servers/api/v1.0'
XMLNS_V11 = 'http://docs.openstack.org/compute/api/v1.1'
+
XMLNS_ATOM = 'http://www.w3.org/2005/Atom'
LOG = logging.getLogger('nova.api.openstack.wsgi')
@@ -386,6 +387,8 @@ class XMLDictSerializer(DictSerializer):
link_node = xml_doc.createElement('atom:link')
link_node.setAttribute('rel', link['rel'])
link_node.setAttribute('href', link['href'])
+ if 'type' in link:
+ link_node.setAttribute('type', link['type'])
link_nodes.append(link_node)
return link_nodes
diff --git a/nova/tests/api/openstack/test_versions.py b/nova/tests/api/openstack/test_versions.py
index da964ee1f..e68455778 100644
--- a/nova/tests/api/openstack/test_versions.py
+++ b/nova/tests/api/openstack/test_versions.py
@@ -16,21 +16,92 @@
# under the License.
import json
+import stubout
import webob
+import xml.etree.ElementTree
+
from nova import context
from nova import test
from nova.tests.api.openstack import fakes
from nova.api.openstack import versions
from nova.api.openstack import views
+from nova.api.openstack import wsgi
+
+VERSIONS = {
+ "v1.0": {
+ "id": "v1.0",
+ "status": "DEPRECATED",
+ "updated": "2011-01-21T11:33:21Z",
+ "links": [
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.0/cs-devguide-20110125.pdf"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.0/application.wadl"
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.0+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.0+json"
+ }
+ ],
+ },
+ "v1.1": {
+ "id": "v1.1",
+ "status": "CURRENT",
+ "updated": "2011-01-21T11:33:21Z",
+ "links": [
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.1/cs-devguide-20110125.pdf"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.1/application.wadl"
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.1+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.1+json"
+ }
+ ],
+ },
+}
class VersionsTest(test.TestCase):
def setUp(self):
super(VersionsTest, self).setUp()
self.context = context.get_admin_context()
+ self.stubs = stubout.StubOutForTesting()
+ fakes.stub_out_auth(self.stubs)
+ #Stub out VERSIONS
+ self.old_versions = versions.VERSIONS
+ versions.VERSIONS = VERSIONS
def tearDown(self):
+ versions.VERSIONS = self.old_versions
super(VersionsTest, self).tearDown()
def test_get_version_list(self):
@@ -44,7 +115,7 @@ class VersionsTest(test.TestCase):
{
"id": "v1.1",
"status": "CURRENT",
- "updated": "2011-07-18T11:30:00Z",
+ "updated": "2011-01-21T11:33:21Z",
"links": [
{
"rel": "self",
@@ -54,7 +125,7 @@ class VersionsTest(test.TestCase):
{
"id": "v1.0",
"status": "DEPRECATED",
- "updated": "2010-10-09T11:30:00Z",
+ "updated": "2011-01-21T11:33:21Z",
"links": [
{
"rel": "self",
@@ -64,6 +135,183 @@ class VersionsTest(test.TestCase):
]
self.assertEqual(versions, expected)
+ def test_get_version_1_0_detail(self):
+ req = webob.Request.blank('/v1.0/')
+ req.accept = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res.content_type, "application/json")
+ version = json.loads(res.body)
+ expected = {
+ "version": {
+ "id": "v1.0",
+ "status": "DEPRECATED",
+ "updated": "2011-01-21T11:33:21Z",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.0/"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.0/cs-devguide-20110125.pdf"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.0/application.wadl"
+ }
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/"
+ "vnd.openstack.compute-v1.0+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/"
+ "vnd.openstack.compute-v1.0+json"
+ }
+ ]
+ }
+ }
+ self.assertEqual(expected, version)
+
+ def test_get_version_1_1_detail(self):
+ req = webob.Request.blank('/v1.1/')
+ req.accept = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res.content_type, "application/json")
+ version = json.loads(res.body)
+ expected = {
+ "version": {
+ "id": "v1.1",
+ "status": "CURRENT",
+ "updated": "2011-01-21T11:33:21Z",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.1/cs-devguide-20110125.pdf"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.1/application.wadl"
+ }
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/"
+ "vnd.openstack.compute-v1.1+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/"
+ "vnd.openstack.compute-v1.1+json"
+ }
+ ]
+ }
+ }
+ self.assertEqual(expected, version)
+
+ def test_get_version_1_0_detail_xml(self):
+ req = webob.Request.blank('/v1.0/')
+ req.accept = "application/xml"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res.content_type, "application/xml")
+ root = xml.etree.ElementTree.XML(res.body)
+ self.assertEqual(root.tag.split('}')[1], "version")
+ self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11)
+
+ children = list(root)
+ media_types = children[0]
+ media_type_nodes = list(media_types)
+ links = (children[1], children[2], children[3])
+
+ self.assertEqual(media_types.tag.split('}')[1], 'media-types')
+ for media_node in media_type_nodes:
+ self.assertEqual(media_node.tag.split('}')[1], 'media-type')
+
+ expected = """
+ <version id="v1.0" status="DEPRECATED"
+ updated="2011-01-21T11:33:21Z"
+ xmlns="%s"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+
+ <media-types>
+ <media-type base="application/xml"
+ type="application/vnd.openstack.compute-v1.0+xml"/>
+ <media-type base="application/json"
+ type="application/vnd.openstack.compute-v1.0+json"/>
+ </media-types>
+
+ <atom:link href="http://localhost/v1.0/"
+ rel="self"/>
+
+ <atom:link href="http://docs.rackspacecloud.com/servers/
+ api/v1.0/cs-devguide-20110125.pdf"
+ rel="describedby"
+ type="application/pdf"/>
+
+ <atom:link href="http://docs.rackspacecloud.com/servers/
+ api/v1.0/application.wadl"
+ rel="describedby"
+ type="application/vnd.sun.wadl+xml"/>
+ </version>""".replace(" ", "").replace("\n", "") % wsgi.XMLNS_V11
+
+ actual = res.body.replace(" ", "").replace("\n", "")
+ self.assertEqual(expected, actual)
+
+ def test_get_version_1_1_detail_xml(self):
+ req = webob.Request.blank('/v1.1/')
+ req.accept = "application/xml"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual(res.content_type, "application/xml")
+ expected = """
+ <version id="v1.1" status="CURRENT"
+ updated="2011-01-21T11:33:21Z"
+ xmlns="%s"
+ xmlns:atom="http://www.w3.org/2005/Atom">
+
+ <media-types>
+ <media-type base="application/xml"
+ type="application/vnd.openstack.compute-v1.1+xml"/>
+ <media-type base="application/json"
+ type="application/vnd.openstack.compute-v1.1+json"/>
+ </media-types>
+
+ <atom:link href="http://localhost/v1.1/"
+ rel="self"/>
+
+ <atom:link href="http://docs.rackspacecloud.com/servers/
+ api/v1.1/cs-devguide-20110125.pdf"
+ rel="describedby"
+ type="application/pdf"/>
+
+ <atom:link href="http://docs.rackspacecloud.com/servers/
+ api/v1.1/application.wadl"
+ rel="describedby"
+ type="application/vnd.sun.wadl+xml"/>
+ </version>""".replace(" ", "").replace("\n", "") % wsgi.XMLNS_V11
+
+ actual = res.body.replace(" ", "").replace("\n", "")
+ self.assertEqual(expected, actual)
+
def test_get_version_list_xml(self):
req = webob.Request.blank('/')
req.accept = "application/xml"
@@ -71,18 +319,94 @@ class VersionsTest(test.TestCase):
self.assertEqual(res.status_int, 200)
self.assertEqual(res.content_type, "application/xml")
- expected = """<versions>
- <version id="v1.1" status="CURRENT" updated="2011-07-18T11:30:00Z">
+ expected = """
+ <versions xmlns="%s" xmlns:atom="%s">
+ <version id="v1.1" status="CURRENT" updated="2011-01-21T11:33:21Z">
<atom:link href="http://localhost/v1.1/" rel="self"/>
</version>
<version id="v1.0" status="DEPRECATED"
- updated="2010-10-09T11:30:00Z">
+ updated="2011-01-21T11:33:21Z">
<atom:link href="http://localhost/v1.0/" rel="self"/>
</version>
- </versions>""".replace(" ", "").replace("\n", "")
+ </versions>""".replace(" ", "").replace("\n", "") % (wsgi.XMLNS_V11,
+ wsgi.XMLNS_ATOM)
+
+ actual = res.body.replace(" ", "").replace("\n", "")
+
+ self.assertEqual(expected, actual)
+
+ def test_get_version_1_0_detail_atom(self):
+ req = webob.Request.blank('/v1.0/')
+ req.accept = "application/atom+xml"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual("application/atom+xml", res.content_type)
+ expected = """
+ <feed xmlns="http://www.w3.org/2005/Atom">
+ <title type="text">About This Version</title>
+ <updated>2011-01-21T11:33:21Z</updated>
+ <id>http://localhost/v1.0/</id>
+ <author>
+ <name>Rackspace</name>
+ <uri>http://www.rackspace.com/</uri>
+ </author>
+ <link href="http://localhost/v1.0/" rel="self"/>
+ <entry>
+ <id>http://localhost/v1.0/</id>
+ <title type="text">Version v1.0</title>
+ <updated>2011-01-21T11:33:21Z</updated>
+ <link href="http://localhost/v1.0/"
+ rel="self"/>
+ <link href="http://docs.rackspacecloud.com/servers/
+ api/v1.0/cs-devguide-20110125.pdf"
+ rel="describedby" type="application/pdf"/>
+ <link href="http://docs.rackspacecloud.com/servers/
+ api/v1.0/application.wadl"
+ rel="describedby" type="application/vnd.sun.wadl+xml"/>
+ <content type="text">
+ Version v1.0 DEPRECATED (2011-01-21T11:33:21Z)
+ </content>
+ </entry>
+ </feed>""".replace(" ", "").replace("\n", "")
actual = res.body.replace(" ", "").replace("\n", "")
+ self.assertEqual(expected, actual)
+
+ def test_get_version_1_1_detail_atom(self):
+ req = webob.Request.blank('/v1.1/')
+ req.accept = "application/atom+xml"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 200)
+ self.assertEqual("application/atom+xml", res.content_type)
+ expected = """
+ <feed xmlns="http://www.w3.org/2005/Atom">
+ <title type="text">About This Version</title>
+ <updated>2011-01-21T11:33:21Z</updated>
+ <id>http://localhost/v1.1/</id>
+ <author>
+ <name>Rackspace</name>
+ <uri>http://www.rackspace.com/</uri>
+ </author>
+ <link href="http://localhost/v1.1/" rel="self"/>
+ <entry>
+ <id>http://localhost/v1.1/</id>
+ <title type="text">Version v1.1</title>
+ <updated>2011-01-21T11:33:21Z</updated>
+ <link href="http://localhost/v1.1/"
+ rel="self"/>
+ <link href="http://docs.rackspacecloud.com/servers/
+ api/v1.1/cs-devguide-20110125.pdf"
+ rel="describedby" type="application/pdf"/>
+ <link href="http://docs.rackspacecloud.com/servers/
+ api/v1.1/application.wadl"
+ rel="describedby" type="application/vnd.sun.wadl+xml"/>
+ <content type="text">
+ Version v1.1 CURRENT (2011-01-21T11:33:21Z)
+ </content>
+ </entry>
+ </feed>""".replace(" ", "").replace("\n", "")
+ actual = res.body.replace(" ", "").replace("\n", "")
self.assertEqual(expected, actual)
def test_get_version_list_atom(self):
@@ -95,7 +419,7 @@ class VersionsTest(test.TestCase):
expected = """
<feed xmlns="http://www.w3.org/2005/Atom">
<title type="text">Available API Versions</title>
- <updated>2011-07-18T11:30:00Z</updated>
+ <updated>2011-01-21T11:33:21Z</updated>
<id>http://localhost/</id>
<author>
<name>Rackspace</name>
@@ -105,19 +429,19 @@ class VersionsTest(test.TestCase):
<entry>
<id>http://localhost/v1.1/</id>
<title type="text">Version v1.1</title>
- <updated>2011-07-18T11:30:00Z</updated>
+ <updated>2011-01-21T11:33:21Z</updated>
<link href="http://localhost/v1.1/" rel="self"/>
<content type="text">
- Version v1.1 CURRENT (2011-07-18T11:30:00Z)
+ Version v1.1 CURRENT (2011-01-21T11:33:21Z)
</content>
</entry>
<entry>
<id>http://localhost/v1.0/</id>
<title type="text">Version v1.0</title>
- <updated>2010-10-09T11:30:00Z</updated>
+ <updated>2011-01-21T11:33:21Z</updated>
<link href="http://localhost/v1.0/" rel="self"/>
<content type="text">
- Version v1.0 DEPRECATED (2010-10-09T11:30:00Z)
+ Version v1.0 DEPRECATED (2011-01-21T11:33:21Z)
</content>
</entry>
</feed>
@@ -127,28 +451,184 @@ class VersionsTest(test.TestCase):
self.assertEqual(expected, actual)
+ def test_multi_choice_image(self):
+ req = webob.Request.blank('/images/1')
+ req.accept = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 300)
+ self.assertEqual(res.content_type, "application/json")
+
+ expected = {
+ "choices": [
+ {
+ "id": "v1.1",
+ "status": "CURRENT",
+ "links": [
+ {
+ "href": "http://localhost/v1.1/images/1",
+ "rel": "self",
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.1+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.1+json"
+ },
+ ],
+ },
+ {
+ "id": "v1.0",
+ "status": "DEPRECATED",
+ "links": [
+ {
+ "href": "http://localhost/v1.0/images/1",
+ "rel": "self",
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.0+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.0+json"
+ },
+ ],
+ },
+ ], }
+
+ self.assertDictMatch(expected, json.loads(res.body))
+
+ def test_multi_choice_image_xml(self):
+ req = webob.Request.blank('/images/1')
+ req.accept = "application/xml"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 300)
+ self.assertEqual(res.content_type, "application/xml")
+
+ expected = """
+ <choices xmlns="%s" xmlns:atom="%s">
+ <version id="v1.1" status="CURRENT">
+ <media-types>
+ <media-type base="application/xml"
+ type="application/vnd.openstack.compute-v1.1+xml"/>
+ <media-type base="application/json"
+ type="application/vnd.openstack.compute-v1.1+json"/>
+ </media-types>
+ <atom:link href="http://localhost/v1.1/images/1" rel="self"/>
+ </version>
+ <version id="v1.0" status="DEPRECATED">
+ <media-types>
+ <media-type base="application/xml"
+ type="application/vnd.openstack.compute-v1.0+xml"/>
+ <media-type base="application/json"
+ type="application/vnd.openstack.compute-v1.0+json"/>
+ </media-types>
+ <atom:link href="http://localhost/v1.0/images/1" rel="self"/>
+ </version>
+ </choices>""".replace(" ", "").replace("\n", "") % (wsgi.XMLNS_V11,
+ wsgi.XMLNS_ATOM)
+
+ def test_multi_choice_server_atom(self):
+ """
+ Make sure multi choice responses do not have content-type
+ application/atom+xml (should use default of json)
+ """
+ req = webob.Request.blank('/servers/2')
+ req.accept = "application/atom+xml"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 300)
+ self.assertEqual(res.content_type, "application/json")
+
+ def test_multi_choice_server(self):
+ req = webob.Request.blank('/servers/2')
+ req.accept = "application/json"
+ res = req.get_response(fakes.wsgi_app())
+ self.assertEqual(res.status_int, 300)
+ self.assertEqual(res.content_type, "application/json")
+
+ expected = {
+ "choices": [
+ {
+ "id": "v1.1",
+ "status": "CURRENT",
+ "links": [
+ {
+ "href": "http://localhost/v1.1/servers/2",
+ "rel": "self",
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.1+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.1+json"
+ },
+ ],
+ },
+ {
+ "id": "v1.0",
+ "status": "DEPRECATED",
+ "links": [
+ {
+ "href": "http://localhost/v1.0/servers/2",
+ "rel": "self",
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.0+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.0+json"
+ },
+ ],
+ },
+ ], }
+
+ self.assertDictMatch(expected, json.loads(res.body))
+
+
+class VersionsViewBuilderTests(test.TestCase):
def test_view_builder(self):
base_url = "http://example.org/"
version_data = {
- "id": "3.2.1",
- "status": "CURRENT",
- "updated": "2011-07-18T11:30:00Z"}
+ "v3.2.1": {
+ "id": "3.2.1",
+ "status": "CURRENT",
+ "updated": "2011-07-18T11:30:00Z",
+ }
+ }
expected = {
- "id": "3.2.1",
- "status": "CURRENT",
- "updated": "2011-07-18T11:30:00Z",
- "links": [
+ "versions": [
{
- "rel": "self",
- "href": "http://example.org/3.2.1/",
- },
- ],
+ "id": "3.2.1",
+ "status": "CURRENT",
+ "updated": "2011-07-18T11:30:00Z",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://example.org/3.2.1/",
+ },
+ ],
+ }
+ ]
}
builder = views.versions.ViewBuilder(base_url)
- output = builder.build(version_data)
+ output = builder.build_versions(version_data)
self.assertEqual(output, expected)
@@ -163,7 +643,9 @@ class VersionsTest(test.TestCase):
self.assertEqual(actual, expected)
- def test_xml_serializer(self):
+
+class VersionsSerializerTests(test.TestCase):
+ def test_versions_list_xml_serializer(self):
versions_data = {
'versions': [
{
@@ -180,20 +662,137 @@ class VersionsTest(test.TestCase):
]
}
- expected = """
- <versions>
- <version id="2.7.1" status="DEPRECATED"
- updated="2011-07-18T11:30:00Z">
- <atom:link href="http://test/2.7.1" rel="self"/>
- </version>
- </versions>""".replace(" ", "").replace("\n", "")
+ serializer = versions.VersionsXMLSerializer()
+ response = serializer.index(versions_data)
+
+ root = xml.etree.ElementTree.XML(response)
+ self.assertEqual(root.tag.split('}')[1], "versions")
+ self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11)
+ version = list(root)[0]
+ self.assertEqual(version.tag.split('}')[1], "version")
+ self.assertEqual(version.get('id'),
+ versions_data['versions'][0]['id'])
+ self.assertEqual(version.get('status'),
+ versions_data['versions'][0]['status'])
+
+ link = list(version)[0]
+
+ self.assertEqual(link.tag.split('}')[1], "link")
+ self.assertEqual(link.tag.split('}')[0].strip('{'), wsgi.XMLNS_ATOM)
+ for key, val in versions_data['versions'][0]['links'][0].items():
+ self.assertEqual(link.get(key), val)
+
+ def test_versions_multi_xml_serializer(self):
+ versions_data = {
+ 'choices': [
+ {
+ "id": "2.7.1",
+ "updated": "2011-07-18T11:30:00Z",
+ "status": "DEPRECATED",
+ "media-types": VERSIONS['v1.1']['media-types'],
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://test/2.7.1/images",
+ },
+ ],
+ },
+ ]
+ }
serializer = versions.VersionsXMLSerializer()
- response = serializer.default(versions_data)
- response = response.replace(" ", "").replace("\n", "")
- self.assertEqual(expected, response)
+ response = serializer.multi(versions_data)
+
+ root = xml.etree.ElementTree.XML(response)
+ self.assertEqual(root.tag.split('}')[1], "choices")
+ self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11)
+ version = list(root)[0]
+ self.assertEqual(version.tag.split('}')[1], "version")
+ self.assertEqual(version.get('id'), versions_data['choices'][0]['id'])
+ self.assertEqual(version.get('status'),
+ versions_data['choices'][0]['status'])
+
+ media_types = list(version)[0]
+ media_type_nodes = list(media_types)
+ self.assertEqual(media_types.tag.split('}')[1], "media-types")
+
+ set_types = versions_data['choices'][0]['media-types']
+ for i, type in enumerate(set_types):
+ node = media_type_nodes[i]
+ self.assertEqual(node.tag.split('}')[1], "media-type")
+ for key, val in set_types[i].items():
+ self.assertEqual(node.get(key), val)
+
+ link = list(version)[1]
+
+ self.assertEqual(link.tag.split('}')[1], "link")
+ self.assertEqual(link.tag.split('}')[0].strip('{'), wsgi.XMLNS_ATOM)
+ for key, val in versions_data['choices'][0]['links'][0].items():
+ self.assertEqual(link.get(key), val)
+
+ def test_version_detail_xml_serializer(self):
+ version_data = {
+ "version": {
+ "id": "v1.0",
+ "status": "CURRENT",
+ "updated": "2011-01-21T11:33:21Z",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.0/"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.0/cs-devguide-20110125.pdf"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.0/application.wadl"
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.0+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.0+json"
+ }
+ ],
+ },
+ }
+
+ serializer = versions.VersionsXMLSerializer()
+ response = serializer.show(version_data)
+
+ root = xml.etree.ElementTree.XML(response)
+ self.assertEqual(root.tag.split('}')[1], "version")
+ self.assertEqual(root.tag.split('}')[0].strip('{'), wsgi.XMLNS_V11)
- def test_atom_serializer(self):
+ children = list(root)
+ media_types = children[0]
+ media_type_nodes = list(media_types)
+ links = (children[1], children[2], children[3])
+
+ self.assertEqual(media_types.tag.split('}')[1], 'media-types')
+ for i, media_node in enumerate(media_type_nodes):
+ self.assertEqual(media_node.tag.split('}')[1], 'media-type')
+ for key, val in version_data['version']['media-types'][i].items():
+ self.assertEqual(val, media_node.get(key))
+
+ for i, link in enumerate(links):
+ self.assertEqual(link.tag.split('}')[0].strip('{'),
+ 'http://www.w3.org/2005/Atom')
+ self.assertEqual(link.tag.split('}')[1], 'link')
+ for key, val in version_data['version']['links'][i].items():
+ self.assertEqual(val, link.get(key))
+
+ def test_versions_list_atom_serializer(self):
versions_data = {
'versions': [
{
@@ -210,45 +809,158 @@ class VersionsTest(test.TestCase):
]
}
- expected = """
- <feed xmlns="http://www.w3.org/2005/Atom">
- <title type="text">
- Available API Versions
- </title>
- <updated>
- 2011-07-20T11:40:00Z
- </updated>
- <id>
- http://test/
- </id>
- <author>
- <name>
- Rackspace
- </name>
- <uri>
- http://www.rackspace.com/
- </uri>
- </author>
- <link href="http://test/" rel="self"/>
- <entry>
- <id>
- http://test/2.9.8
- </id>
- <title type="text">
- Version 2.9.8
- </title>
- <updated>
- 2011-07-20T11:40:00Z
- </updated>
- <link href="http://test/2.9.8" rel="self"/>
- <content type="text">
- Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)
- </content>
- </entry>
- </feed>""".replace(" ", "").replace("\n", "")
+ serializer = versions.VersionsAtomSerializer()
+ response = serializer.index(versions_data)
+
+ root = xml.etree.ElementTree.XML(response)
+ self.assertEqual(root.tag.split('}')[1], "feed")
+ self.assertEqual(root.tag.split('}')[0].strip('{'),
+ "http://www.w3.org/2005/Atom")
+
+ children = list(root)
+ title = children[0]
+ updated = children[1]
+ id = children[2]
+ author = children[3]
+ link = children[4]
+ entry = children[5]
+
+ self.assertEqual(title.tag.split('}')[1], 'title')
+ self.assertEqual(title.text, 'Available API Versions')
+ self.assertEqual(updated.tag.split('}')[1], 'updated')
+ self.assertEqual(updated.text, '2011-07-20T11:40:00Z')
+ self.assertEqual(id.tag.split('}')[1], 'id')
+ self.assertEqual(id.text, 'http://test/')
+
+ self.assertEqual(author.tag.split('}')[1], 'author')
+ author_name = list(author)[0]
+ author_uri = list(author)[1]
+ self.assertEqual(author_name.tag.split('}')[1], 'name')
+ self.assertEqual(author_name.text, 'Rackspace')
+ self.assertEqual(author_uri.tag.split('}')[1], 'uri')
+ self.assertEqual(author_uri.text, 'http://www.rackspace.com/')
+
+ self.assertEqual(link.get('href'), 'http://test/')
+ self.assertEqual(link.get('rel'), 'self')
+
+ self.assertEqual(entry.tag.split('}')[1], 'entry')
+ entry_children = list(entry)
+ entry_id = entry_children[0]
+ entry_title = entry_children[1]
+ entry_updated = entry_children[2]
+ entry_link = entry_children[3]
+ entry_content = entry_children[4]
+ self.assertEqual(entry_id.tag.split('}')[1], "id")
+ self.assertEqual(entry_id.text, "http://test/2.9.8")
+ self.assertEqual(entry_title.tag.split('}')[1], "title")
+ self.assertEqual(entry_title.get('type'), "text")
+ self.assertEqual(entry_title.text, "Version 2.9.8")
+ self.assertEqual(entry_updated.tag.split('}')[1], "updated")
+ self.assertEqual(entry_updated.text, "2011-07-20T11:40:00Z")
+ self.assertEqual(entry_link.tag.split('}')[1], "link")
+ self.assertEqual(entry_link.get('href'), "http://test/2.9.8")
+ self.assertEqual(entry_link.get('rel'), "self")
+ self.assertEqual(entry_content.tag.split('}')[1], "content")
+ self.assertEqual(entry_content.get('type'), "text")
+ self.assertEqual(entry_content.text,
+ "Version 2.9.8 CURRENT (2011-07-20T11:40:00Z)")
+
+ def test_version_detail_atom_serializer(self):
+ versions_data = {
+ "version": {
+ "id": "v1.1",
+ "status": "CURRENT",
+ "updated": "2011-01-21T11:33:21Z",
+ "links": [
+ {
+ "rel": "self",
+ "href": "http://localhost/v1.1/"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/pdf",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.1/cs-devguide-20110125.pdf"
+ },
+ {
+ "rel": "describedby",
+ "type": "application/vnd.sun.wadl+xml",
+ "href": "http://docs.rackspacecloud.com/"
+ "servers/api/v1.1/application.wadl"
+ },
+ ],
+ "media-types": [
+ {
+ "base": "application/xml",
+ "type": "application/vnd.openstack.compute-v1.1+xml"
+ },
+ {
+ "base": "application/json",
+ "type": "application/vnd.openstack.compute-v1.1+json"
+ }
+ ],
+ },
+ }
serializer = versions.VersionsAtomSerializer()
- response = serializer.default(versions_data)
- print response
- response = response.replace(" ", "").replace("\n", "")
- self.assertEqual(expected, response)
+ response = serializer.show(versions_data)
+
+ root = xml.etree.ElementTree.XML(response)
+ self.assertEqual(root.tag.split('}')[1], "feed")
+ self.assertEqual(root.tag.split('}')[0].strip('{'),
+ "http://www.w3.org/2005/Atom")
+
+ children = list(root)
+ title = children[0]
+ updated = children[1]
+ id = children[2]
+ author = children[3]
+ link = children[4]
+ entry = children[5]
+
+ self.assertEqual(root.tag.split('}')[1], 'feed')
+ self.assertEqual(title.tag.split('}')[1], 'title')
+ self.assertEqual(title.text, 'About This Version')
+ self.assertEqual(updated.tag.split('}')[1], 'updated')
+ self.assertEqual(updated.text, '2011-01-21T11:33:21Z')
+ self.assertEqual(id.tag.split('}')[1], 'id')
+ self.assertEqual(id.text, 'http://localhost/v1.1/')
+
+ self.assertEqual(author.tag.split('}')[1], 'author')
+ author_name = list(author)[0]
+ author_uri = list(author)[1]
+ self.assertEqual(author_name.tag.split('}')[1], 'name')
+ self.assertEqual(author_name.text, 'Rackspace')
+ self.assertEqual(author_uri.tag.split('}')[1], 'uri')
+ self.assertEqual(author_uri.text, 'http://www.rackspace.com/')
+
+ self.assertEqual(link.get('href'),
+ 'http://localhost/v1.1/')
+ self.assertEqual(link.get('rel'), 'self')
+
+ self.assertEqual(entry.tag.split('}')[1], 'entry')
+ entry_children = list(entry)
+ entry_id = entry_children[0]
+ entry_title = entry_children[1]
+ entry_updated = entry_children[2]
+ entry_links = (entry_children[3], entry_children[4], entry_children[5])
+ entry_content = entry_children[6]
+
+ self.assertEqual(entry_id.tag.split('}')[1], "id")
+ self.assertEqual(entry_id.text,
+ "http://localhost/v1.1/")
+ self.assertEqual(entry_title.tag.split('}')[1], "title")
+ self.assertEqual(entry_title.get('type'), "text")
+ self.assertEqual(entry_title.text, "Version v1.1")
+ self.assertEqual(entry_updated.tag.split('}')[1], "updated")
+ self.assertEqual(entry_updated.text, "2011-01-21T11:33:21Z")
+
+ for i, link in enumerate(versions_data["version"]["links"]):
+ self.assertEqual(entry_links[i].tag.split('}')[1], "link")
+ for key, val in versions_data["version"]["links"][i].items():
+ self.assertEqual(entry_links[i].get(key), val)
+
+ self.assertEqual(entry_content.tag.split('}')[1], "content")
+ self.assertEqual(entry_content.get('type'), "text")
+ self.assertEqual(entry_content.text,
+ "Version v1.1 CURRENT (2011-01-21T11:33:21Z)")