Переглянути джерело

[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 роки тому
батько
коміт
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) {