From fefb88877c6d6f00626df747eb0172484c16f0ec Mon Sep 17 00:00:00 2001 From: Cole Robinson Date: Mon, 23 Jan 2012 11:29:24 -0500 Subject: extensions: Allow registering actions for create + delete This allows an extension to add new actions for create and delete operations to an existing collection. Currently when extending an existing collection (API namespace), an extension can register brand new 'actions', and extend any preexisting API functionality, but unfortunately create and delete are special cases that don't fall under the 'actions' classification, meaning the infrastructure can't handle them unless extending an existing impl. Stubbing out the create/delete methods in the original resource with the equiv of a NotImplementedError doesn't work, since 'extend'ing requires the original implementation to exit correctly. Whitelist 'create' and 'delete' and handle them appropriately. v2: Add myself to Authors Update wsgi.action docs Add a test case v3: Actually update Authors Change-Id: I550ae93c5e200f18644042ac81656bc76dbe8955 --- Authors | 1 + nova/api/openstack/wsgi.py | 15 +++++++++++---- nova/tests/api/openstack/test_wsgi.py | 29 +++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/Authors b/Authors index 6a80ade28..cb08b5b67 100644 --- a/Authors +++ b/Authors @@ -30,6 +30,7 @@ Chris Behrens Christian Berendt Christopher MacGown Chuck Short +Cole Robinson Cor Cornelisse Cory Wright Dan Prince diff --git a/nova/api/openstack/wsgi.py b/nova/api/openstack/wsgi.py index bcb64c98a..8a7a077b8 100644 --- a/nova/api/openstack/wsgi.py +++ b/nova/api/openstack/wsgi.py @@ -884,15 +884,19 @@ class Resource(wsgi.Application): else: meth = getattr(self.controller, action) except AttributeError as ex: - if action != 'action' or not self.wsgi_actions: + if (not self.wsgi_actions or + action not in ['action', 'create', 'delete']): # Propagate the error raise else: return meth, self.wsgi_extensions.get(action, []) - # OK, it's an action; figure out which action... - mtype = _MEDIA_TYPE_MAP.get(content_type) - action_name = self.action_peek[mtype](body) + if action == 'action': + # OK, it's an action; figure out which action... + mtype = _MEDIA_TYPE_MAP.get(content_type) + action_name = self.action_peek[mtype](body) + else: + action_name = action # Look up the action method return (self.wsgi_actions[action_name], @@ -908,6 +912,9 @@ def action(name): """Mark a function as an action. The given name will be taken as the action key in the body. + + This is also overloaded to allow extensions to provide + non-extending definitions of create and delete operations. """ def decorator(func): diff --git a/nova/tests/api/openstack/test_wsgi.py b/nova/tests/api/openstack/test_wsgi.py index 90d048443..3dfcb2388 100644 --- a/nova/tests/api/openstack/test_wsgi.py +++ b/nova/tests/api/openstack/test_wsgi.py @@ -516,6 +516,35 @@ class ResourceTest(test.TestCase): self.assertEqual(method, controller._action_foo) self.assertEqual(extensions, [extended._action_foo]) + def test_get_method_action_whitelist_extensions(self): + class Controller(wsgi.Controller): + def index(self, req, pants=None): + return pants + + class ControllerExtended(wsgi.Controller): + @wsgi.action('create') + def _create(self, req, body): + pass + + @wsgi.action('delete') + def _delete(self, req, id): + pass + + controller = Controller() + extended = ControllerExtended() + resource = wsgi.Resource(controller) + resource.register_actions(extended) + + method, extensions = resource.get_method(None, 'create', + 'application/json', + '{"create": true}') + self.assertEqual(method, extended._create) + self.assertEqual(extensions, []) + + method, extensions = resource.get_method(None, 'delete', None, None) + self.assertEqual(method, extended._delete) + self.assertEqual(extensions, []) + def test_pre_process_extensions_regular(self): class Controller(object): def index(self, req, pants=None): -- cgit