Răsfoiți Sursa

HUE-1842 [beeswax] Error info above query box instead of the popup

Make error messages appear on bottom half of the screen if
the error occurs on submission.
Abraham Elmahrek 12 ani în urmă
părinte
comite
63558b1663

+ 23 - 15
apps/beeswax/src/beeswax/api.py

@@ -53,13 +53,19 @@ def error_handler(view_fn):
     except Exception, e:
     except Exception, e:
       if not hasattr(e, 'message') or not e.message:
       if not hasattr(e, 'message') or not e.message:
         message = _("Unknown exception.")
         message = _("Unknown exception.")
+        response = {
+          'status': -1,
+          'error': str(e),
+          'message': message,
+        }
       else:
       else:
         message = force_unicode(e.message, strings_only=True, errors='replace')
         message = force_unicode(e.message, strings_only=True, errors='replace')
-      response = {
-        'error': str(e),
-        'message': message,
-      }
-      return HttpResponse(json.dumps(response), mimetype="application/json", status=500)
+        response = {
+          'status': 1,
+          'error': str(e),
+          'message': message,
+        }
+      return HttpResponse(json.dumps(response), mimetype="application/json", status=200)
   return decorator
   return decorator
 
 
 
 
@@ -139,15 +145,6 @@ def watch_query_refresh_json(request, id):
   handle, state = _get_query_handle_and_state(query_history)
   handle, state = _get_query_handle_and_state(query_history)
   query_history.save_state(state)
   query_history.save_state(state)
 
 
-  # Show popup message if error, should be better in error tab instead and merged into the result response below
-  if query_history.is_failure():
-    res = db.get_operation_status(handle)
-    if hasattr(res, 'errorMessage') and res.errorMessage:
-      message = res.errorMessage
-    else:
-      message = ''
-    raise QueryServerException(Exception('Bad status for request %s:\n%s' % (id, res)), message=message)
-
   # Multi query if more statements
   # Multi query if more statements
   try:
   try:
     if not query_history.is_finished() and query_history.is_success() and not query_history.has_results:
     if not query_history.is_finished() and query_history.is_success() and not query_history.has_results:
@@ -169,11 +166,22 @@ def watch_query_refresh_json(request, id):
     'log': log,
     'log': log,
     'jobs': jobs,
     'jobs': jobs,
     'jobUrls': job_urls,
     'jobUrls': job_urls,
-    'isSuccess': query_history.is_finished() or (query_history.is_success() and query_history.has_results),
+    'isSuccess': query_history.is_finished() or (query_history.is_success() and query_history.has_results and query_history.is_failure()),
     'isFailure': query_history.is_failure(),
     'isFailure': query_history.is_failure(),
     'id': id
     'id': id
   }
   }
 
 
+  # Show popup message if error, should be better in error tab instead and merged into the result response below
+  if query_history.is_failure():
+    res = db.get_operation_status(handle)
+    if hasattr(res, 'errorMessage') and res.errorMessage:
+      result['message'] = res.errorMessage
+    else:
+      result['message'] = ''
+    
+    result['error'] = 'Bad status for request %s:\n%s' % (id, res)
+    result['status'] = 1
+
   return HttpResponse(json.dumps(result), mimetype="application/json")
   return HttpResponse(json.dumps(result), mimetype="application/json")
 
 
 
 

+ 43 - 12
apps/beeswax/src/beeswax/templates/execute.mako

@@ -96,11 +96,10 @@ ${layout.menubar(section='query')}
             <a data-bind="click: function() { $root.addFileResources('','') }" class="btn btn-mini paramAdd">${_('Add')}</a>
             <a data-bind="click: function() { $root.addFileResources('','') }" class="btn btn-mini paramAdd">${_('Add')}</a>
           </div>
           </div>
         </li>
         </li>
-        <li class="nav-header" title="${_("User-Defined Functions")}"
+        <li class="nav-header 
           % if app_name == 'impala':
           % if app_name == 'impala':
             hide
             hide
-          % endif
-          ">
+          % endif" title="${_("User-Defined Functions")}">
           ${_('UDFs')}
           ${_('UDFs')}
         </li>
         </li>
         <li class="white paramContainer
         <li class="white paramContainer
@@ -223,6 +222,14 @@ ${layout.menubar(section='query')}
             </div>
             </div>
           </div>
           </div>
 
 
+          <div data-bind="css: {'hide': query.watch.errors().length == 0}" class="alert alert-error">
+            <p><strong>${_('Your query has the following error(s):')}</strong></p>
+
+            <div data-bind="foreach: query.watch.errors">
+              <p data-bind="text: $data" class="queryErrorMessage"></p>
+            </div>
+          </div>
+
           <textarea class="hide" tabindex="1" name="query" id="queryField"></textarea>
           <textarea class="hide" tabindex="1" name="query" id="queryField"></textarea>
 
 
           <div class="actions">
           <div class="actions">
