From 4a638fab63c2a5a8a623c95337562f91b70f30a6 Mon Sep 17 00:00:00 2001 From: Mehdi Abaakouk Date: Wed, 30 Oct 2013 09:54:05 +0100 Subject: [PATCH] Refactor API error handling Refactor the API error handling to ensure error messages are translated. Since wsme 0.5b6, the error message doesn't need to be converted to unicode, wsme handles it for us. Related bug #1200518 Change-Id: Iec396f045cd3afe817301f0d00917c42c7587b55 --- ceilometer/api/controllers/v2.py | 53 +++++++++++---------------- ceilometer/tests/api/v2/test_query.py | 5 +++ 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/ceilometer/api/controllers/v2.py b/ceilometer/api/controllers/v2.py index 618eeb83b..314f8cd3e 100644 --- a/ceilometer/api/controllers/v2.py +++ b/ceilometer/api/controllers/v2.py @@ -67,7 +67,13 @@ state_kind_enum = wtypes.Enum(str, *state_kind) operation_kind = wtypes.Enum(str, 'lt', 'le', 'eq', 'ne', 'ge', 'gt') -class EntityNotFound(wsme.exc.ClientSideError): +class ClientSideError(wsme.exc.ClientSideError): + def __init__(self, error, status_code=400): + pecan.response.translatable_error = error + super(ClientSideError, self).__init__(error, status_code) + + +class EntityNotFound(ClientSideError): def __init__(self, entity, id): super(EntityNotFound, self).__init__( _("%(entity)s %(id)s Not Found") % {'entity': entity, @@ -99,14 +105,12 @@ class BoundedInt(wtypes.UserType): if self.min is not None and value < self.min: error = _('Value %(value)s is invalid (should be greater or equal ' 'to %(min)s)') % dict(value=value, min=self.min) - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) + raise ClientSideError(error) if self.max is not None and value > self.max: error = _('Value %(value)s is invalid (should be lower or equal ' 'to %(max)s)') % dict(value=value, max=self.max) - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) + raise ClientSideError(error) return value @@ -262,21 +266,21 @@ class Query(_Base): msg = _('Failed to convert the metadata value %(value)s' ' to the expected data type %(type)s.') % \ {'value': self.value, 'type': type} - raise wsme.exc.ClientSideError(unicode(msg)) + raise ClientSideError(msg) except TypeError: msg = _('The data type %s is not supported. The supported' ' data type list is: integer, float, boolean and' ' string.') % (type) - raise wsme.exc.ClientSideError(unicode(msg)) + raise ClientSideError(msg) except Exception: msg = _('Unexpected exception converting %(value)s to' ' the expected data type %(type)s.') % \ {'value': self.value, 'type': type} - raise wsme.exc.ClientSideError(unicode(msg)) + raise ClientSideError(msg) return converted_value -class ProjectNotAuthorized(wsme.exc.ClientSideError): +class ProjectNotAuthorized(ClientSideError): def __init__(self, id): super(ProjectNotAuthorized, self).__init__( _("Not Authorized to access project %s") % id, @@ -787,9 +791,7 @@ class MeterController(rest.RestController): period long of that number of seconds. """ if period and period < 0: - error = _("Period must be positive.") - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) + raise ClientSideError(_("Period must be positive.")) kwargs = _query_to_kwargs(q, storage.SampleFilter.__init__) kwargs['meter'] = self._id @@ -1158,14 +1160,12 @@ class Alarm(_Base): and alarm.combination_rule == wtypes.Unset): error = _("either threshold_rule or combination_rule " "must be set") - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) + raise ClientSideError(error) if alarm.threshold_rule and alarm.combination_rule: error = _("threshold_rule and combination_rule " "cannot be set at the same time") - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) + raise ClientSideError(error) if alarm.threshold_rule: # ensure an implicit constraint on project_id is added to @@ -1183,9 +1183,7 @@ class Alarm(_Base): alarms = list(pecan.request.storage_conn.get_alarms( alarm_id=id, project=project)) if not alarms: - error = _("Alarm %s doesn't exist") % id - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) + raise ClientSideError(_("Alarm %s doesn't exist") % id) return alarm @@ -1350,9 +1348,7 @@ class AlarmController(rest.RestController): alarm_in = storage.models.Alarm(**updated_alarm) except Exception: LOG.exception("Error while putting alarm: %s" % updated_alarm) - error = _("Alarm incorrect") - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) + raise ClientSideError(_("Alarm incorrect")) alarm = self.conn.update_alarm(alarm_in) @@ -1402,10 +1398,7 @@ class AlarmController(rest.RestController): # note(sileht): body are not validated by wsme # Workaround for https://bugs.launchpad.net/wsme/+bug/1227229 if state not in state_kind: - error = _("state invalid") - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) - + raise ClientSideError(_("state invalid")) now = timeutils.utcnow() alarm = self._alarm() alarm.state = state @@ -1488,17 +1481,13 @@ class AlarmsController(rest.RestController): alarms = list(conn.get_alarms(name=data.name, project=data.project_id)) if len(alarms) > 0: - error = _("Alarm with that name exists") - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) + raise ClientSideError(_("Alarm with that name exists")) try: alarm_in = storage.models.Alarm(**change) except Exception: LOG.exception("Error while posting alarm: %s" % change) - error = _("Alarm incorrect") - pecan.response.translatable_error = error - raise wsme.exc.ClientSideError(unicode(error)) + raise ClientSideError(_("Alarm incorrect")) alarm = conn.create_alarm(alarm_in) self._record_creation(conn, change, alarm.alarm_id, now) diff --git a/ceilometer/tests/api/v2/test_query.py b/ceilometer/tests/api/v2/test_query.py index 1d5cff532..ea8d81673 100644 --- a/ceilometer/tests/api/v2/test_query.py +++ b/ceilometer/tests/api/v2/test_query.py @@ -16,6 +16,7 @@ """Test the methods related to query.""" import datetime +import fixtures import mock import wsme @@ -29,6 +30,10 @@ from ceilometer.tests import base as tests_base class TestQuery(test.BaseTestCase): + def setUp(self): + super(TestQuery, self).setUp() + self.useFixture(fixtures.MonkeyPatch( + 'pecan.response', mock.MagicMock())) def test_get_value_as_type_with_integer(self): query = Query(field='metadata.size',