Explorar o código

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 %!s(int64=12) %!d(string=hai) anos
pai
achega
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);
         }
         }
       });
       });