浏览代码

HUE-9000 [editor] Make it optional to submit notebook and snippet on execute for history in editor v2

Johan Ahlen 6 年之前
父节点
当前提交
6db70177fc

+ 62 - 81
desktop/core/src/desktop/js/api/apiHelper.js

@@ -2029,47 +2029,6 @@ class ApiHelper {
     });
   }
 
-  /**
-   *
-   * @param {Executable} executable
-   * @param {Session} [session]
-   *
-   * @return {{snippet: string, notebook: string}}
-   */
-  static adaptExecutableToNotebook(executable, session) {
-    const statement = executable.getStatement();
-    const snippet = {
-      type: executable.executor.sourceType(),
-      result: {
-        handle: executable.handle
-      },
-      status: executable.status,
-      id: executable.snippetId || hueUtils.UUID(),
-      statement_raw: statement,
-      statement: statement,
-      variables: [],
-      compute: executable.executor.compute(),
-      namespace: executable.executor.namespace(),
-      database: executable.database,
-      properties: { settings: [] }
-    };
-
-    const notebook = {
-      type: executable.executor.sourceType(),
-      snippets: [snippet],
-      id: executable.notebookId,
-      uuid: hueUtils.UUID(),
-      name: '',
-      isSaved: false,
-      sessions: session ? [session] : []
-    };
-
-    return {
-      snippet: JSON.stringify(snippet),
-      notebook: JSON.stringify(notebook)
-    };
-  }
-
   /**
    * @typedef {Object} ExecutionHandle
    * @property {string} guid
@@ -2096,22 +2055,38 @@ class ApiHelper {
    *
    * @return {Promise<ExecutionHandle>}
    */
