Package coprs :: Module error_handlers
[hide private]
[frames] | no frames]

Source Code for Module coprs.error_handlers

  1  """ 
  2  A place for exception-handling logic 
  3  """ 
  4   
  5  import logging 
  6   
  7  import flask 
  8  from werkzeug.exceptions import HTTPException, NotFound, GatewayTimeout 
  9  from coprs.exceptions import CoprHttpException 
 10  from coprs.views.misc import ( 
 11      generic_error, 
 12      access_restricted, 
 13      bad_request_handler, 
 14      conflict_request_handler, 
 15      page_not_found, 
 16  ) 
 17   
 18  LOG = logging.getLogger(__name__) 
 19   
 20   
21 -def get_error_handler():
22 """ 23 Determine what error handler should be used for this request 24 See http://flask.pocoo.org/docs/1.0/blueprints/#error-handlers 25 """ 26 if flask.request.path.startswith('/api_3/'): 27 return APIErrorHandler() 28 return UIErrorHandler()
29 30
31 -class BaseErrorHandler:
32 """ 33 Do not use this class for handling errors. It is only a parent class for 34 the actual error-handler classes. 35 """ 36
37 - def handle_error(self, error):
38 """ 39 Return a flask response suitable for the current situation (e.g. reder 40 HTML page for UI failures, send JSON back to API client, etc). 41 42 This method is expected to be implemented in descendants of this class. 43 """ 44 raise NotImplementedError
45
46 - def code(self, error): # pylint: disable=no-self-use
47 """ 48 Return status code for a given exception 49 """ 50 return getattr(error, "code", 500)
51
52 - def message(self, error): # pylint: disable=no-self-use
53 """ 54 Return an error message for a given exception. We want to obtain messages 55 differently for `CoprHttpException`, `HTTPException`, or others. 56 """ 57 if isinstance(error, HTTPException): 58 return error.description 59 return str(error) 60 61
62 -class UIErrorHandler(BaseErrorHandler):
63 """ 64 Handle exceptions raised from the web user interface 65 """ 66
67 - def handle_error(self, error):
68 code = self.code(error) 69 message = self.message(error) 70 71 # The most common error has their own custom error pages. When catching 72 # a new exception, try to keep it simple and just the the generic one. 73 # Create it's own view only if necessary. 74 error_views = { 75 400: bad_request_handler, 76 403: access_restricted, 77 404: page_not_found, 78 409: conflict_request_handler, 79 } 80 if code in error_views: 81 return error_views[code](message) 82 83 LOG.exception("Admin-only exception") 84 return generic_error("Server error, contact admin", code)
85 86
87 -class APIErrorHandler(BaseErrorHandler):
88 """ 89 Handle exceptions raised from API (v3) 90 """ 91
92 - def handle_error(self, error):
93 code = self.code(error) 94 message = self.message(error) 95 96 # In the majority of cases, we want to return the message that was 97 # passed through an exception, but occasionally we want to redefine the 98 # message to some API-related one. Please try to keep it simple and 99 # do this only if necessary. 100 errors = { 101 NotFound: "Such API endpoint doesn't exist", 102 GatewayTimeout: "The API request timeouted", 103 } 104 if error.__class__ in errors: 105 message = errors[error.__class__] 106 107 # Every `CoprHttpException` and `HTTPException` failure has valuable 108 # message for the end user. It holds information that e.g. some value is 109 # missing or incorrect, something cannot be done, something doesn't 110 # exist. Eveything else should really be an uncaught exception caused by 111 # either not properly running all frontend requirements (PostgreSQL, 112 # Redis), or having a bug in the code. 113 if not any([isinstance(error, CoprHttpException), 114 isinstance(error, HTTPException)]): 115 message = ("Request wasn't successful, " 116 "there is probably a bug in the API code.") 117 LOG.exception("Admin-only exception") 118 return self.respond(message, code)
119
120 - def respond(self, message, code): # pylint: disable=no-self-use
121 """ 122 Return JSON response suitable for API clients 123 """ 124 response = flask.jsonify(error=message) 125 response.status_code = code 126 return response
127