changeset 47:e99cbb47eafb

Improve error handling by returning flask/werkzeug responses directly on errors
author Björn Ricks <bjoern.ricks@intevation.de>
date Tue, 28 Oct 2014 10:51:42 +0100
parents f270eaa7de8d
children 3bff1a4bc80e
files odfcast/convert.py
diffstat 1 files changed, 70 insertions(+), 34 deletions(-) [+]
line wrap: on
line diff
--- a/odfcast/convert.py	Fri Oct 24 13:25:12 2014 +0200
+++ b/odfcast/convert.py	Tue Oct 28 10:51:42 2014 +0100
@@ -10,11 +10,15 @@
 
 from PyPDF2 import PdfFileMerger
 
+from werkzeug.utils import escape
+
 log = logging.getLogger(__name__)
 
 ALLOWED_FORMATS = ["pdf", "doc", "docx", "odt"]
 
 PDF_MIMETYPE = "application/pdf"
+JSON_MIMETYPE = "application/json"
+HTML_MIMETYPE = "text/html"
 
 MIMETYPES = {
     "odt": "application/vnd.oasis.opendocument.text",
@@ -27,34 +31,68 @@
 DEFAULT_MIMETYPE = "application/octet-stream"
 
 
-class ErrorResponse(object):
+class ErrorResponse(Response):
 
-    def __init__(self, title, details="", html_error_code=500):
-        self.title = title
-        self.details = details
-        self.html_error_code = html_error_code
+    BAD_REQUEST_ERROR_CODE = 400
 
-    def __call__(self):
+    def __init__(self, title, error_code, details,
+                 html_error_code=BAD_REQUEST_ERROR_CODE):
+        data, mime_type = self.get_response_data(title, error_code, details)
+        super(ErrorResponse, self).__init__(response=data, mimetype=mime_type,
+                                            status=html_error_code)
+
+    def json(self, title, error_code, details):
+        return json.dumps({
+            "error": title,
+            "error_code": error_code,
+            "details": details,
+        }), JSON_MIMETYPE
+
+    def html(self, title, error_code, details):
+        data = ""
+        return (
+            u'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
+            u'<title>%(code)s %(name)s</title>\n'
+            u'<h1>%(name)s</h1>\n'
+            u'%(details)s\n'
+        ) % {
+            "code": error_code,
+            "name": escape(title),
+            "details": escape(details),
+        }
+        return data, HTML_MIMETYPE
+
+    def get_response_data(self, title, error_code, details):
         if self.is_wants_json():
-            return self.json()
-        else:
-            return self.html()
-
-    def json(self):
-        return json.dumps({
-            "error": self.title,
-            "details": self.details
-        }), self.html_error_code
-
-    def html(self):
-        return self.title, self.html_error_code
+            return self.json(title, error_code, details)
+        return self.html(title, error_code, details)
 
     def is_wants_json(self):
-        best = request.accept_mimetypes.best_match(['application/json',
-                                                    'text/html'])
-        return best == 'application/json' and \
+        best = request.accept_mimetypes.best_match([JSON_MIMETYPE,
+                                                    HTML_MIMETYPE])
+        return best == JSON_MIMETYPE and \
             request.accept_mimetypes[best] > \
-            request.accept_mimetypes['text/html']
+            request.accept_mimetypes[HTML_MIMETYPE]
+
+
+class TemplateErrorResponse(ErrorResponse):
+
+    TEMPLATE_ERROR_CODE = 100
+
+    def __init__(self, details, error_code=TEMPLATE_ERROR_CODE):
+        super(TemplateErrorResponse, self).__init__(
+            title="TemplateError", error_code=error_code, details=details,
+            html_error_code=500)
+
+
+class ConversionErrorResponse(ErrorResponse):
+
+    CONVERSION_ERROR_CODE = 200
+
+    def __init__(self, details, error_code=CONVERSION_ERROR_CODE):
+        super(TemplateErrorResponse, self).__init__(
+            title="ConversionError", error_code=error_code, details=details,
+            html_error_code=500)
 
 
 class ConvertView(MethodView):
@@ -73,15 +111,17 @@
     def post(self):
         ffile = request.files['file']
         if not ffile.filename:
-            error = ErrorResponse("Please upload a file for conversion",
-                                  html_error_code=401)
-            return error()
+            return ErrorResponse(
+                "Upload file missing", error_code=101,
+                details="Please upload a file for conversion",
+                html_error_code=400)
 
         fformat = request.form['format']
         if not self.is_format_supported(fformat):
-            error = ErrorResponse("Format %s not allowed" % fformat,
-                                  html_error_code=401)
-            return error()
+            return ErrorResponse(
+                "Invalid format", error_code=102,
+                details="Format %s not allowed" % fformat,
+                html_error_code=400)
 
         datadict = self.get_datadict()
 
@@ -98,18 +138,14 @@
                 outfile = tfile
             except Exception, e:
                 log.exception("Template error")
-                error = ErrorResponse(
-                    "Template error", details=str(e), html_error_code=500)
-                return error()
+                return TemplateErrorResponse(details=str(e))
 
         if fformat != "odt":
             try:
                 outfile = self.convert(outfile, fformat)
             except Exception, e:
                 log.exception("Conversion error")
-                error = ErrorResponse(
-                    "Conversion error", details=str(e), html_error_code=500)
-                return error()
+                return ConversionErrorResponse(details=str(e))
 
         return Response(outfile, mimetype=mimetype)
 
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)