diff options
| author | Ziad Sawalha <github@highbridgellc.com> | 2011-12-21 22:46:32 -0600 |
|---|---|---|
| committer | Ziad Sawalha <github@highbridgellc.com> | 2011-12-22 13:21:42 -0600 |
| commit | 8a842139ddc78c7ccb7e93eb8749cba38359a078 (patch) | |
| tree | 304d26fff61d556ad8ccf3e17ea21f4a4d7a5a4a | |
| parent | 462af873c4e33a8209f9ee10c88f4a722b7bb8f7 (diff) | |
| download | keystone-8a842139ddc78c7ccb7e93eb8749cba38359a078.tar.gz keystone-8a842139ddc78c7ccb7e93eb8749cba38359a078.tar.xz keystone-8a842139ddc78c7ccb7e93eb8749cba38359a078.zip | |
Fixed version response (bug 891555 and bug 843052)
891555: Version response was supposed to be wrapped in a
'versions' element, instead there was only one version
being returned at the root.
Fixed and added tests to validate that.
843052: requests for version ATOM were ignoring the
atom+xml as a valid content-type. Fix required
adding a new atom template file and adding a new
mapping for the content-type/extension in the
URL normalizer.
Fixed and added tests to validate that.
Change-Id: I259c060e592e11cfc06f59ac5a540178c7f2def2
| -rw-r--r-- | keystone/content/admin/version.atom.tpl | 29 | ||||
| -rw-r--r-- | keystone/content/admin/version.json.tpl | 66 | ||||
| -rw-r--r-- | keystone/content/admin/version.xml.tpl | 44 | ||||
| -rw-r--r-- | keystone/content/service/version.atom.tpl | 29 | ||||
| -rw-r--r-- | keystone/content/service/version.json.tpl | 66 | ||||
| -rw-r--r-- | keystone/content/service/version.xml.tpl | 44 | ||||
| -rw-r--r-- | keystone/controllers/version.py | 4 | ||||
| -rw-r--r-- | keystone/middleware/url.py | 6 | ||||
| -rw-r--r-- | keystone/test/unit/test_controller_version.py | 139 | ||||
| -rw-r--r-- | keystone/test/unit/test_normalizingfilter.py | 6 | ||||
| -rwxr-xr-x | keystone/utils.py | 5 |
11 files changed, 322 insertions, 116 deletions
diff --git a/keystone/content/admin/version.atom.tpl b/keystone/content/admin/version.atom.tpl new file mode 100644 index 00000000..e39250d5 --- /dev/null +++ b/keystone/content/admin/version.atom.tpl @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feed xmlns="http://www.w3.org/2005/Atom"> + <title type="text">Available API Versions</title> + <updated>2010-12-22T00:00:00.00Z</updated> + <id>http://identity.api.openstack.org/</id> + <author><name>OpenStack</name><uri>http://www.openstack.org/</uri></author> + <link rel="self" href="http://identity.api.openstack.org/"/> + <entry> + <id>http://identity.api.openstack.org/v2.0/</id> + <title type="text">Version v2.0</title> + <updated>2011-09-30T00:00:00.00Z</updated> + <link rel="self" href="http://identity.api.openstack.org/v2.0/"/> + <content type="text">Version v2.0 BETA (2011-09-30T00:00:00.00Z)</content> + </entry> + <entry> + <id>http://identity.api.openstack.org/v1.1/</id> + <title type="text">Version v1.1</title> + <updated>2011-01-21T11:33:21-06:00</updated> + <link rel="self" href="http://identity.api.openstack.org/v1.1/"/> + <content type="text">Version v1.1 CURRENT (2011-01-21T11:33:21-06:00)</content> + </entry> + <entry> + <id>http://identity.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://identity.api.openstack.org/v1.0/"/> + <content type="text">Version v1.0 DEPRECATED (2009-10-09T11:30:00Z)</content> + </entry> +</feed> diff --git a/keystone/content/admin/version.json.tpl b/keystone/content/admin/version.json.tpl index c7ca3a42..112ba1b8 100644 --- a/keystone/content/admin/version.json.tpl +++ b/keystone/content/admin/version.json.tpl @@ -1,38 +1,32 @@ { - "version" : { - "id" : "v{{API_VERSION}}", - "status" : "{{API_VERSION_STATUS}}", - "updated" : "{{API_VERSION_DATE}}", - "links": [ - { - "rel" : "self", - "href" : "http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/" - }, - { - "rel" : "describedby", - "type" : "text/html", - "href" : "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" - }, - { - "rel" : "describedby", - "type" : "application/pdf", - "href" : "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" - }, - { - "rel" : "describedby", - "type" : "application/vnd.sun.wadl+xml", - "href" : "http://{{HOST}}:{{PORT}}/v2.0/identity-admin.wadl" - } - ], - "media-types": [ - { - "base" : "application/xml", - "type" : "application/vnd.openstack.identity-v{{API_VERSION}}+xml" - }, - { - "base" : "application/json", - "type" : "application/vnd.openstack.identity-v{{API_VERSION}}+json" - } - ] + "versions": { + "values": [{ + "id": "v{{API_VERSION}}", + "status": "{{API_VERSION_STATUS}}", + "updated": "{{API_VERSION_DATE}}", + "links": [{ + "rel": "self", + "href": "http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/" + }, { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" + }, { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" + }, { + "rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://{{HOST}}:{{PORT}}/v2.0/identity-admin.wadl" + }], + "media-types": [{ + "base": "application/xml", + "type": "application/vnd.openstack.identity-v{{API_VERSION}}+xml" + }, { + "base": "application/json", + "type": "application/vnd.openstack.identity-v{{API_VERSION}}+json" + }] + }] } -} +}
\ No newline at end of file diff --git a/keystone/content/admin/version.xml.tpl b/keystone/content/admin/version.xml.tpl index 5074d146..b62c9b6a 100644 --- a/keystone/content/admin/version.xml.tpl +++ b/keystone/content/admin/version.xml.tpl @@ -1,27 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> -<version xmlns="http://docs.openstack.org/common/api/v2.0" - xmlns:atom="http://www.w3.org/2005/Atom" - id="v{{API_VERSION}}" status="{{API_VERSION_STATUS}}" updated="{{API_VERSION_DATE}}"> +<versions xmlns="http://docs.openstack.org/common/api/v2.0" + xmlns:atom="http://www.w3.org/2005/Atom"> - <media-types> - <media-type base="application/xml" - type="application/vnd.openstack.identity-v{{API_VERSION}}+xml"/> - <media-type base="application/json" - type="application/vnd.openstack.identity-v{{API_VERSION}}+json"/> - </media-types> + <version id="v{{API_VERSION}}" status="{{API_VERSION_STATUS}}" updated="{{API_VERSION_DATE}}"> - <atom:link rel="self" - href="http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/"/> + <media-types> + <media-type base="application/xml" + type="application/vnd.openstack.identity-v{{API_VERSION}}+xml"/> + <media-type base="application/json" + type="application/vnd.openstack.identity-v{{API_VERSION}}+json"/> + </media-types> - <atom:link rel="describedby" - type="text/html" - href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" /> + <atom:link rel="self" + href="http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/"/> - <atom:link rel="describedby" - type="application/pdf" - href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" /> + <atom:link rel="describedby" + type="text/html" + href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" /> - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="http://{{HOST}}:{{PORT}}/v2.0/identity-admin.wadl" /> -</version> + <atom:link rel="describedby" + type="application/pdf" + href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" /> + + <atom:link rel="describedby" + type="application/vnd.sun.wadl+xml" + href="http://{{HOST}}:{{PORT}}/v2.0/identity-admin.wadl" /> + </version> +</versions>
\ No newline at end of file diff --git a/keystone/content/service/version.atom.tpl b/keystone/content/service/version.atom.tpl new file mode 100644 index 00000000..e39250d5 --- /dev/null +++ b/keystone/content/service/version.atom.tpl @@ -0,0 +1,29 @@ +<?xml version="1.0" encoding="UTF-8"?> +<feed xmlns="http://www.w3.org/2005/Atom"> + <title type="text">Available API Versions</title> + <updated>2010-12-22T00:00:00.00Z</updated> + <id>http://identity.api.openstack.org/</id> + <author><name>OpenStack</name><uri>http://www.openstack.org/</uri></author> + <link rel="self" href="http://identity.api.openstack.org/"/> + <entry> + <id>http://identity.api.openstack.org/v2.0/</id> + <title type="text">Version v2.0</title> + <updated>2011-09-30T00:00:00.00Z</updated> + <link rel="self" href="http://identity.api.openstack.org/v2.0/"/> + <content type="text">Version v2.0 BETA (2011-09-30T00:00:00.00Z)</content> + </entry> + <entry> + <id>http://identity.api.openstack.org/v1.1/</id> + <title type="text">Version v1.1</title> + <updated>2011-01-21T11:33:21-06:00</updated> + <link rel="self" href="http://identity.api.openstack.org/v1.1/"/> + <content type="text">Version v1.1 CURRENT (2011-01-21T11:33:21-06:00)</content> + </entry> + <entry> + <id>http://identity.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://identity.api.openstack.org/v1.0/"/> + <content type="text">Version v1.0 DEPRECATED (2009-10-09T11:30:00Z)</content> + </entry> +</feed> diff --git a/keystone/content/service/version.json.tpl b/keystone/content/service/version.json.tpl index 58d1fa4d..0eb824bf 100644 --- a/keystone/content/service/version.json.tpl +++ b/keystone/content/service/version.json.tpl @@ -1,38 +1,32 @@ { - "version" : { - "id" : "v{{API_VERSION}}", - "status" : "{{API_VERSION_STATUS}}", - "updated" : "{{API_VERSION_DATE}}", - "links": [ - { - "rel" : "self", - "href" : "http://{{HOST}}:{{PORT}}/v2.0/" - }, - { - "rel" : "describedby", - "type" : "text/html", - "href" : "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" - }, - { - "rel" : "describedby", - "type" : "application/pdf", - "href" : "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" - }, - { - "rel" : "describedby", - "type" : "application/vnd.sun.wadl+xml", - "href" : "http://{{HOST}}:{{PORT}}/v2.0/identity.wadl" - } - ], - "media-types": [ - { - "base" : "application/xml", - "type" : "application/vnd.openstack.identity-v2.0+xml" - }, - { - "base" : "application/json", - "type" : "application/vnd.openstack.identity-v2.0+json" - } - ] + "versions": { + "values": [{ + "id": "v{{API_VERSION}}", + "status": "{{API_VERSION_STATUS}}", + "updated": "{{API_VERSION_DATE}}", + "links": [{ + "rel": "self", + "href": "http://{{HOST}}:{{PORT}}/v2.0/" + }, { + "rel": "describedby", + "type": "text/html", + "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" + }, { + "rel": "describedby", + "type": "application/pdf", + "href": "http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" + }, { + "rel": "describedby", + "type": "application/vnd.sun.wadl+xml", + "href": "http://{{HOST}}:{{PORT}}/v2.0/identity.wadl" + }], + "media-types": [{ + "base": "application/xml", + "type": "application/vnd.openstack.identity-v2.0+xml" + }, { + "base": "application/json", + "type": "application/vnd.openstack.identity-v2.0+json" + }] + }] } -} +}
\ No newline at end of file diff --git a/keystone/content/service/version.xml.tpl b/keystone/content/service/version.xml.tpl index 1d0dcc19..30a46489 100644 --- a/keystone/content/service/version.xml.tpl +++ b/keystone/content/service/version.xml.tpl @@ -1,27 +1,29 @@ <?xml version="1.0" encoding="UTF-8"?> -<version xmlns="http://docs.openstack.org/common/api/v2.0" - xmlns:atom="http://www.w3.org/2005/Atom" - id="v{{API_VERSION}}" status="{{API_VERSION_STATUS}}" updated="{{API_VERSION_DATE}}"> +<versions xmlns="http://docs.openstack.org/common/api/v2.0" + xmlns:atom="http://www.w3.org/2005/Atom"> - <media-types> - <media-type base="application/xml" - type="application/vnd.openstack.identity-v{{API_VERSION}}+xml"/> - <media-type base="application/json" - type="application/vnd.openstack.identity-v{{API_VERSION}}+json"/> - </media-types> + <version id="v{{API_VERSION}}" status="{{API_VERSION_STATUS}}" updated="{{API_VERSION_DATE}}"> - <atom:link rel="self" - href="http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/"/> + <media-types> + <media-type base="application/xml" + type="application/vnd.openstack.identity-v{{API_VERSION}}+xml"/> + <media-type base="application/json" + type="application/vnd.openstack.identity-v{{API_VERSION}}+json"/> + </media-types> - <atom:link rel="describedby" - type="text/html" - href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" /> + <atom:link rel="self" + href="http://{{HOST}}:{{PORT}}/v{{API_VERSION}}/"/> - <atom:link rel="describedby" - type="application/pdf" - href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" /> + <atom:link rel="describedby" + type="text/html" + href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/content/" /> - <atom:link rel="describedby" - type="application/vnd.sun.wadl+xml" - href="http://{{HOST}}:{{PORT}}/v2.0/identity.wadl" /> -</version> + <atom:link rel="describedby" + type="application/pdf" + href="http://docs.openstack.org/api/openstack-identity-service/{{API_VERSION}}/identity-dev-guide-{{API_VERSION}}.pdf" /> + + <atom:link rel="describedby" + type="application/vnd.sun.wadl+xml" + href="http://{{HOST}}:{{PORT}}/v2.0/identity.wadl" /> + </version> +</versions>
\ No newline at end of file diff --git a/keystone/controllers/version.py b/keystone/controllers/version.py index 4a4f8645..30fe1c31 100644 --- a/keystone/controllers/version.py +++ b/keystone/controllers/version.py @@ -25,6 +25,10 @@ class VersionController(wsgi.Controller): resp_file = os.path.join(possible_topdir, "keystone/content/%s.xml.tpl" % file) resp.content_type = "application/xml" + elif utils.is_atom_response(req): + resp_file = os.path.join(possible_topdir, + "keystone/content/%s.atom.tpl" % file) + resp.content_type = "application/atom+xml" else: resp_file = os.path.join(possible_topdir, "keystone/content/%s.json.tpl" % file) diff --git a/keystone/middleware/url.py b/keystone/middleware/url.py index 4bc214e4..fa2f6183 100644 --- a/keystone/middleware/url.py +++ b/keystone/middleware/url.py @@ -38,7 +38,8 @@ PATH_PREFIXES = { # Maps supported URL extensions to RESPONSE_ENCODING PATH_SUFFIXES = { '.json': 'json', - '.xml': 'xml'} + '.xml': 'xml', + '.atom': 'atom+xml'} # Maps supported Accept headers to RESPONSE_ENCODING and API_VERSION ACCEPT_HEADERS = { @@ -49,7 +50,8 @@ ACCEPT_HEADERS = { 'application/vnd.openstack.identity-v1.0+json': ('json', '1.0'), 'application/vnd.openstack.identity-v1.0+xml': ('xml', '1.0'), 'application/json': ('json', None), - 'application/xml': ('xml', None)} + 'application/xml': ('xml', None), + 'application/atom+xml': ('atom+xml', None)} DEFAULT_RESPONSE_ENCODING = 'json' DEFAULT_API_VERSION = '2.0' diff --git a/keystone/test/unit/test_controller_version.py b/keystone/test/unit/test_controller_version.py new file mode 100644 index 00000000..c935185e --- /dev/null +++ b/keystone/test/unit/test_controller_version.py @@ -0,0 +1,139 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 +# Copyright (c) 2011 OpenStack, LLC. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import json +import logging +from lxml import etree +import unittest2 as unittest +from webob import Request + +from keystone.controllers.version import VersionController + +LOGGER = logging.getLogger(__name__) + + +class TestVersionController(unittest.TestCase): + def setUp(self): + self.controller = VersionController({}) + + def _default_version(self, file=None): + """ Verify default response for versions is JSON """ + if file is None: + file = 'admin/version' + req = Request.blank('/') + req.environ = {} + response = self.controller.get_version_info(req, file=file) + self.assertEqual(response.content_type, 'application/json') + data = json.loads(response.body) + self.assertIsNotNone(data) + + def _json_version(self, file=None): + """ Verify JSON response for versions + + Checks that JSON is returned when Accept is set to application/json. + Also checks that verions and version exist and that + values are as expected + + """ + if file is None: + file = 'admin/version' + req = Request.blank('/') + req.headers['Accept'] = 'application/json' + req.environ = {} + response = self.controller.get_version_info(req, file=file) + self.assertEqual(response.content_type, 'application/json') + data = json.loads(response.body) + self.assertIn("versions", data) + versions = data['versions'] + self.assertIn("values", versions) + values = versions['values'] + self.assertIsInstance(values, type([])) + for version in values: + for item in version: + self.assertIn(item, ["id", "status", "updated", "links", + "media-types"]) + + def _xml_version(self, file=None): + """ Verify XML response for versions + + Checks that XML is returned when Accept is set to application/xml. + Also checks that verions and version tags exist and that + attributes are as expected + + """ + if file is None: + file = 'admin/version' + req = Request.blank('/') + req.headers['Accept'] = 'application/xml' + req.environ = {} + response = self.controller.get_version_info(req, file=file) + self.assertEqual(response.content_type, 'application/xml') + data = etree.fromstring(response.body) + self.assertEqual(data.tag, + '{http://docs.openstack.org/common/api/v2.0}versions') + for version in data: + self.assertEqual(version.tag, + '{http://docs.openstack.org/common/api/v2.0}version') + for attribute in version.attrib: + self.assertIn(attribute, ["id", "status", "updated", "links", + "media-types"]) + + def _atom_version(self, file=None): + """ Verify ATOM response for versions + + Checks that ATOM XML is returned when Accept is set to + aapplication/atom+xml. + Also checks that verions and version tags exist and that + attributes are as expected + + """ + if file is None: + file = 'admin/version' + req = Request.blank('/') + req.headers['Accept'] = 'application/atom+xml' + req.environ = {} + response = self.controller.get_version_info(req, file=file) + self.assertEqual(response.content_type, 'application/atom+xml') + data = etree.fromstring(response.body) + self.assertEqual(data.tag, + '{http://www.w3.org/2005/Atom}feed') + + def test_default_version_admin(self): + self._default_version(file='admin/version') + + def test_default_version_service(self): + self._default_version(file='service/version') + + def test_xml_version_admin(self): + self._xml_version(file='admin/version') + + def test_xml_version_service(self): + self._xml_version(file='service/version') + + def test_json_version_admin(self): + self._json_version(file='admin/version') + + def test_json_version_service(self): + self._json_version(file='service/version') + + def test_atom_version_admin(self): + self._atom_version(file='admin/version') + + def test_atom_version_service(self): + self._atom_version(file='service/version') + +if __name__ == '__main__': + unittest.main() diff --git a/keystone/test/unit/test_normalizingfilter.py b/keystone/test/unit/test_normalizingfilter.py index e83af762..1b608729 100644 --- a/keystone/test/unit/test_normalizingfilter.py +++ b/keystone/test/unit/test_normalizingfilter.py @@ -60,6 +60,12 @@ class NormalizingFilterTest(unittest.TestCase): self.assertEqual('/someresource', env['PATH_INFO']) self.assertEqual('application/xml', env['HTTP_ACCEPT']) + def test_atom_extension(self): + env = {'PATH_INFO': '/v2.0/someresource.atom'} + self.filter(env, _start_response) + self.assertEqual('/someresource', env['PATH_INFO']) + self.assertEqual('application/atom+xml', env['HTTP_ACCEPT']) + def test_json_extension(self): env = {'PATH_INFO': '/v2.0/someresource.json'} self.filter(env, _start_response) diff --git a/keystone/utils.py b/keystone/utils.py index 440070de..2660451f 100755 --- a/keystone/utils.py +++ b/keystone/utils.py @@ -33,6 +33,11 @@ def is_json_response(req): return "Accept" in req.headers and "application/json" in req.accept +def is_atom_response(req): + """Returns True when the request wants an ATOM response, False otherwise""" + return "Accept" in req.headers and "application/atom+xml" in req.accept + + def get_app_root(): return os.path.abspath(os.path.dirname(__file__)) |
