Revert "Return a descriptive error message for controllers"

This reverts commit 6512c71a49.

While this fixes the problem it doesn't do it in a comprehensive way.
The right way would be to implement real validation.

The reverted patch breaks on methods marked as deprecated because the
deprecated decorator is a function that accepts *args and **kwargs.
This bypasses the check and we go back to the same TypeError from the
original bug.

Related-Bug: #1149987
Change-Id: I0efbcce9abc23bf79eb970e441e5f7644b1c8453
This commit is contained in:
David Stanek 2013-12-02 21:16:58 +00:00
parent f72f369538
commit be069f646f
3 changed files with 3 additions and 222 deletions

View File

@ -20,7 +20,6 @@
"""Utility methods for working with WSGI servers.""" """Utility methods for working with WSGI servers."""
import inspect
import re import re
import routes.middleware import routes.middleware
@ -233,7 +232,6 @@ class Application(BaseApplication):
params = self._normalize_dict(params) params = self._normalize_dict(params)
try: try:
self._method_inspect(method, params)
result = method(context, **params) result = method(context, **params)
except exception.Unauthorized as e: except exception.Unauthorized as e:
LOG.warning( LOG.warning(
@ -243,9 +241,10 @@ class Application(BaseApplication):
except exception.Error as e: except exception.Error as e:
LOG.warning(e) LOG.warning(e)
return render_exception(e, user_locale=req.best_match_language()) return render_exception(e, user_locale=req.best_match_language())
except exception.ControllerArgsError as e: except TypeError as e:
LOG.exception(e) LOG.exception(e)
return render_exception(e, user_locale=req.best_match_language()) return render_exception(exception.ValidationError(e),
user_locale=req.best_match_language())
except Exception as e: except Exception as e:
LOG.exception(e) LOG.exception(e)
return render_exception(exception.UnexpectedError(exception=e), return render_exception(exception.UnexpectedError(exception=e),
@ -263,39 +262,6 @@ class Application(BaseApplication):
response_code = self._get_response_code(req) response_code = self._get_response_code(req)
return render_response(body=result, status=response_code) return render_response(body=result, status=response_code)
def _method_inspect(self, method, params):
"""Validates the number of params passed to controller methods.
A `ControllerArgsError` will be raised if:
1. Required arguments are missing
2. Unexpected keyword args are provided to a method not
expecting **kwargs
"""
params = set(params)
spec = inspect.getargspec(method)
if spec.defaults:
required_args = set(spec.args[:-len(spec.defaults)])
else:
required_args = set(spec.args)
required_args.discard('self') # provided by Python
required_args.discard('context') # provided by caller
err_args = {}
missing_required_args = required_args - params
if missing_required_args:
err_args['missing_required_args'] = missing_required_args
if not spec.keywords:
extra_params = params - set(spec.args)
if extra_params:
err_args['extra_params'] = extra_params
if err_args:
raise exception.ControllerArgsError(**err_args)
def _get_response_code(self, req): def _get_response_code(self, req):
req_method = req.environ['REQUEST_METHOD'] req_method = req.environ['REQUEST_METHOD']
controller = importutils.import_class('keystone.common.controller') controller = importutils.import_class('keystone.common.controller')

View File

@ -78,24 +78,6 @@ class ValidationTimeStampError(Error):
title = 'Bad Request' title = 'Bad Request'
class ControllerArgsError(ValidationError):
"""Raised when controller methods receive incorrect argumemnts.
"""
def _build_message(self, message, missing_required_args=None,
extra_params=None):
errors = []
for param in sorted(extra_params or []):
errors.append(_('%s is not allowed.') % param)
for arg in sorted(missing_required_args or []):
errors.append(_('%s is required.') % arg)
return u' '.join(str(e) for e in errors)
class StringLengthExceeded(ValidationError): class StringLengthExceeded(ValidationError):
message_format = _("String length exceeded.The length of" message_format = _("String length exceeded.The length of"
" string '%(string)s' exceeded the limit" " string '%(string)s' exceeded the limit"

View File

@ -18,8 +18,6 @@ import uuid
from babel import localedata from babel import localedata
import gettext import gettext
from testtools import matchers
from keystone.common import wsgi from keystone.common import wsgi
from keystone import exception from keystone import exception
@ -316,168 +314,3 @@ class LocalizedResponseTest(tests.TestCase):
# are lazy-translated. # are lazy-translated.
self.assertIsInstance(_('The resource could not be found.'), self.assertIsInstance(_('The resource could not be found.'),
gettextutils.Message) gettextutils.Message)
class TestControllerMethodInspection(tests.TestCase):
def setUp(self):
super(TestControllerMethodInspection, self).setUp()
self.app = wsgi.Application()
self.inspect = self.app._method_inspect
def assertSuccess(self, func_user_test, params):
self.inspect(func_user_test, params)
def assertFailure(self, func_user_test, params,
expected_extra=None, expected_missing=None):
# NOTE(dstanek): In Python2.7+ assertRaises can be used as a context
# manager. This would make the code much cleaner.
try:
self.inspect(func_user_test, params)
except exception.ControllerArgsError as e:
message = str(e)
for param in expected_extra or []:
expected_msg = '%s is not allowed.' % param
self.assertThat(message, matchers.Contains(expected_msg))
for param in expected_missing or []:
expected_msg = '%s is required.' % param
self.assertThat(message, matchers.Contains(expected_msg))
else:
raise self.failureException('ControllerArgsError not raised')
def test_all_required_parameters_provided(self):
def func_under_test(a):
pass
self.assertSuccess(func_under_test, params=['a'])
def func_under_test(a, b):
pass
self.assertSuccess(func_under_test, params=['a', 'b'])
def test_optional_parameters_provided(self):
def func_under_test(a=None):
pass
self.assertSuccess(func_under_test, params=['a'])
def test_optional_parameters_not_provided(self):
def func_under_test(a=None):
pass
self.assertSuccess(func_under_test, params=[])
def func_under_test(a, b=None):
pass
self.assertSuccess(func_under_test, params=['a'])
def test_some_required_parameters_missing(self):
def func_under_test(a, b):
pass
self.assertFailure(func_under_test, params=['b'],
expected_missing=['a'])
def test_extra_parameter_supplied(self):
def func_under_test(a):
pass
self.assertFailure(func_under_test, params=['a', 'b'],
expected_extra=['b'])
def test_extra_parameter_supplied_with_kwargs_defined(self):
def func_under_test(a, **kw):
pass
self.assertSuccess(func_under_test, params=['a', 'b'])
def test_some_required_parameters_missing_with_kwargs_defined(self):
def func_under_test(a, b, **kw):
pass
self.assertFailure(func_under_test, params=['a'],
expected_missing=['b'])
def test_a_method_works(self):
class AppUnderTest(object):
def index(self, a):
pass
self.assertSuccess(AppUnderTest().index, params=['a'])
class AppUnderTest(object):
def index(self, a, b):
pass
self.assertFailure(AppUnderTest().index, params=['a'],
expected_missing=['b'])
def test_auto_provided_params_are_ignored(self):
class AppUnderTest(object):
def index(self, context):
pass
self.assertSuccess(AppUnderTest().index, params=[])
class AppUnderTest(object):
def index(self, context, a):
pass
self.assertSuccess(AppUnderTest().index, params=['a'])
class TestWSGIControllerMethodInspection(BaseWSGITest):
class FakeAppWithArgs(wsgi.Application):
def index(self, context, arg0, arg1):
return arg0, arg1
def _execute_test(self, app, expected_body, params=None):
req = self._make_request()
if params:
req.environ['openstack.params'] = params
resp = req.get_response(app())
self.assertEqual(jsonutils.loads(resp.body), expected_body)
def test_controller_method_with_no_args(self):
class FakeApp(wsgi.Application):
def index(self, context):
return ['index']
self._execute_test(FakeApp, ['index'])
def test_controller_method_with_correct_args(self):
self._execute_test(self.FakeAppWithArgs, ['value0', 'value1'],
{'arg0': 'value0', 'arg1': 'value1'})
def test_controller_method_with_missing_arg(self):
expected_body = {
"error": {
"message": "arg1 is required.",
"code": 400,
"title": "Bad Request"
}
}
self._execute_test(self.FakeAppWithArgs, expected_body,
{'arg0': 'value0'})
def test_controller_method_with_multiple_errors(self):
expected_body = {
"error": {
"message": "arg3 is not allowed. "
"arg0 is required. "
"arg1 is required.",
"code": 400,
"title": "Bad Request"
}
}
self._execute_test(self.FakeAppWithArgs, expected_body,
{'arg3': 'value3'})
def test_controller_method_with_default_args(self):
class FakeApp(wsgi.Application):
def index(self, context, arg0, arg1='1'):
return arg0, arg1
self._execute_test(FakeApp, ['0', '1'], {'arg0': '0'})