Add description to 404 and 409 error responses

It would be better if Zaqar could provide descriptions in it's 404 and
409 error response bodies.

Reasons:

1. RFC 7231 asks to do it:
   https://tools.ietf.org/html/rfc7231#section-6.5
2. Sometimes it might be hard for the user to figure out why Zaqar
   returns 409 response code without description in response body.
3. Zaqar already provides descriptions in these error responses:
   400 401 403 500 503 406
4. Keystone project is a good example of a project, which includes info
   in 404 responses. We can follow this example.

This patch makes it so.

Change-Id: I0d5c5b73498a40b5ec949ceb74f9bb7dcf925009
Closes-Bug: 1547258
This commit is contained in:
Eva Balycheva 2016-02-24 22:30:14 +03:00
parent 830c54dc55
commit d97327e95b
19 changed files with 77 additions and 47 deletions

View File

@ -65,3 +65,22 @@ class HTTPForbidden(falcon.HTTPForbidden):
def __init__(self):
super(HTTPForbidden, self).__init__(self.TITLE, self.DESCRIPTION)
class HTTPConflict(falcon.HTTPConflict):
"""Wraps falcon.HTTPConflict with contextual title."""
TITLE = _(u'Resource conflict')
def __init__(self, description, **kwargs):
super(HTTPConflict, self).__init__(self.TITLE, description, **kwargs)
class HTTPNotFound(falcon.HTTPNotFound):
"""Wraps falcon.HTTPConflict with contextual title."""
TITLE = _(u'Not found')
def __init__(self, description):
super(HTTPNotFound, self).__init__(title=self.TITLE,
description=description)

View File

@ -110,7 +110,7 @@ class ItemResource(Resource):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)
description = _(u'Claim could not be queried.')
@ -151,7 +151,7 @@ class ItemResource(Resource):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -98,7 +98,7 @@ class CollectionResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)
@ -159,7 +159,7 @@ class CollectionResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except storage_errors.MessageConflict as ex:
LOG.exception(ex)
@ -247,7 +247,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -45,7 +45,7 @@ class Resource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)
@ -78,8 +78,8 @@ class Resource(object):
LOG.debug(ex)
raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
except storage_errors.QueueDoesNotExist:
raise falcon.HTTPNotFound()
except storage_errors.QueueDoesNotExist as ex:
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -42,7 +42,6 @@ import six
from zaqar.common.api.schemas import pools as schema
from zaqar.common import utils as common_utils
from zaqar.i18n import _
from zaqar.storage import errors
from zaqar.storage import utils as storage_utils
from zaqar.transport import utils as transport_utils
@ -145,7 +144,7 @@ class Resource(object):
except errors.PoolDoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
data['href'] = request.path
@ -181,8 +180,7 @@ class Resource(object):
response.location = request.path
except errors.PoolAlreadyExists as e:
LOG.exception(e)
title = _(u'Unable to create pool')
raise falcon.HTTPConflict(title, six.text_type(e))
raise wsgi_errors.HTTPConflict(six.text_type(e))
def on_delete(self, request, response, project_id, pool):
"""Deregisters a pool.
@ -234,4 +232,4 @@ class Resource(object):
self._ctrl.update(pool, **fields)
except errors.PoolDoesNotExist as ex:
LOG.exception(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))

View File

@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import falcon
from oslo_log import log as logging
import six
from zaqar.i18n import _
from zaqar.storage import errors as storage_errors
@ -56,7 +56,7 @@ class Resource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -137,7 +137,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)
description = _(u'Claim could not be queried.')
@ -178,7 +178,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -16,6 +16,7 @@
import falcon
import jsonschema
from oslo_log import log
import six
from zaqar.common.api.schemas import flavors as schema
from zaqar.common import utils as common_utils
@ -123,7 +124,7 @@ class Resource(object):
except errors.FlavorDoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
data['href'] = request.path
@ -204,4 +205,4 @@ class Resource(object):
self._ctrl.update(flavor, project=project_id, **fields)
except errors.FlavorDoesNotExist as ex:
LOG.exception(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))

View File

@ -190,7 +190,7 @@ class CollectionResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except storage_errors.MessageConflict as ex:
LOG.exception(ex)
@ -226,7 +226,11 @@ class CollectionResource(object):
# NOTE(TheSriram): Trying to get a message by id, should
# return the message if its present, otherwise a 404 since
# the message might have been deleted.
resp.status = falcon.HTTP_404
msg = _(u'No messages with IDs: {ids} found in the queue {queue} '
u'for project {project}.')
description = msg.format(queue=queue_name, project=project_id,
ids=ids)
raise wsgi_errors.HTTPNotFound(description)
else:
resp.body = utils.to_json(response)
@ -308,7 +312,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -148,7 +148,7 @@ class Resource(object):
except errors.PoolDoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
data['href'] = request.path
@ -188,8 +188,7 @@ class Resource(object):
raise falcon.HTTPBadRequest(title, six.text_type(e))
except errors.PoolAlreadyExists as e:
LOG.exception(e)
title = _(u'Unable to create pool')
raise falcon.HTTPConflict(title, six.text_type(e))
raise wsgi_errors.HTTPConflict(six.text_type(e))
def on_delete(self, request, response, project_id, pool):
"""Deregisters a pool.
@ -251,4 +250,4 @@ class Resource(object):
self._ctrl.update(pool, **fields)
except errors.PoolDoesNotExist as ex:
LOG.exception(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))