-  executeStatement(options) {
+  async executeStatement(options) {
     const executable = options.executable;
     const url = EXECUTE_API_PREFIX + executable.executor.sourceType();
 
-    const promise = new Promise((resolve, reject) => {
-      this.simplePost(
-        url,
-        ApiHelper.adaptExecutableToNotebook(executable, options.session),
-        options
-      )
+    const promise = new Promise(async (resolve, reject) => {
+      let data = {};
+      if (executable.executor.snippet) {
+        data = {
+          notebook: await executable.executor.snippet.parentNotebook.toJson(),
+          snippet: executable.executor.snippet.toContextJson()
+        };
+      } else {
+        data = await executable.toContext();
+      }
+
+      this.simplePost(url, data, options)
         .done(response => {
+          const executeResponse = {};
           if (response.handle) {
-            resolve(response.handle);
+            executeResponse.handle = response.handle;
           } else {
             reject('No handle in execute response');
+            return;
+          }
+          if (response.history_id) {
+            executeResponse.history = {
+              id: response.history_id,
+              uuid: response.history_uuid,
+              parentUuid: response.history_parent_uuid
+            };
           }
+          resolve(executeResponse);
         })
         .fail(reject);
     });
@@ -2163,24 +2138,32 @@ class ApiHelper {
   checkExecutionStatus(options) {
     const deferred = $.Deferred();
 
-    const request = $.post({
-      url: '/notebook/api/check_status',
-      data: ApiHelper.adaptExecutableToNotebook(options.executable)
-    })
-      .done(response => {
-        if (response && response.query_status) {
-          deferred.resolve(response.query_status.status);
-        } else if (response && response.status === -3) {
-          deferred.resolve(EXECUTION_STATUS.expired);
-        } else {
-          deferred.resolve(EXECUTION_STATUS.failed);
-        }
+    const result = new CancellablePromise(deferred);
+
+    options.executable
+      .toContext()
+      .then(notebookApiContext => {
+        const request = $.post({
+          url: '/notebook/api/check_status',
+          data: notebookApiContext
+        })
+          .done(response => {
+            if (response && response.query_status) {
+              deferred.resolve(response.query_status.status);
+            } else if (response && response.status === -3) {
+              deferred.resolve(EXECUTION_STATUS.expired);
+            } else {
+              deferred.resolve(EXECUTION_STATUS.failed);
+            }
+          })
+          .fail(err => {
+            deferred.reject(this.assistErrorCallback(options)(err));
+          });
+        result.request = request;
       })
-      .fail(err => {
-        deferred.reject(this.assistErrorCallback(options)(err));
-      });
+      .catch(deferred.reject);
 
-    return new CancellablePromise(deferred, request);
+    return result;
   }
 
   /**
@@ -2195,8 +2178,8 @@ class ApiHelper {
    * @return {Promise<?>}
    */
   fetchLogs(options) {
-    return new Promise((resolve, reject) => {
-      const data = ApiHelper.adaptExecutableToNotebook(options.executable);
+    return new Promise(async (resolve, reject) => {
+      const data = options.executable.toContext();
       data.full_log = options.fullLog;
       data.jobs = options.jobs && JSON.stringify(options.jobs);
       data.from = options.from || 0;
@@ -2226,12 +2209,12 @@ class ApiHelper {
    *
    * @return {Promise}
    */
-  cancelStatement(options) {
-    return this.simplePost(
-      '/notebook/api/cancel_statement',
-      ApiHelper.adaptExecutableToNotebook(options.executable),
-      options
-    );
+  async cancelStatement(options) {
+    return new Promise(async (resolve, reject) => {
+      this.simplePost('/notebook/api/cancel_statement', options.executable.toContext(), options)
+        .done(resolve)
+        .fail(reject);
+    });
   }
 
   /**
@@ -2261,8 +2244,8 @@ class ApiHelper {
    * @return {Promise<ResultResponse>}
    */
   async fetchResults(options) {
-    return new Promise((resolve, reject) => {
-      const data = ApiHelper.adaptExecutableToNotebook(options.executable);
+    return new Promise(async (resolve, reject) => {
+      const data = await options.executable.toContext();
       data.rows = options.rows;
       data.startOver = !!options.startOver;
 
@@ -2298,10 +2281,10 @@ class ApiHelper {
    * @return {Promise<ResultResponse>}
    */
   async fetchResultSize2(options) {
-    return new Promise((resolve, reject) => {
+    return new Promise(async (resolve, reject) => {
       const request = this.simplePost(
         '/notebook/api/fetch_result_size',
-        ApiHelper.adaptExecutableToNotebook(options.executable),
+        await options.executable.toContext(),
         options
       )
         .done(response => {
@@ -2326,12 +2309,10 @@ class ApiHelper {
    * @return {Promise}
    */
   async closeStatement(options) {
-    const executable = options.executable;
-
-    return new Promise((resolve, reject) => {
+    return new Promise(async (resolve, reject) => {
       this.simplePost(
         '/notebook/api/close_statement',
-        ApiHelper.adaptExecutableToNotebook(executable),
+        await options.executable.toContext(),
         options
       )
         .done(resolve)

+ 0 - 1
desktop/core/src/desktop/js/apps/notebook2/components/resultGrid/ko.resultDownloadModal.js

@@ -17,7 +17,6 @@
 import $ from 'jquery';
 import ko from 'knockout';
 
-import { ApiHelper } from 'api/apiHelper';
 import componentUtils from 'ko/components/componentUtils';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';

+ 47 - 3
desktop/core/src/desktop/js/apps/notebook2/execution/executable.js

@@ -20,6 +20,7 @@ import hueAnalytics from 'utils/hueAnalytics';
 import huePubSub from 'utils/huePubSub';
 import sessionManager from 'apps/notebook2/execution/sessionManager';
 import ExecutionLogs from 'apps/notebook2/execution/executionLogs';
+import hueUtils, { UUID } from 'utils/hueUtils';
 
 /**
  *
@@ -56,6 +57,7 @@ export default class Executable {
     this.handle = {
       statement_id: 0 // TODO: Get rid of need for initial handle in the backend
     };
+    this.history = undefined;
     this.status = EXECUTION_STATUS.ready;
     this.progress = 0;
     this.result = undefined;
@@ -164,10 +166,11 @@ export default class Executable {
     this.setProgress(0);
 
     try {
-      const session = await sessionManager.getSession({ type: this.executor.sourceType() });
       hueAnalytics.log('notebook', 'execute/' + this.executor.sourceType());
       try {
-        this.handle = await this.internalExecute(session);
+        const response = await this.internalExecute();
+        this.handle = response.handle;
+        this.history = response.history;
       } catch (err) {
         const match = ERROR_REGEX.exec(err);
         if (match) {
@@ -273,7 +276,7 @@ export default class Executable {
     this.cancellables.push(cancellable);
   }
 
-  async internalExecute(session) {
+  async internalExecute() {
     throw new Error('Implement in subclass!');
   }
 
@@ -345,4 +348,45 @@ export default class Executable {
     }
     this.setStatus(EXECUTION_STATUS.closed);
   }
+
+  async toContext(id) {
+    if (this.executor.snippet) {
+      return {
+        snippet: this.executor.snippet.toContextJson(),
+        notebook: await this.executor.snippet.parentNotebook.toContextJson()
+      };
+    }
+    const session = await sessionManager.getSession({ type: this.executor.sourceType() });
+    const statement = this.getStatement();
+    const snippet = {
+      type: this.executor.sourceType(),
+      result: {
+        handle: this.handle
+      },
+      status: this.status,
+      id: id || UUID(),
+      statement_raw: statement,
+      statement: statement,
+      variables: [],
+      compute: this.executor.compute(),
+      namespace: this.executor.namespace(),
+      database: this.database,
+      properties: { settings: [] }
+    };
+
+    const notebook = {
+      type: this.executor.sourceType(),
+      snippets: [snippet],
+      id: this.notebookId,
+      uuid: hueUtils.UUID(),
+      name: '',
+      isSaved: false,
+      sessions: [session]
+    };
+
+    return {
+      snippet: JSON.stringify(snippet),
+      notebook: JSON.stringify(notebook)
+    };
+  }
 }

+ 3 - 0
desktop/core/src/desktop/js/apps/notebook2/execution/executor.js

@@ -29,6 +29,7 @@ class Executor {
    * @param {ContextNamespace} options.namespace
    * @param {string} options.statement
    * @param {string} [options.database]
+   * @param {Snippet} [options.snippet] - Optional snippet for history
    */
   constructor(options) {
     this.sourceType = options.sourceType;
@@ -37,6 +38,8 @@ class Executor {
     this.database = options.database;
     this.isSqlEngine = options.isSqlEngine;
     this.executables = [];
+
+    this.snippet = options.snippet;
   }
 
   getExecutables(statementDetails) {

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

@@ -37,10 +37,9 @@ export default class SqlExecutable extends Executable {
     return this.statement || this.parsedStatement.statement;
   }
 
-  async internalExecute(session) {
+  async internalExecute() {
     return await apiHelper.executeStatement({
       executable: this,
-      session: session,
       silenceErrors: true
     });
   }

+ 11 - 1
desktop/core/src/desktop/js/apps/notebook2/snippet.js

@@ -1006,6 +1006,7 @@ export default class Snippet {
       database: this.database,
       sourceType: this.type,
       namespace: this.namespace,
+      snippet: this,
       isSqlEngine: this.isSqlDialect
     });
 
@@ -1053,7 +1054,16 @@ export default class Snippet {
       if (executable.result) {
         this.currentQueryTab('queryResults');
       }
-    } else {
+      if (this.parentVm.editorMode() && executable.history) {
+        this.parentNotebook.id(executable.history.id);
+        this.parentNotebook.uuid(executable.history.uuid);
+        this.parentNotebook.isHistory(true);
+        this.parentNotebook.parentSavedQueryUuid(executable.history.parentId);
+        if (!this.parentVm.isNotificationManager()) {
+          const url = this.parentVm.URLS.editor + '?editor=' + executable.history.id;
+          this.parentVm.changeURL(url);
+        }
+      }
     }
   }