class Request(webob.Request):
"""Add some OpenStack API-specific logic to the base webob.Request."""
+ def __init__(self, *args, **kwargs):
+ super(Request, self).__init__(*args, **kwargs)
+ self._resource_cache = {}
+
+ def cache_resource(self, resource_to_cache, id_attribute='id', name=None):
+ """Cache the given resource.
+
+ Allow API methods to cache objects, such as results from a DB query,
+ to be used by API extensions within the same API request.
+
+ The resource_to_cache can be a list or an individual resource,
+ but ultimately resources are cached individually using the given
+ id_attribute.
+
+ Different resources types might need to be cached during the same
+ request, they can be cached using the name parameter. For example:
+
+ Controller 1:
+ request.cache_resource(db_volumes, 'volumes')
+ request.cache_resource(db_volume_types, 'types')
+ Controller 2:
+ db_volumes = request.cached_resource('volumes')
+ db_type_1 = request.cached_resource_by_id('1', 'types')
+
+ If no name is given, a default name will be used for the resource.
+
+ An instance of this class only lives for the lifetime of a
+ single API request, so there's no need to implement full
+ cache management.
+ """
+ if not isinstance(resource_to_cache, list):
+ resource_to_cache = [resource_to_cache]
+ if not name:
+ name = self.path
+ cached_resources = self._resource_cache.setdefault(name, {})
+ for resource in resource_to_cache:
+ cached_resources[resource[id_attribute]] = resource
+
+ def cached_resource(self, name=None):
+ """Get the cached resources cached under the given resource name.
+
+ Allow an API extension to get previously stored objects within
+ the same API request.
+
+ Note that the object data will be slightly stale.
+
+ :returns: a dict of id_attribute to the resource from the cached
+ resources, an empty map if an empty collection was cached,
+ or None if nothing has been cached yet under this name
+ """
+ if not name:
+ name = self.path
+ if name not in self._resource_cache:
+ # Nothing has been cached for this key yet
+ return None
+ return self._resource_cache[name]
+
+ def cached_resource_by_id(self, resource_id, name=None):
+ """Get a resource by ID cached under the given resource name.
+
+ Allow an API extension to get a previously stored object
+ within the same API request. This is basically a convenience method
+ to lookup by ID on the dictionary of all cached resources.
+
+ Note that the object data will be slightly stale.
+
+ :returns: the cached resource or None if the item is not in the cache
+ """
+ resources = self.cached_resource(name)
+ if not resources:
+ # Nothing has been cached yet for this key yet
+ return None
+ return resources.get(resource_id)
+
def best_match_content_type(self):
"""Determine the requested response content-type."""
if 'cinder.best_content_type' not in self.environ:
request.headers.pop('Accept-Language')
self.assertIsNone(request.best_match_language())
+ def test_cache_and_retrieve_resources(self):
+ request = wsgi.Request.blank('/foo')
+ # Test that trying to retrieve a cached object on
+ # an empty cache fails gracefully
+ self.assertIsNone(request.cached_resource())
+ self.assertIsNone(request.cached_resource_by_id('r-0'))
+
+ resources = []
+ for x in xrange(3):
+ resources.append({'id': 'r-%s' % x})
+
+ # Cache an empty list of resources using the default name
+ request.cache_resource([])
+ self.assertEqual({}, request.cached_resource())
+ self.assertIsNone(request.cached_resource('r-0'))
+ # Cache some resources
+ request.cache_resource(resources[:2])
+ # Cache one resource
+ request.cache_resource(resources[2])
+ # Cache a different resource name
+ other_resource = {'id': 'o-0'}
+ request.cache_resource(other_resource, name='other-resource')
+
+ self.assertEqual(resources[0], request.cached_resource_by_id('r-0'))
+ self.assertEqual(resources[1], request.cached_resource_by_id('r-1'))
+ self.assertEqual(resources[2], request.cached_resource_by_id('r-2'))
+ self.assertIsNone(request.cached_resource_by_id('r-3'))
+ self.assertEqual({'r-0': resources[0],
+ 'r-1': resources[1],
+ 'r-2': resources[2]}, request.cached_resource())
+ self.assertEqual(other_resource,
+ request.cached_resource_by_id('o-0',
+ name='other-resource'))
+
class ActionDispatcherTest(test.TestCase):
def test_dispatch(self):