@@ -296,6 +303,7 @@ ${layout.menubar(section='query')}
           </table>
           </table>
         </div>
         </div>
         <div class="tab-pane" id="results">
         <div class="tab-pane" id="results">
+
           <div data-bind="css: {'hide': $root.query.results.rows().length == 0}" class="hide">
           <div data-bind="css: {'hide': $root.query.results.rows().length == 0}" class="hide">
             <table class="table table-striped table-condensed resultTable" cellpadding="0" cellspacing="0" data-tablescroller-enforce-height="true">
             <table class="table table-striped table-condensed resultTable" cellpadding="0" cellspacing="0" data-tablescroller-enforce-height="true">
               <thead>
               <thead>
@@ -318,6 +326,7 @@ ${layout.menubar(section='query')}
             </div>
             </div>
           </div>
           </div>
         </div>
         </div>
+
          <div class="tab-pane" id="chart">
          <div class="tab-pane" id="chart">
           <div style="text-align: center">
           <div style="text-align: center">
           <form class="form-inline">
           <form class="form-inline">
@@ -1079,8 +1088,11 @@ $(document).ready(function () {
     if (codeMirror.getValue() == queryPlaceholder) {
     if (codeMirror.getValue() == queryPlaceholder) {
       codeMirror.setValue("");
       codeMirror.setValue("");
     }
     }
-    if (errorWidget) {
-      errorWidget.clear();
+    if (errorWidgets) {
+      $.each(errorWidgets, function(index, errorWidget) {
+        errorWidget.clear();
+      });
+      errorWidgets = [];
     }
     }
     $("#validationResults").empty();
     $("#validationResults").empty();
   });
   });
@@ -1432,15 +1444,25 @@ $(document).on('explain.query', cleanResultsTable);
 $(document).on('fetched.results', resultsTable);
 $(document).on('fetched.results', resultsTable);
 
 
 var selectedLine = -1;
 var selectedLine = -1;
-var errorWidget = null;
+var errorWidgets = [];
 $(document).on('error.query', function () {
 $(document).on('error.query', function () {
-  if ($(".queryErrorMessage").length > 0) {
-    var err = $(".queryErrorMessage").text().toLowerCase();
+  $.each(errorWidgets, function(index, el) {
+    $(el).remove();
+    errorWidgets = [];
+  });
+
+  $.each($(".queryErrorMessage"), function(index, el) {
+    var err = $(el).text().toLowerCase();
     var firstPos = err.indexOf("line");
     var firstPos = err.indexOf("line");
     if (firstPos > -1) {
     if (firstPos > -1) {
       selectedLine = $.trim(err.substring(err.indexOf(" ", firstPos), err.indexOf(":", firstPos))) * 1;
       selectedLine = $.trim(err.substring(err.indexOf(" ", firstPos), err.indexOf(":", firstPos))) * 1;
-      errorWidget = codeMirror.addLineWidget(selectedLine - 1, $("<div>").addClass("editorError").html("<i class='fa fa-exclamation-circle'></i> " + err)[0], {coverGutter: true, noHScroll: true})
+      errorWidgets.push(codeMirror.addLineWidget(selectedLine - 1, $("<div>").addClass("editorError").html("<i class='fa fa-exclamation-circle'></i> " + err)[0], {coverGutter: true, noHScroll: true}));
     }
     }
+    $(el).hide();
+  });
+
+  if ($(".queryErrorMessage:hidden").length == $(".queryErrorMessage").length) {
+    $(".queryErrorMessage").parent().parent().hide();
   }
   }
 });
 });
 
 
