From 307cde8cd84bf92b25c584ebf198d80f15fb69e7 Mon Sep 17 00:00:00 2001
From: Zhenguo Niu <niuzhenguo@huawei.com>
Date: Fri, 3 Jul 2015 19:20:01 +0800
Subject: [PATCH] Add handle_error decorator to API calls

It wraps the original method in a try-except block, with horizon's
error handling added.

Change-Id: Id6f7001a6d77f4ba0c0ca8fa62801d6007266bd3
---
 mistraldashboard/api.py           |  8 +++-
 mistraldashboard/handle_errors.py | 71 +++++++++++++++++++++++++++++++
 2 files changed, 78 insertions(+), 1 deletion(-)
 create mode 100644 mistraldashboard/handle_errors.py

diff --git a/mistraldashboard/api.py b/mistraldashboard/api.py
index 54938ec..66de019 100644
--- a/mistraldashboard/api.py
+++ b/mistraldashboard/api.py
@@ -15,8 +15,10 @@
 # limitations under the License.
 
 from django.conf import settings
+from django.utils.translation import ugettext_lazy as _
 
 from mistralclient.api import client as mistral_client
+from mistraldashboard.handle_errors import handle_errors
 
 SERVICE_TYPE = 'workflowv2'
 
@@ -46,27 +48,31 @@ def execution_create(request, **data):
     return mistralclient(request).executions.create(**data)
 
 
+@handle_errors(_("Unable to retrieve executions."), [])
 def execution_list(request):
     """Returns all executions."""
 
     return mistralclient(request).executions.list()
 
 
+@handle_errors(_("Unable to retrieve tasks."), [])
 def task_list(request, execution_id=None):
     """Returns all tasks.
 
-    :param execution_id: Workflow execution ID associated with list of Tasks
+    :param execution_id: Workflow execution ID associated with list of tasks
     """
 
     return mistralclient(request).tasks.list(execution_id)
 
 
+@handle_errors(_("Unable to retrieve workflows."), [])
 def workflow_list(request):
     """Returns all workflows."""
 
     return mistralclient(request).workflows.list()
 
 
+@handle_errors(_("Unable to retrieve workbooks."), [])
 def workbook_list(request):
     """Returns all workbooks."""
 
diff --git a/mistraldashboard/handle_errors.py b/mistraldashboard/handle_errors.py
new file mode 100644
index 0000000..e89c784
--- /dev/null
+++ b/mistraldashboard/handle_errors.py
@@ -0,0 +1,71 @@
+# Copyright 2015 Huawei Technologies Co., Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+import functools
+import inspect
+
+import horizon.exceptions
+
+
+def handle_errors(error_message, error_default=None, request_arg=None):
+    """A decorator for adding default error handling to API calls.
+
+    It wraps the original method in a try-except block, with horizon's
+    error handling added.
+
+    Note: it should only be used on functions or methods that take request as
+    their argument (it has to be named "request", or ``request_arg`` has to be
+    provided, indicating which argument is the request).
+
+    The decorated method accepts a number of additional parameters:
+
+        :param _error_handle: whether to handle the errors in this call
+        :param _error_message: override the error message
+        :param _error_default: override the default value returned on error
+        :param _error_redirect: specify a redirect url for errors
+        :param _error_ignore: ignore known errors
+    """
+    def decorator(func):
+        if request_arg is None:
+            if 'request' not in inspect.getargspec(func).args:
+                raise RuntimeError(
+                    "The handle_errors decorator requires 'request' as "
+                    "an argument of the function or method being decorated")
+        else:
+            _request_arg = request_arg
+
+        @functools.wraps(func)
+        def wrapper(*args, **kwargs):
+            _error_handle = kwargs.pop('_error_handle', True)
+            _error_message = kwargs.pop('_error_message', error_message)
+            _error_default = kwargs.pop('_error_default', error_default)
+            _error_redirect = kwargs.pop('_error_redirect', None)
+            _error_ignore = kwargs.pop('_error_ignore', False)
+
+            if not _error_handle:
+                return func(*args, **kwargs)
+            try:
+                return func(*args, **kwargs)
+            except Exception:
+                request = args[_request_arg]
+                horizon.exceptions.handle(request, _error_message,
+                                          ignore=_error_ignore,
+                                          redirect=_error_redirect)
+                return _error_default
+
+        wrapper.wrapped = func
+
+        return wrapper
+
+    return decorator