Esempio n. 1
0
 def test_error_msg_format_custom_message(self):
     with self.assertRaises(httperror.HTTPError) as err:
         raise httperror.HTTPError(status=404, message="Nothing Here")
     self.assertEqual("404: Not Found - Nothing Here", str(err.exception))
Esempio n. 2
0
 def test_error_msg_format(self):
     with self.assertRaises(httperror.HTTPError) as err:
         raise httperror.HTTPError(status=404)
     self.assertEqual("404: Not Found", str(err.exception))
Esempio n. 3
0
    def call_api(self, event):
        """Make a request against the API defined by this app.

        Return any result from the API call as a JSON object/dict. In the event
        of a client or server error response from the API endpoint handler (4xx
        or 5xx), raise a :class:`fleece.httperror.HTTPError` containing some
        context about the error. Any unexpected exception encountered while
        executing an API endpoint handler will be appropriately raised as a
        generic 500 error with no error context (in order to not expose too
        much of the internals of the application).

        :param dict event:
            Dictionary containing the entire request payload passed to the
            Lambda function handler.
        :returns:
            JSON object (as a `list` or `dict`) containing the response data
            from the API endpoint.
        :raises:
            :class:`fleece.httperror.HTTPError` on 4xx or 5xx responses from
            the API endpoint handler, or a 500 response on an unexpected
            failure (due to a bug in handler code, for example).
        """
        try:
            environ = _build_wsgi_env(event, self.import_name)
            response = werkzeug.wrappers.Response.from_app(self, environ)
            response_dict = json.loads(response.get_data())

            if 400 <= response.status_code < 500:
                if ('error' in response_dict
                        and 'message' in response_dict['error']):
                    # FIXME(larsbutler): If 'error' is not a collection
                    # (list/dict) and is a scalar such as an int, the check
                    # `message in response_dict['error']` will blow up and
                    # result in a generic 500 error. This check assumes too
                    # much about the format of error responses given by the
                    # handler. It might be good to allow this handling to
                    # support custom structures.
                    msg = response_dict['error']['message']
                elif 'detail' in response_dict:
                    # Likely this is a generated 400 response from Connexion.
                    # Reveal the 'detail' of the message to the user.
                    # NOTE(larsbutler): If your API handler explicitly returns
                    # a 'detail' key in in the response, be aware that this
                    # will be exposed to the client.
                    msg = response_dict['detail']
                else:
                    # Respond with a generic client error.
                    msg = 'Client Error'
                # FIXME(larsbutler): The logic above still assumes a lot about
                # the API response. That's not great. It would be nice to make
                # this more flexible and explicit.
                self.logger.error(
                    'Raising 4xx error',
                    http_status=response.status_code,
                    message=msg,
                )
                raise httperror.HTTPError(
                    status=response.status_code,
                    message=msg,
                )
            elif 500 <= response.status_code < 600:
                if response_dict['title'] == RESPONSE_CONTRACT_VIOLATION:
                    # This case is generally enountered if the API endpoint
                    # handler code does not conform to the contract dictated by
                    # the Swagger specification.
                    self.logger.error(RESPONSE_CONTRACT_VIOLATION,
                                      detail=response_dict['detail'])
                else:
                    # This case will trigger if
                    # a) the handler code raises an unexpected exception
                    # or
                    # b) the handler code explicitly returns a 5xx error.
                    self.logger.error(
                        'Raising 5xx error',
                        response=response_dict,
                        http_status=response.status_code,
                    )
                raise httperror.HTTPError(status=response.status_code)
            else:
                return response_dict
        except httperror.HTTPError:
            self.logger.exception('HTTPError')
            raise
        except Exception:
            self.logger.exception('Unhandled exception')
            raise httperror.HTTPError(status=500)