@@ -1725,7 +1747,7 @@ $(document).ready(function () {
       showSection('explain-parameter-selection');
       showSection('explain-parameter-selection');
     },
     },
     'query/logs': function () {
     'query/logs': function () {
-      if (viewModel.query.watch.logs().length == 0) {
+      if (viewModel.query.watch.logs().length == 0 && viewModel.query.watch.errors().length == 0) {
         routie('query');
         routie('query');
       }
       }
       codeMirror.setSize("99%", 100);
       codeMirror.setSize("99%", 100);
@@ -1735,7 +1757,7 @@ $(document).ready(function () {
       clickHard('.resultsContainer .nav-tabs a[href="#log"]');
       clickHard('.resultsContainer .nav-tabs a[href="#log"]');
     },
     },
     'query/results': function () {
     'query/results': function () {
-      if (viewModel.query.results.empty()) {
+      if (viewModel.query.id() == -1 && viewModel.query.results.empty()) {
         routie('query');
         routie('query');
       } else {
       } else {
         codeMirror.setSize("99%", 100);
         codeMirror.setSize("99%", 100);
@@ -1770,12 +1792,21 @@ $(document).ready(function () {
   $(document).on('explained.query', function () {
   $(document).on('explained.query', function () {
     routie('query/explanation');
     routie('query/explanation');
   });
   });
-  $(document).on('watched.query', function () {
+  $(document).on('watched.query', function (e, data) {
+    if (data.status && data.status && data.status != 0) {
+      viewModel.query.watch.errors.push(data.error || data.message);
+    }
+    routie('query/logs');
+  });
+  $(document).on('error_watch.query', function () {
     routie('query/logs');
     routie('query/logs');
   });
   });
   $(document).on('fetched.results', function () {
   $(document).on('fetched.results', function () {
     routie('query/results');
     routie('query/results');
   });
   });
+  $(document).on('execute.query', function() {
+    routie('query');
+  });
 });
 });
 
 
 // @TODO: Make file resources list automatically generated.
 // @TODO: Make file resources list automatically generated.

+ 23 - 9
apps/beeswax/static/js/beeswax.vm.js

@@ -46,6 +46,7 @@ function BeeswaxViewModel(server, query_id) {
     'watch': {
     'watch': {
       'logs': [],
       'logs': [],
       'url': null,
       'url': null,
+      'errors': []
     },
     },
     'isRunning': false
     'isRunning': false
   };
   };
@@ -80,7 +81,9 @@ function BeeswaxViewModel(server, query_id) {
       }
       }
     },
     },
     'write': function(value) {
     'write': function(value) {
-      self.selectedDatabase(self.databases.indexOf(value));
+      if (value) {
+        self.selectedDatabase(self.databases.indexOf(value));
+      }
     },
     },
     'deferEvaluation': true
     'deferEvaluation': true
   });
   });
@@ -344,6 +347,7 @@ function BeeswaxViewModel(server, query_id) {
     $(document).trigger('execute.query', data);
     $(document).trigger('execute.query', data);
     self.query.explain(false);
     self.query.explain(false);
     self.query.isRunning(true);
     self.query.isRunning(true);
+    self.query.errors.removeAll();
 
 
     var data = {
     var data = {
       'query-query': self.query.query(),
       'query-query': self.query.query(),
@@ -367,6 +371,7 @@ function BeeswaxViewModel(server, query_id) {
           self.watchQueryLoop();
           self.watchQueryLoop();
         } else {
         } else {
           self.query.errors.push(data.message);
           self.query.errors.push(data.message);
+          self.query.isRunning(false);
           $(document).trigger('error.query');
           $(document).trigger('error.query');
         }
         }
         $(document).trigger('executed.query', data);
         $(document).trigger('executed.query', data);
@@ -411,28 +416,37 @@ function BeeswaxViewModel(server, query_id) {
   self.watchQueryLoop = function(fn) {
   self.watchQueryLoop = function(fn) {
     var TIMEOUT = 1000;
     var TIMEOUT = 1000;
     var timer = null;
     var timer = null;
-    var executed_once = false;
 
 
     self.query.watch.logs.removeAll();
     self.query.watch.logs.removeAll();
+    self.query.watch.errors.removeAll();
     self.query.results.rows.removeAll();
     self.query.results.rows.removeAll();
     self.query.results.columns.removeAll();
     self.query.results.columns.removeAll();
 
 
     var _fn = function() {
     var _fn = function() {
       $(document).one('watched.query', function(e, data) {
       $(document).one('watched.query', function(e, data) {
-        if (executed_once && (data.isSuccess || data.isFailure)) {
+        if (data.isSuccess || data.isFailure) {
           clearTimeout(timer);
           clearTimeout(timer);
-          $(document).trigger('stop_watch.query');
-          if (fn) {
-            fn(data);
-          } else {
-            self.fetchResults();
+          self.query.isRunning(false);
+
+          if (data.log) {
+            self.query.watch.logs.push(data.log);
+            // scroll logs
+          }
+          if (!data.isFailure) {
+            $(document).trigger('stop_watch.query');
+
+            if (fn) {
+              fn(data);
+            } else {
+              self.fetchResults();
+            }
           }
           }
         } else {
         } else {
-          executed_once = true;
           if (data.log) {
           if (data.log) {
             self.query.watch.logs.push(data.log);
             self.query.watch.logs.push(data.log);
             // scroll logs
             // scroll logs
           }
           }
+          
           timer = setTimeout(_fn, TIMEOUT);
           timer = setTimeout(_fn, TIMEOUT);
         }
         }
       });
       });