Selaa lähdekoodia

HUE-9028 [editor] Add execution errors to the logs component and properly mark them in the gutter

Johan Ahlen 6 vuotta sitten
vanhempi
commit
13d995a57f

+ 9 - 1
desktop/core/src/desktop/js/apps/notebook2/components/ko.executableLogs.js

@@ -19,7 +19,7 @@ import ko from 'knockout';
 import 'ko/bindings/ko.publish';
 
 import componentUtils from 'ko/components/componentUtils';
-import { EXECUTABLE_UPDATED_EVENT, EXECUTION_STATUS } from 'apps/notebook2/execution/executable';
+import { EXECUTABLE_UPDATED_EVENT } from 'apps/notebook2/execution/executable';
 import DisposableComponent from 'ko/components/DisposableComponent';
 import I18n from 'utils/i18n';
 import { RESULT_UPDATED_EVENT } from 'apps/notebook2/execution/executionResult';
@@ -29,6 +29,12 @@ export const NAME = 'executable-logs';
 
 // prettier-ignore
 const TEMPLATE = `
+<div class="snippet-error-container alert alert-error" data-bind="visible: errors().length" style="display: none;">
+  <ul class="unstyled" data-bind="foreach: errors">
+    <li data-bind="text: $data"></li>
+  </ul>
+</div>
+
 <div class="snippet-log-container margin-bottom-10" data-bind="visible: showLogs" style="display: none;">
   <div data-bind="delayedOverflow: 'slow', css: resultsKlass" style="margin-top: 5px; position: relative;">
     <a href="javascript: void(0)" class="inactive-action close-logs-overlay" data-bind="toggle: showLogs">&times;</a>
@@ -119,6 +125,7 @@ class ExecutableLogs extends DisposableComponent {
     this.compute = undefined;
 
     this.jobs = ko.observableArray();
+    this.errors = ko.observableArray();
     this.logs = ko.observable();
 
     this.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
@@ -158,6 +165,7 @@ class ExecutableLogs extends DisposableComponent {
   updateFromLogs(executionLogs) {
     this.logs(executionLogs.fullLog);
     this.jobs(executionLogs.jobs);
+    this.errors(executionLogs.errors);
   }
 
   updateFromResult(executionResult) {

+ 31 - 1
desktop/core/src/desktop/js/apps/notebook2/execution/executable.js

@@ -41,6 +41,8 @@ export const EXECUTION_STATUS = {
 
 export const EXECUTABLE_UPDATED_EVENT = 'hue.executable.updated';
 
+const ERROR_REGEX = /line ([0-9]+)(\:([0-9]+))?/i;
+
 export default class Executable {
   /**
    * @param options
@@ -106,6 +108,10 @@ export default class Executable {
     return this.status === EXECUTION_STATUS.success || this.status === EXECUTION_STATUS.available;
   }
 
+  isFailed() {
+    return this.status === EXECUTION_STATUS.failed;
+  }
+
   isPartOfRunningExecution() {
     if (!this.isReady()) {
       return true;
@@ -156,8 +162,32 @@ export default class Executable {
     try {
       const session = await sessionManager.getSession({ type: this.executor.sourceType() });
       hueAnalytics.log('notebook', 'execute/' + this.executor.sourceType());
-      this.handle = await this.internalExecute(session);
+      try {
+        this.handle = await this.internalExecute(session);
+      } catch (err) {
+        const match = ERROR_REGEX.exec(err);
+        if (match) {
+          const errorLine = parseInt(match[1]) + this.parsedStatement.location.first_line - 1;
+          let errorCol = match[3] && parseInt(match[3]);
+          if (errorCol && errorLine === 1) {
+            errorCol += this.parsedStatement.location.first_column;
+          }
+
+          const adjustedErr = err.replace(
+            match[0],
+            'line ' + errorLine + (errorCol !== null ? ':' + errorCol : '')
+          );
+
+          this.logs.errors.push(adjustedErr);
+          this.logs.notify();
+
+          throw new Error(adjustedErr);
+        }
 
+        this.logs.errors.push(err);
+
+        throw err;
+      }
       if (this.handle.has_result_set && this.handle.sync) {
         this.result = new ExecutionResult(this);
         if (this.handle.sync) {

+ 7 - 1
desktop/core/src/desktop/js/apps/notebook2/execution/executionLogs.js

@@ -30,13 +30,19 @@ export default class ExecutionLogs {
     this.fullLog = '';
     this.logLines = 0;
     this.jobs = [];
+    this.errors = [];
+  }
+
+  notify() {
+    huePubSub.publish(LOGS_UPDATED_EVENT, this);
   }
 
   reset() {
     this.fullLog = '';
     this.logLines = 0;
     this.jobs = [];
-    huePubSub.publish(LOGS_UPDATED_EVENT, this);
+    this.errors = [];
+    this.notify();
   }
 
   async fetchLogs(finalFetch) {

+ 2 - 1
desktop/core/src/desktop/js/apps/notebook2/execution/sqlExecutable.js

@@ -40,7 +40,8 @@ export default class SqlExecutable extends Executable {
   async internalExecute(session) {
     return await apiHelper.executeStatement({
       executable: this,
-      session: session
+      session: session,
+      silenceErrors: true
     });
   }
 

+ 13 - 4
desktop/core/src/desktop/js/apps/notebook2/snippet.js

@@ -1010,18 +1010,27 @@ export default class Snippet {
 
     huePubSub.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
       if (this.activeExecutable() === executable) {
-        this.status(executable.status);
-        if (executable.result) {
-          this.currentQueryTab('queryResults');
-        }
+        this.updateFromExecutable();
       }
     });
 
+    this.activeExecutable.subscribe(this.updateFromExecutable.bind(this));
+
     this.refreshHistory = notebook.fetchHistory;
 
     huePubSub.publish(REFRESH_STATEMENT_LOCATIONS_EVENT, this);
   }
 
+  updateFromExecutable(executable) {
+    if (executable) {
+      this.status(executable.status);
+      if (executable.result) {
+        this.currentQueryTab('queryResults');
+      }
+    } else {
+    }
+  }
+
   ace(newVal) {
     if (newVal) {
       this.aceEditor = newVal;

+ 23 - 1
desktop/core/src/desktop/js/ko/bindings/ace/aceGutterHandler.js

@@ -24,8 +24,9 @@ const LINE_BREAK_REGEX = /(\r\n)|(\n)|(\r)/g;
 const LEADING_WHITE_SPACE_REGEX = /^\s+/;
 
 const ACTIVE_CSS = 'ace-active-gutter-decoration';
-const EXECUTING_CSS = 'ace-executing-gutter-decoration';
 const COMPLETED_CSS = 'ace-completed-gutter-decoration';
+const EXECUTING_CSS = 'ace-executing-gutter-decoration';
+const FAILED_CSS = 'ace-failed-gutter-decoration';
 
 const getLeadingEmptyLineCount = parsedStatement => {
   let leadingEmptyLineCount = 0;
@@ -47,6 +48,21 @@ const forEachLine = (statement, callback) => {
   }
 };
 
+const clearErrorForLine = (session, line) => {
+  const markers = session.getMarkers(false);
+  Object.keys(markers).some(key => {
+    const marker = markers[key];
+    if (
+      marker.clazz === 'ace_error-line' &&
+      marker.range.start.row <= line &&
+      line <= marker.range.end.row
+    ) {
+      session.removeMarker(marker.id);
+      return true;
+    }
+  });
+};
+
 export default class AceGutterHandler {
   constructor(options) {
     this.editor = options.editor;
@@ -82,6 +98,7 @@ export default class AceGutterHandler {
 
     if (this.executor) {
       const session = this.editor.getSession();
+      const AceRange = window.ace.require('ace/range').Range;
 
       const executableSub = huePubSub.subscribe(EXECUTABLE_UPDATED_EVENT, executable => {
         if (executable.executor === this.executor) {
@@ -89,10 +106,15 @@ export default class AceGutterHandler {
           forEachLine(statement, line => {
             session.removeGutterDecoration(line, COMPLETED_CSS);
             session.removeGutterDecoration(line, EXECUTING_CSS);
+            clearErrorForLine(session, line);
             if (executable.isRunning()) {
               session.addGutterDecoration(line, EXECUTING_CSS);
             } else if (executable.isSuccess()) {
               session.addGutterDecoration(line, COMPLETED_CSS);
+            } else if (executable.isFailed()) {
+              const range = new AceRange(line, 0, line, session.getLine(line).length);
+              session.addMarker(range, 'ace_error-line');
+              session.addGutterDecoration(line, FAILED_CSS);
             }
           });
         }

Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue.css


Tiedoston diff-näkymää rajattu, sillä se on liian suuri
+ 0 - 0
desktop/core/src/desktop/static/desktop/css/hue3-extra.css


+ 6 - 1
desktop/core/src/desktop/static/desktop/less/hue-cross-version.less

@@ -266,8 +266,13 @@ input[type='password']::-ms-reveal {
       100% { background-color: rgba(0, 140, 255, 0.1); }
     });
 
+    &.ace-failed-gutter-decoration {
+      background-color: @cui-pink-010;
+      border-right: 1px solid @cui-red-700;
+    }
+
     &.ace-completed-gutter-decoration {
-      background-color: @cui-green-100;
+      background-color: @cui-green-050;
       border-right: 1px solid @cui-green-400;
     }
 

Kaikkia tiedostoja ei voida näyttää, sillä liian monta tiedostoa muuttui tässä diffissä