View File

@ -46,7 +46,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import falcon
from oslo_log import log as logging
import six
from zaqar.i18n import _
from zaqar.storage import errors as storage_errors
@ -65,7 +65,7 @@ class Resource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -140,7 +140,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)
description = _(u'Claim could not be queried.')
@ -182,7 +182,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -16,6 +16,7 @@
import falcon
import jsonschema
from oslo_log import log
import six
from zaqar.common.api.schemas import flavors as schema
from zaqar.common import utils as common_utils
@ -137,7 +138,7 @@ class Resource(object):
except errors.FlavorDoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
data['href'] = request.path
@ -223,6 +224,6 @@ class Resource(object):
for cap in capabilities]
except errors.FlavorDoesNotExist as ex:
LOG.exception(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
resp_data['href'] = request.path
response.body = transport_utils.to_json(resp_data)

View File

@ -192,7 +192,7 @@ class CollectionResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except storage_errors.MessageConflict as ex:
LOG.exception(ex)
@ -229,7 +229,11 @@ class CollectionResource(object):
# NOTE(TheSriram): Trying to get a message by id, should
# return the message if its present, otherwise a 404 since
# the message might have been deleted.
resp.status = falcon.HTTP_404
msg = _(u'No messages with IDs: {ids} found in the queue {queue} '
u'for project {project}.')
description = msg.format(queue=queue_name, project=project_id,
ids=ids)
raise wsgi_errors.HTTPNotFound(description)
else:
resp.body = utils.to_json(response)
@ -313,7 +317,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -151,7 +151,7 @@ class Resource(object):
except errors.PoolDoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
data['href'] = request.path
@ -192,8 +192,7 @@ class Resource(object):
raise falcon.HTTPBadRequest(title, six.text_type(e))
except errors.PoolAlreadyExists as e:
LOG.exception(e)
title = _(u'Unable to create pool')
raise falcon.HTTPConflict(title, six.text_type(e))
raise falcon.HTTPConflict(six.text_type(e))
@acl.enforce("pools:delete")
def on_delete(self, request, response, project_id, pool):
@ -258,7 +257,7 @@ class Resource(object):
resp_data = self._ctrl.get(pool, False)
except errors.PoolDoesNotExist as ex:
LOG.exception(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
resp_data['href'] = request.path
response.body = transport_utils.to_json(resp_data)

View File

@ -47,7 +47,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -13,8 +13,8 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import falcon
from oslo_log import log as logging
import six
from zaqar.i18n import _
from zaqar.storage import errors as storage_errors
@ -65,7 +65,7 @@ class Resource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)

View File

@ -48,7 +48,7 @@ class ItemResource(object):
except storage_errors.DoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except Exception as ex:
LOG.exception(ex)
@ -90,7 +90,7 @@ class ItemResource(object):
resp.location = req.path
except storage_errors.SubscriptionDoesNotExist as ex:
LOG.debug(ex)
raise falcon.HTTPNotFound()
raise wsgi_errors.HTTPNotFound(six.text_type(ex))
except validation.ValidationFailed as ex:
LOG.debug(ex)
raise wsgi_errors.HTTPBadRequestAPI(six.text_type(ex))
@ -187,8 +187,13 @@ class CollectionResource(object):
description = _(u'Subscription could not be created.')
raise wsgi_errors.HTTPServiceUnavailable(description)
resp.status = falcon.HTTP_201 if created else falcon.HTTP_409
resp.location = req.path
if created:
resp.location = req.path
resp.status = falcon.HTTP_201
resp.body = utils.to_json(
{'subscription_id': six.text_type(created)})
else:
description = _(u'Such subscription already exists. Subscriptions '
u'are unique by project + queue + subscriber URI.')
raise wsgi_errors.HTTPConflict(description, headers={'location':
req.path})