Browse Source

[core] Move the active database to the assist helper

This moves the active database observable to the assist helper, keeping the page-wide database in one place (as opposed to keeping multiple instances in sync). Before this change more than one components kept track of the active database, by using pub/sub events they were all kept in sync.

As the assist panel is a ko component we were required to use timeouts to make sure the panel was active before certain events were fired, this is not a very robust approach. By just keeping one observable there's no longer any need to have the events and all changes are instant.
Johan Ahlen 10 năm trước cách đây
mục cha
commit
f17468bfe3

+ 3 - 29
apps/beeswax/src/beeswax/static/beeswax/js/beeswax.vm.js

@@ -14,7 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-function BeeswaxViewModel(server) {
+function BeeswaxViewModel(server, assistHelper) {
   var self = this;
 
   var DESIGN_DEFAULTS = {
@@ -72,6 +72,8 @@ function BeeswaxViewModel(server) {
     'isRedacted': false
   };
 
+  self.database = assistHelper.activeDatabase;
+
   self.design = ko.mapping.fromJS(DESIGN_DEFAULTS);
 
   self.design.inlineErrors = ko.computed(function() {
@@ -93,8 +95,6 @@ function BeeswaxViewModel(server) {
   self.chartData = ko.observableArray();
 
   self.server = ko.observable(server);
-  self.databases = ko.observableArray();
-  self.selectedDatabase = ko.observable(0);
   self.isReady = ko.observable(false);
   // Use a view model attribute so that we don't have to override KO.
   // This allows Hue to disable the execute button until the query placeholder dies.
@@ -125,22 +125,6 @@ function BeeswaxViewModel(server) {
     return (self.design.results.save.errors() && 'target_file' in self.design.results.save.errors()) ? self.design.results.save.errors()['target_file'] : null;
   });
 
-  self.database = ko.computed({
-    'read': function() {
-      if (self.databases()) {
-        return self.databases()[self.selectedDatabase()];
-      } else{
-        return "";
-      }
-    },
-    'write': function(databaseName) {
-      if (databaseName) {
-        self.selectedDatabase(self.databases.indexOf(databaseName));
-      }
-    },
-    'deferEvaluation': true
-  });
-
   self.hasParametersFilled = ko.computed(function() {
     var hasBlank = false;
     $.each(self.design.parameters(), function(index, element) {
@@ -180,16 +164,6 @@ function BeeswaxViewModel(server) {
     }
   };
 
-  self.updateDatabases = function(databases) {
-    if (databases) {
-      var i = databases.indexOf("_impala_builtins"); // Blacklist of system databases
-      if (i != -1) {
-        databases.splice(i, 1);
-      }
-      self.databases(databases);
-    }
-  };
-
   self.updateDesign = function(design) {
     self.design.query.value(design.query);
     self.design.id(design.id);

+ 1 - 7
apps/beeswax/src/beeswax/templates/execute.mako

@@ -2570,12 +2570,6 @@ function loadDesign(design_id) {
   setupCodeMirrorSubscription();
 }
 
-huePubSub.subscribe('hue.assist.databaseChanged', function(name) {
-  if (name !== viewModel.database()) {
-    viewModel.database(name);
-  }
-});
-
 function loadQueryHistory(query_history_id) {
   viewModel.design.history.id(query_history_id);
   viewModel.fetchQueryHistory();
@@ -2598,7 +2592,7 @@ function setupCodeMirrorSubscription() {
 }
 
 // Knockout
-viewModel = new BeeswaxViewModel("${app_name}");
+viewModel = new BeeswaxViewModel("${app_name}", assistHelper);
 ko.applyBindings(viewModel, $("#beeswax-execute")[0]);
 
 shareViewModel = initSharing("#documentShareModal");

+ 1 - 0
apps/spark/src/spark/templates/editor_components.mako

@@ -445,6 +445,7 @@ from desktop.views import _ko
           extraCompleters: completers,
           errors: errors,
           autocompleter: aceAutocompleter,
+          assistHelper: assistHelper,
           openIt: '${ _ko("Double click to open it") }',
           placeholder: $root.snippetPlaceholders[type()] }"></div>
       </div>

+ 17 - 12
desktop/core/src/desktop/static/desktop/js/assistHelper.js

@@ -26,6 +26,11 @@ var TIME_TO_LIVE_IN_MILLIS = 86400000; // 1 day
 function AssistHelper (options) {
   var self = this;
   self.options = options;
+
+  self.activeDatabase = ko.observable();
+  if (typeof options.db !== "undefined") {
+    self.activeDatabase(options.db)
+  }
 }
 
 AssistHelper.prototype.hasExpired = function (timestamp) {
@@ -44,10 +49,10 @@ AssistHelper.prototype.getTotalStorageUserPrefix = function () {
   return app;
 };
 
-AssistHelper.prototype.fetchTableHtmlPreview = function(databaseName, tableName, successCallback, errorCallback) {
+AssistHelper.prototype.fetchTableHtmlPreview = function(tableName, successCallback, errorCallback) {
   var self = this;
   $.ajax({
-    url: "/" + self.options.app + "/api/table/" + databaseName + "/" + tableName,
+    url: "/" + self.options.app + "/api/table/" + self.activeDatabase() + "/" + tableName,
     data: {"sample": true},
     beforeSend: function (xhr) {
       xhr.setRequestHeader("X-Requested-With", "Hue");
@@ -58,7 +63,7 @@ AssistHelper.prototype.fetchTableHtmlPreview = function(databaseName, tableName,
   });
 };
 
-AssistHelper.prototype.refreshTableStats = function(databaseName, tableName, successCallback, errorCallback) {
+AssistHelper.prototype.refreshTableStats = function(tableName, successCallback, errorCallback) {
   var self = this;
   var pollRefresh = function (url) {
     $.post(url, function (data) {
@@ -74,7 +79,7 @@ AssistHelper.prototype.refreshTableStats = function(databaseName, tableName, suc
     }).fail(errorCallback);
   };
 
-  $.post("/" + self.options.app + "/api/analyze/" + databaseName + "/" + tableName + "/", function (data) {
+  $.post("/" + self.options.app + "/api/analyze/" + self.activeDatabase() + "/" + tableName + "/", function (data) {
     if (data.status == 0 && data.watch_url) {
       pollRefresh(data.watch_url);
     } else {
@@ -83,10 +88,10 @@ AssistHelper.prototype.refreshTableStats = function(databaseName, tableName, suc
   }).fail(errorCallback);
 };
 
-AssistHelper.prototype.fetchStats = function(databaseName, tableName, columnName, successCallback, errorCallback) {
+AssistHelper.prototype.fetchStats = function(tableName, columnName, successCallback, errorCallback) {
   var self = this;
   $.ajax({
-    url: "/" + self.options.app + "/api/table/" + databaseName + "/" + tableName + "/stats/" + (columnName || ""),
+    url: "/" + self.options.app + "/api/table/" + self.activeDatabase() + "/" + tableName + "/stats/" + (columnName || ""),
     data: {},
     beforeSend: function (xhr) {
       xhr.setRequestHeader("X-Requested-With", "Hue");
@@ -97,10 +102,10 @@ AssistHelper.prototype.fetchStats = function(databaseName, tableName, columnName
   });
 };
 
-AssistHelper.prototype.fetchTerms = function(databaseName, tableName, columnName, prefixFilter, successCallback, errorCallback) {
+AssistHelper.prototype.fetchTerms = function(tableName, columnName, prefixFilter, successCallback, errorCallback) {
   var self = this;
   $.ajax({
-    url: "/" + self.options.app + "/api/table/" + databaseName + "/" + tableName + "/terms/" + columnName + "/" + (prefixFilter || ""),
+    url: "/" + self.options.app + "/api/table/" + self.activeDatabase() + "/" + tableName + "/terms/" + columnName + "/" + (prefixFilter || ""),
     data: {},
     beforeSend: function (xhr) {
       xhr.setRequestHeader("X-Requested-With", "Hue");
@@ -120,16 +125,16 @@ AssistHelper.prototype.fetchDatabases = function(successCallback, errorCallback)
   }, errorCallback);
 };
 
-AssistHelper.prototype.fetchTables = function(databaseName, successCallback, errorCallback) {
+AssistHelper.prototype.fetchTables = function(successCallback, errorCallback) {
   var self = this;
-  self.fetchAssistData("/" + self.options.app + "/api/autocomplete/" + databaseName, successCallback, errorCallback);
+  self.fetchAssistData("/" + self.options.app + "/api/autocomplete/" + self.activeDatabase(), successCallback, errorCallback);
 };
 
-AssistHelper.prototype.fetchFields = function(databaseName, tableName, fields, successCallback, errorCallback) {
+AssistHelper.prototype.fetchFields = function(tableName, fields, successCallback, errorCallback) {
   var self = this;
 
   var fieldPart = fields.length > 0 ? "/" + fields.join("/") : "";
-  self.fetchAssistData("/" + self.options.app + "/api/autocomplete/" + databaseName + "/" + tableName + fieldPart, successCallback, errorCallback);
+  self.fetchAssistData("/" + self.options.app + "/api/autocomplete/" + self.activeDatabase() + "/" + tableName + fieldPart, successCallback, errorCallback);
 };
 
 AssistHelper.prototype.clearCache = function() {

+ 5 - 10
desktop/core/src/desktop/static/desktop/js/autocomplete.js

@@ -29,17 +29,12 @@ function Autocompleter(options) {
   var self = this;
   self.options = options;
   self.assistHelper = options.assistHelper;
-  self.currentDb = options.db;
   if (typeof options.mode === "undefined" || options.mode === null || options.mode === "beeswax") {
     self.currentMode = "hive";
   } else {
     self.currentMode = options.mode;
   }
 
-  huePubSub.subscribe('hue.assist.databaseChanged', function (db) {
-    self.currentDb = db;
-  });
-
   huePubSub.subscribe('hue.ace.activeMode', function(mode) {
     self.currentMode = mode.split("/").pop();
   })
@@ -96,9 +91,9 @@ Autocompleter.prototype.extractFields = function (data, valuePrefix, includeStar
 Autocompleter.prototype.autocomplete = function(beforeCursor, afterCursor, callback) {
   var self = this;
 
-  if (typeof self.currentDb == "undefined"
-    || self.currentDb == null
-    || self.currentDb == ""
+  if (typeof self.assistHelper.activeDatabase() == "undefined"
+    || self.assistHelper.activeDatabase() == null
+    || self.assistHelper.activeDatabase() == ""
     || (self.currentMode !== "hive" && self.currentMode !== "impala")) {
     callback([]);
     return;
@@ -131,7 +126,7 @@ Autocompleter.prototype.autocomplete = function(beforeCursor, afterCursor, callb
 
 
   if (tableNameAutoComplete || (selectBefore && !fromAfter)) {
-    self.assistHelper.fetchTables(self.currentDb, function(data) {
+    self.assistHelper.fetchTables(function(data) {
       var fromKeyword = "";
       if (selectBefore) {
         if (beforeCursor.indexOf("SELECT") > -1) {
@@ -197,7 +192,7 @@ Autocompleter.prototype.autocomplete = function(beforeCursor, afterCursor, callb
       }
     });
 
-    self.assistHelper.fetchFields(self.currentDb, tableName, fields, function(data) {
+    self.assistHelper.fetchFields(tableName, fields, function(data) {
       callback(self.extractFields(data, "", !fieldTermBefore));
     }, function() {
       callback([]);

+ 2 - 9
desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js

@@ -1662,6 +1662,7 @@ ko.bindingHandlers.aceEditor = {
     var onAfterExec = options.onAfterExec || function () {};
     var onExecute = options.onExecute || function () {};
     var autocompleter = options.autocompleter;
+    var assistHelper = options.assistHelper;
 
     $el.text(options.value());
 
@@ -1887,16 +1888,8 @@ ko.bindingHandlers.aceEditor = {
       }).call(HueLink.prototype);
 
       exports.HueLink = HueLink;
-
     });
 
-
-    var currentAssistDb = "";
-    huePubSub.subscribe('hue.assist.databaseChanged', function (db) {
-      currentAssistDb = db;
-    });
-
-
     HueLink = ace.require("huelink").HueLink;
     editor.hueLink = new HueLink(editor);
     editor.hueLink.on("open", function (token) {
@@ -1907,7 +1900,7 @@ ko.bindingHandlers.aceEditor = {
         window.open("/filebrowser/#" + token.value.replace(/\"/gi, ""));
       }
       else {
-        window.open("/metastore/table/" + currentAssistDb + "/" + token.value);
+        window.open("/metastore/table/" + self.assistHelper.activeDatabase() + "/" + token.value);
       }
     });
 

+ 6 - 14
desktop/core/src/desktop/static/desktop/spec/autocompleteSpec.js

@@ -23,13 +23,8 @@ describe("autocomplete.js", function() {
   };
 
   beforeAll(function() {
-    var totalStorage = {};
     $.totalStorage = function(key, value) {
-      if (typeof value === "undefined") {
-        return totalStorage[key];
-      }
-      totalStorage[key] = value;
-      return value;
+      return null;
     };
 
     jasmine.addMatchers({
@@ -66,9 +61,9 @@ describe("autocomplete.js", function() {
     var options = {
       assistHelper: new AssistHelper({
         app: "testApp",
-        user: "testUser"
-      }),
-      db: "testDb"
+        user: "testUser",
+        db: "testDb"
+      })
     };
     subject = new Autocompleter(options);
     ajaxHelper.responseForUrls = {};
@@ -159,9 +154,9 @@ describe("autocomplete.js", function() {
       var options = {
         assistHelper: new AssistHelper({
           app: "testApp",
-          user: "testUser"
+          user: "testUser",
+          db: "testDb"
         }),
-        db: "testDb",
         mode: "hive"
       };
       subject = new Autocompleter(options);
@@ -247,7 +242,6 @@ describe("autocomplete.js", function() {
       });
     });
 
-    // TODO: Fix me
     it("should suggest columns for table after WHERE", function() {
       assertAutoComplete({
         serverResponses: {
@@ -261,7 +255,6 @@ describe("autocomplete.js", function() {
       });
     });
 
-    // TODO: Fix me
     it("should suggest columns for table after ORDER BY ", function() {
       assertAutoComplete({
         serverResponses: {
@@ -275,7 +268,6 @@ describe("autocomplete.js", function() {
       });
     });
 
-    // TODO: Fix me
     it("should suggest columns for table after ON ", function() {
       assertAutoComplete({
         serverResponses: {

+ 38 - 46
desktop/core/src/desktop/templates/ko_components.mako

@@ -179,7 +179,7 @@ from desktop.views import _ko
           </div>
         </li>
         <li data-bind="visible: ! hasErrors()" >
-          <select data-bind="options: availableDatabaseNames, select2: { width: '100%', placeholder: '${ _ko("Choose a database...") }', update: selectedDatabaseName }" class="input-medium" data-placeholder="${_('Choose a database...')}"></select>
+          <select data-bind="options: availableDatabaseNames, select2: { width: '100%', placeholder: '${ _ko("Choose a database...") }', update: assistHelper.activeDatabase }" class="input-medium" data-placeholder="${_('Choose a database...')}"></select>
         </li>
         <li data-bind="visible: hasErrors">
           <span>${ _('The database list cannot be loaded.') }</span>
@@ -430,11 +430,11 @@ from desktop.views import _ko
         var tableName = hierarchy[1];
 
         $assistQuickLook.find(".tableName").text(self.definition.name);
-        $assistQuickLook.find(".tableLink").attr("href", "/metastore/table/" + databaseName + "/" + tableName);
+        $assistQuickLook.find(".tableLink").attr("href", "/metastore/table/" + self.assistPanel.assistHelper.activeDatabase() + "/" + tableName);
         $assistQuickLook.find(".sample").empty("");
         $assistQuickLook.attr("style", "width: " + ($(window).width() - 120) + "px;margin-left:-" + (($(window).width() - 80) / 2) + "px!important;");
 
-        self.assistPanel.assistHelper.fetchTableHtmlPreview(databaseName, tableName, function(data) {
+        self.assistPanel.assistHelper.fetchTableHtmlPreview(tableName, function(data) {
           $assistQuickLook.find(".loader").hide();
           $assistQuickLook.find(".sample").html(data);
         }, function(e) {
@@ -507,7 +507,7 @@ from desktop.views import _ko
       TableStats.prototype.fetchData = function () {
         var self = this;
         self.loading(true);
-        self.assistHelper.fetchStats(self.database, self.table, self.column != null ? self.column : null, function (data) {
+        self.assistHelper.fetchStats(self.table, self.column != null ? self.column : null, function (data) {
           if (data && data.status == 0) {
             self.statRows(data.stats);
             var inaccurate = true;
@@ -540,7 +540,7 @@ from desktop.views import _ko
         var shouldFetchTerms = self.termsTabActive() || self.terms().length > 0;
         self.refreshing(true);
 
-        self.assistHelper.refreshTableStats(self.database, self.table, function() {
+        self.assistHelper.refreshTableStats(self.table, function() {
           self.refreshing(false);
           self.fetchData();
           if (shouldFetchTerms) {
@@ -559,7 +559,7 @@ from desktop.views import _ko
         }
 
         self.loadingTerms(true);
-        self.assistHelper.fetchTerms(self.database, self.table, self.column, self.prefixFilter(), function (data) {
+        self.assistHelper.fetchTerms(self.table, self.column, self.prefixFilter(), function (data) {
           if (data && data.status == 0) {
             self.terms($.map(data.terms, function (term) {
               return {
@@ -614,6 +614,10 @@ from desktop.views import _ko
         self.databases = ko.observableArray();
         self.selectedDatabase = ko.observable();
 
+        self.loadingTables = ko.computed(function() {
+          return  ! (self.selectedDatabase() && !self.selectedDatabase().loading());
+        });
+
         self.selectedDatabase.subscribe(function(newValue) {
           if (newValue != null) {
             newValue.loadEntries();
@@ -622,46 +626,22 @@ from desktop.views import _ko
 
         // We need the names because select2 does not support objects
         self.availableDatabaseNames = ko.observableArray();
-        self.selectedDatabaseName = ko.observable();
 
-        self.selectedDatabaseName.subscribe(function(name) {
-          if (name == null) {
+        self.fetchDatabases(function() {
+          self.assistHelper.activeDatabase.subscribe(setDatabase);
+
+          if ($.inArray(self.assistHelper.activeDatabase(), self.availableDatabaseNames()) > -1) {
+            setDatabase(self.assistHelper.activeDatabase());
             return;
           }
-          self.options.lastSelectedDb(name);
-
-          // TODO: find a better criteria for showing the spinner
-          // This is deferred so that the spinner gets a chance to be shown
-          self.selectedDatabase(null);
-          window.setTimeout(function () {
-            self.selectedDatabase(ko.utils.arrayFirst(self.databases(), function(database) {
-              return name === database.definition.name;
-            }));
-          }, 10);
-          huePubSub.publish('hue.assist.databaseChanged', name);
-        });
-
-        huePubSub.subscribe('hue.assist.changeDatabase', function(name) {
-          self.selectedDatabaseName(name);
-        });
-
-        self.loadingTables = ko.computed(function() {
-          return  ! (self.selectedDatabase() && !self.selectedDatabase().loading());
-        });
+          if ($.inArray(self.options.lastSelectedDb(), self.availableDatabaseNames()) > -1) {
+            self.assistHelper.activeDatabase(self.options.lastSelectedDb());
+          } else if ($.inArray("default", self.availableDatabaseNames()) > -1) {
+            self.assistHelper.activeDatabase("default");
+          } else if (self.availableDatabaseNames().length > 0) {
+            self.assistHelper.activeDatabase(self.availableDatabaseNames()[0]);
+          }
 
-        self.fetchDatabases(function() {
-          window.setTimeout(function() {
-            if ($.inArray(self.selectedDatabaseName(), self.availableDatabaseNames()) > -1) {
-              return;
-            }
-            if ($.inArray(self.options.lastSelectedDb(), self.availableDatabaseNames()) > -1) {
-              self.selectedDatabaseName(self.options.lastSelectedDb());
-            } else if ($.inArray("default", self.availableDatabaseNames()) > -1) {
-              self.selectedDatabaseName("default");
-            } else if (self.availableDatabaseNames().length > 0) {
-              self.selectedDatabaseName(self.availableDatabaseNames()[0]);
-            }
-          }, 150);
         });
 
         self.modalItem = ko.observable();
@@ -701,6 +681,18 @@ from desktop.views import _ko
         };
       }
 
+      AssistPanel.prototype.setDatabase = function(name) {
+        var self = this;
+        if (name == null) {
+          return;
+        }
+        self.options.lastSelectedDb(name);
+
+        self.selectedDatabase(ko.utils.arrayFirst(self.databases(), function(database) {
+          return name === database.definition.name;
+        }));
+      };
+
       AssistPanel.prototype.toggleSearch = function () {
         var self = this;
         self.options.isSearchVisible(!self.options.isSearchVisible());
@@ -709,11 +701,11 @@ from desktop.views import _ko
       AssistPanel.prototype.reloadAssist = function() {
         var self = this;
         self.reloading(true);
-        var selectedDb = self.selectedDatabaseName();
-        self.selectedDatabaseName(null);
+        self.selectedDatabase(null);
         self.assistHelper.clearCache();
-        self.fetchDatabases();
-        self.selectedDatabaseName(selectedDb);
+        self.fetchDatabases(function() {
+          self.setDatabase(self.options.lastSelectedDb());
+        });
       };
 
       AssistPanel.prototype.fetchDatabases = function(callback) {