Bläddra i källkod

HUE-8687 [frontend] Move the assist models into the wepback bundle

Johan Ahlen 6 år sedan
förälder
incheckning
9449dc6
24 ändrade filer med 3245 tillägg och 900 borttagningar
  1. 104 102
      desktop/core/src/desktop/js/assist/assistDbEntry.js
  2. 303 0
      desktop/core/src/desktop/js/assist/assistDbNamespace.js
  3. 233 0
      desktop/core/src/desktop/js/assist/assistDbSource.js
  4. 56 68
      desktop/core/src/desktop/js/assist/assistGitEntry.js
  5. 34 40
      desktop/core/src/desktop/js/assist/assistHBaseEntry.js
  6. 111 128
      desktop/core/src/desktop/js/assist/assistStorageEntry.js
  7. 28 0
      desktop/core/src/desktop/js/assist/assistViewModel.js
  8. 1 0
      desktop/core/src/desktop/js/ext/ko.selectize.custom.js
  9. 6 1
      desktop/core/src/desktop/js/hue.js
  10. 293 0
      desktop/core/src/desktop/js/sql/sqlUtils.js
  11. 0 519
      desktop/core/src/desktop/static/desktop/js/assist/assistDbSource.js
  12. 1 1
      desktop/core/src/desktop/static/desktop/js/autocomplete/sqlParseSupport.js
  13. 2040 0
      desktop/core/src/desktop/static/desktop/js/hue-bundle-8e5037d20b056ab61e91.js
  14. 0 0
      desktop/core/src/desktop/static/desktop/js/hue-bundle-8e5037d20b056ab61e91.js.map
  15. 0 0
      desktop/core/src/desktop/static/desktop/js/hue-bundle-fbeb7ee72f1233caa7d8.js.map
  16. 10 10
      desktop/core/src/desktop/static/desktop/js/ko.hue-bindings.js
  17. 9 9
      desktop/core/src/desktop/static/desktop/js/sqlAutocompleter3.js
  18. 5 5
      desktop/core/src/desktop/static/desktop/js/sqlUtils.js
  19. 0 5
      desktop/core/src/desktop/templates/assist.mako
  20. 0 1
      desktop/core/src/desktop/templates/hue.mako
  21. 3 3
      desktop/core/src/desktop/templates/ko_components/ko_context_popover.mako
  22. 4 4
      desktop/core/src/desktop/templates/ko_components/ko_simple_ace_editor.mako
  23. 3 3
      desktop/libs/indexer/src/indexer/templates/importer.mako
  24. 1 1
      webpack-stats.json

+ 104 - 102
desktop/core/src/desktop/static/desktop/js/assist/assistDbEntry.js → desktop/core/src/desktop/js/assist/assistDbEntry.js

@@ -14,7 +14,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-var AssistDbEntry = (function () {
+import $ from 'jquery'
+import ko from 'knockout'
+
+import huePubSub from '../utils/huePubSub'
+import sqlUtils from '../sql/sqlUtils'
+
+const findNameInHierarchy = (entry, searchCondition) => {
+  let sourceType = entry.sourceType;
+  while (entry && !searchCondition(entry)) {
+    entry = entry.parent;
+  }
+  if (entry) {
+    return sqlUtils.backTickIfNeeded(sourceType, entry.catalogEntry.name);
+  }
+};
+
+class AssistDbEntry {
   /**
    * @param {DataCatalogEntry} catalogEntry
    * @param {AssistDbEntry} parent
@@ -26,8 +42,8 @@ var AssistDbEntry = (function () {
    * @param {Object} navigationSettings
    * @constructor
    */
-  function AssistDbEntry (catalogEntry, parent, assistDbNamespace, filter, i18n, navigationSettings) {
-    var self = this;
+  constructor (catalogEntry, parent, assistDbNamespace, filter, i18n, navigationSettings) {
+    let self = this;
     self.catalogEntry = catalogEntry;
     self.parent = parent;
     self.assistDbNamespace = assistDbNamespace;
@@ -65,28 +81,26 @@ var AssistDbEntry = (function () {
       }
     }
 
-    self.open.subscribe(function(newValue) {
+    self.open.subscribe(newValue => {
       if (newValue && self.entries().length === 0) {
         self.loadEntries();
       }
     });
 
-    self.hasEntries = ko.pureComputed(function() {
-      return self.entries().length > 0;
-    });
+    self.hasEntries = ko.pureComputed(() => self.entries().length > 0)
 
-    self.filteredEntries = ko.pureComputed(function () {
-      var facets = self.filter.querySpec().facets;
-      var facetMatch = !facets || Object.keys(facets).length === 0 || !facets['type']; // So far only type facet is used for SQL
+    self.filteredEntries = ko.pureComputed(() => {
+      let facets = self.filter.querySpec().facets;
+      let facetMatch = !facets || Object.keys(facets).length === 0 || !facets['type']; // So far only type facet is used for SQL
       // Only text match on tables/views or columns if flag is set
-      var textMatch = (!self.catalogEntry.isDatabase() && !self.filterColumnNames()) || (!self.filter.querySpec().text || self.filter.querySpec().text.length === 0);
+      let textMatch = (!self.catalogEntry.isDatabase() && !self.filterColumnNames()) || (!self.filter.querySpec().text || self.filter.querySpec().text.length === 0);
 
       if (facetMatch && textMatch) {
         return self.entries();
       }
 
-      return self.entries().filter(function (entry) {
-        var match = true;
+      return self.entries().filter(entry => {
+        let match = true;
 
         if (match && !facetMatch) {
           if (entry.catalogEntry.isField()) {
@@ -99,20 +113,18 @@ var AssistDbEntry = (function () {
         }
 
         if (match && !textMatch) {
-          var nameLower = entry.catalogEntry.name.toLowerCase();
-          match = self.filter.querySpec().text.every(function (text) {
-            return nameLower.indexOf(text.toLowerCase()) !== -1
-          });
+          let nameLower = entry.catalogEntry.name.toLowerCase();
+          match = self.filter.querySpec().text.every(text => nameLower.indexOf(text.toLowerCase()) !== -1)
         }
 
         return match;
       });
     });
 
-    self.autocompleteFromEntries = function (nonPartial, partial) {
-      var result = [];
-      var partialLower = partial.toLowerCase();
-      self.entries().forEach(function (entry) {
+    self.autocompleteFromEntries = (nonPartial, partial) => {
+      let result = [];
+      let partialLower = partial.toLowerCase();
+      self.entries().forEach(entry => {
         if (entry.catalogEntry.name.toLowerCase().indexOf(partialLower) === 0) {
           result.push(nonPartial + partial + entry.catalogEntry.name.substring(partial.length))
         }
@@ -133,7 +145,7 @@ var AssistDbEntry = (function () {
       self.columnName = self.catalogEntry.name;
     }
 
-    self.editorText = ko.pureComputed(function () {
+    self.editorText = ko.pureComputed(() => {
       if (self.catalogEntry.isTableOrView()) {
         return self.getTableName();
       }
@@ -144,21 +156,11 @@ var AssistDbEntry = (function () {
     });
   }
 
-  var findNameInHierarchy = function (entry, searchCondition) {
-    var sourceType = entry.sourceType;
-    while (entry && !searchCondition(entry)) {
-      entry = entry.parent;
-    }
-    if (entry) {
-      return SqlUtils.backTickIfNeeded(sourceType, entry.catalogEntry.name);
-    }
-  };
-
-  AssistDbEntry.prototype.knownFacetValues = function () {
-    var self = this;
-    var types = {};
+  knownFacetValues() {
+    let self = this;
+    let types = {};
     if (self.parent === null) { // Only find facets on the DB level
-      self.entries().forEach(function (tableEntry) {
+      self.entries().forEach(tableEntry => {
         if (!self.assistDbNamespace.nonSqlType) {
           if (tableEntry.catalogEntry.isTable()) {
             types.table =  types.table ? types.table + 1 : 1;
@@ -167,7 +169,7 @@ var AssistDbEntry = (function () {
           }
         }
         if (tableEntry.open()) {
-          tableEntry.entries().forEach(function (colEntry) {
+          tableEntry.entries().forEach(colEntry => {
             if (!types[colEntry.catalogEntry.getType()]) {
               types[colEntry.catalogEntry.getType()] = 1;
             } else {
@@ -183,22 +185,22 @@ var AssistDbEntry = (function () {
     return {};
   };
 
-  AssistDbEntry.prototype.getDatabaseName = function () {
-    return findNameInHierarchy(this, function (entry) { return entry.catalogEntry.isDatabase() });
+  getDatabaseName() {
+    return findNameInHierarchy(this, entry => entry.catalogEntry.isDatabase());
   };
 
-  AssistDbEntry.prototype.getTableName = function () {
-    return findNameInHierarchy(this, function (entry) { return entry.catalogEntry.isTableOrView() });
+  getTableName() {
+    return findNameInHierarchy(this, entry => entry.catalogEntry.isTableOrView());
   };
 
-  AssistDbEntry.prototype.getColumnName = function () {
-    return findNameInHierarchy(this, function (entry) { return entry.catalogEntry.isColumn() });
+  getColumnName() {
+    return findNameInHierarchy(this, entry => entry.catalogEntry.isColumn());
   };
 
-  AssistDbEntry.prototype.getComplexName = function () {
-    var entry = this;
-    var sourceType = entry.sourceType;
-    var parts = [];
+  getComplexName() {
+    let entry = this;
+    let sourceType = entry.sourceType;
+    let parts = [];
     while (entry != null) {
       if (entry.catalogEntry.isTableOrView()) {
         break;
@@ -208,7 +210,7 @@ var AssistDbEntry = (function () {
           parts.push("[]");
         }
       } else {
-        parts.push(SqlUtils.backTickIfNeeded(sourceType, entry.catalogEntry.name));
+        parts.push(sqlUtils.backTickIfNeeded(sourceType, entry.catalogEntry.name));
         parts.push(".");
       }
       entry = entry.parent;
@@ -217,10 +219,10 @@ var AssistDbEntry = (function () {
     return parts.slice(1).join("");
   };
 
-  AssistDbEntry.prototype.showContextPopover = function (entry, event, positionAdjustment) {
-    var self = this;
-    var $source = $(event.target);
-    var offset = $source.offset();
+  showContextPopover(entry, event, positionAdjustment) {
+    let self = this;
+    let $source = $(event.target);
+    let offset = $source.offset();
     if (positionAdjustment) {
       offset.left += positionAdjustment.left;
       offset.top += positionAdjustment.top;
@@ -243,22 +245,22 @@ var AssistDbEntry = (function () {
         bottom: offset.top + $source.height() - 3
       }
     });
-    huePubSub.subscribeOnce('context.popover.hidden', function () {
+    huePubSub.subscribeOnce('context.popover.hidden', () => {
       self.statsVisible(false);
     });
   };
 
-  AssistDbEntry.prototype.triggerRefresh = function () {
-    var self = this;
+  triggerRefresh() {
+    let self = this;
     self.catalogEntry.clearCache({ invalidate: self.invalidateOnRefresh(), cascade: true });
   };
 
-  AssistDbEntry.prototype.highlightInside = function (path) {
-    var self = this;
+  highlightInside(path) {
+    let self = this;
 
-    var searchEntry = function () {
-      var foundEntry;
-      $.each(self.entries(), function (idx, entry) {
+    let searchEntry = () => {
+      let foundEntry;
+      self.entries().forEach(entry => {
         entry.highlight(false);
         if (entry.catalogEntry.name === path[0]) {
           foundEntry = entry;
@@ -269,14 +271,14 @@ var AssistDbEntry = (function () {
           foundEntry.open(true);
         }
 
-        window.setTimeout(function () {
+        window.setTimeout(() => {
           if (path.length > 1) {
             foundEntry.highlightInside(path.slice(1));
           } else {
-            huePubSub.subscribeOnce('assist.db.scrollToComplete', function () {
+            huePubSub.subscribeOnce('assist.db.scrollToComplete', () => {
               foundEntry.highlight(true);
               // Timeout is for animation effect
-              window.setTimeout(function () {
+              window.setTimeout(() => {
                 foundEntry.highlight(false);
               }, 1800);
             });
@@ -288,7 +290,7 @@ var AssistDbEntry = (function () {
 
     if (self.entries().length === 0) {
       if (self.loading()) {
-        var subscription = self.loading.subscribe(function (newVal) {
+        let subscription = self.loading.subscribe(newVal => {
           if (!newVal) {
             subscription.dispose();
             searchEntry();
@@ -302,19 +304,19 @@ var AssistDbEntry = (function () {
     }
   };
 
-  AssistDbEntry.prototype.loadEntries = function(callback) {
-    var self = this;
+  loadEntries(callback) {
+    let self = this;
     if (!self.expandable || self.loading()) {
       return;
     }
     self.loading(true);
 
-    var loadEntriesDeferred = $.Deferred();
+    let loadEntriesDeferred = $.Deferred();
 
-    var successCallback = function(sourceMeta) {
+    let successCallback = sourceMeta => {
       self.entries([]);
       if (!sourceMeta.notFound) {
-        self.catalogEntry.getChildren({ silenceErrors: self.navigationSettings.rightAssist }).done(function (catalogEntries) {
+        self.catalogEntry.getChildren({ silenceErrors: self.navigationSettings.rightAssist }).done( catalogEntries => {
           self.hasErrors(false);
           self.loading(false);
           self.loaded = true;
@@ -322,8 +324,8 @@ var AssistDbEntry = (function () {
             self.entries([]);
             return;
           }
-          var newEntries = [];
-          catalogEntries.forEach(function (catalogEntry) {
+          let newEntries = [];
+          catalogEntries.forEach(catalogEntry => {
             newEntries.push(self.createEntry(catalogEntry));
           });
           if (sourceMeta.type === 'array') {
@@ -337,7 +339,7 @@ var AssistDbEntry = (function () {
           if (typeof callback === 'function') {
             callback();
           }
-        }).fail(function () {
+        }).fail(() => {
           self.loading(false);
           self.loaded = true;
           self.hasErrors(true);
@@ -356,7 +358,7 @@ var AssistDbEntry = (function () {
       }
     };
 
-    var errorCallback = function () {
+    let errorCallback = () => {
       self.hasErrors(true);
       self.loading(false);
       self.loaded = true;
@@ -364,10 +366,10 @@ var AssistDbEntry = (function () {
     };
 
     if (!self.navigationSettings.rightAssist && HAS_OPTIMIZER && (self.catalogEntry.isTable() || self.catalogEntry.isDatabase()) && !self.assistDbNamespace.nonSqlType) {
-      self.catalogEntry.loadNavOptPopularityForChildren({ silenceErrors: true }).done(function () {
-        loadEntriesDeferred.done(function () {
+      self.catalogEntry.loadNavOptPopularityForChildren({ silenceErrors: true }).done(() => {
+        loadEntriesDeferred.done(() => {
           if (!self.hasErrors()) {
-            self.entries().forEach(function (entry) {
+            self.entries().forEach(entry => {
               if (entry.catalogEntry.navOptPopularity) {
                 if (entry.catalogEntry.navOptPopularity.popularity) {
                   entry.popularity(entry.catalogEntry.navOptPopularity.popularity)
@@ -389,18 +391,18 @@ var AssistDbEntry = (function () {
   /**
    * @param {DataCatalogEntry} catalogEntry
    */
-  AssistDbEntry.prototype.createEntry = function (catalogEntry) {
-    var self = this;
+  createEntry(catalogEntry) {
+    let self = this;
     return new AssistDbEntry(catalogEntry, self, self.assistDbNamespace, self.filter, self.i18n, self.navigationSettings)
   };
 
-  AssistDbEntry.prototype.getHierarchy = function () {
-    var self = this;
+  getHierarchy() {
+    let self = this;
     return self.catalogEntry.path.concat();
   };
 
-  AssistDbEntry.prototype.dblClick = function () {
-    var self = this;
+  dblClick() {
+    let self = this;
     if (self.catalogEntry.isTableOrView()) {
       huePubSub.publish('editor.insert.table.at.cursor', { name: self.getTableName(), database: self.getDatabaseName() });
     } else if (self.catalogEntry.isColumn()) {
@@ -410,8 +412,8 @@ var AssistDbEntry = (function () {
     }
   };
 
-  AssistDbEntry.prototype.explore = function (isSolr) {
-    var self = this;
+  explore(isSolr) {
+    let self = this;
     if (isSolr) {
       huePubSub.publish('open.link', '/hue/dashboard/browse/' + self.catalogEntry.name);
     }
@@ -420,9 +422,9 @@ var AssistDbEntry = (function () {
     }
   };
 
-  AssistDbEntry.prototype.openInMetastore = function () {
-    var self = this;
-    var url;
+  openInMetastore() {
+    let self = this;
+    let url;
     if (self.catalogEntry.isDatabase()) {
       url = '/metastore/tables/' + self.catalogEntry.name + '?source=' + self.catalogEntry.getSourceType() + '&namespace=' + self.catalogEntry.namespace.id;
     } else if (self.catalogEntry.isTableOrView()) {
@@ -438,24 +440,24 @@ var AssistDbEntry = (function () {
     }
   };
 
-  AssistDbEntry.prototype.openInIndexer = function () {
-    var self = this;
-    var definitionName = self.catalogEntry.name;
-    if (IS_NEW_INDEXER_ENABLED) {
-      if (IS_HUE_4) {
+  openInIndexer() {
+    let self = this;
+    let definitionName = self.catalogEntry.name;
+    if (window.IS_NEW_INDEXER_ENABLED) {
+      if (window.IS_HUE_4) {
         huePubSub.publish('open.link', '/indexer/indexes/' + definitionName);
       } else {
         window.open('/indexer/indexes/' + definitionName);
       }
     } else {
-      var hash = '#edit/' + definitionName;
-      if (IS_HUE_4) {
+      let hash = '#edit/' + definitionName;
+      if (window.IS_HUE_4) {
         if (window.location.pathname.startsWith('/hue/indexer') && !window.location.pathname.startsWith('/hue/indexer/importer')) {
           window.location.hash = hash;
         } else {
-          huePubSub.subscribeOnce('app.gained.focus', function (app) {
+          huePubSub.subscribeOnce('app.gained.focus', app => {
             if (app === 'indexes') {
-              window.setTimeout(function () {
+              window.setTimeout(() => {
                 window.location.hash = hash;
               }, 0)
             }
@@ -468,13 +470,13 @@ var AssistDbEntry = (function () {
     }
   };
 
-  AssistDbEntry.prototype.toggleOpen = function () {
-    var self = this;
+  toggleOpen() {
+    let self = this;
     self.open(!self.open());
   };
 
-  AssistDbEntry.prototype.openItem = function () {
-    var self = this;
+  openItem() {
+    let self = this;
     if (self.catalogEntry.isTableOrView()) {
       huePubSub.publish('assist.table.selected', {
         sourceType: self.assistDbNamespace.sourceType,
@@ -490,6 +492,6 @@ var AssistDbEntry = (function () {
       })
     }
   };
+}
 
-  return AssistDbEntry;
-})();
+export default AssistDbEntry;

+ 303 - 0
desktop/core/src/desktop/js/assist/assistDbNamespace.js

@@ -0,0 +1,303 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import $ from 'jquery'
+import ko from 'knockout'
+
+import AssistDbEntry from './assistDbEntry'
+import dataCatalog from '../catalog/dataCatalog'
+import huePubSub from '../utils/huePubSub'
+
+class AssistDbNamespace {
+
+  /**
+   * @param {Object} options
+   * @param {Object} options.i18n
+   * @param {string} options.sourceType
+   * @param {ContextNamespace} options.namespace
+   * @param {boolean} options.nonSqlType - Optional, default false
+   * @param {Object} options.navigationSettings
+   * @constructor
+   */
+  constructor(options) {
+    let self = this;
+
+    self.i18n = options.i18n;
+    self.navigationSettings = options.navigationSettings;
+    self.sourceType = options.sourceType;
+    self.nonSqlType = options.nonSqlType;
+
+    self.namespace = options.namespace;
+    self.status = ko.observable(options.namespace.status);
+    // TODO: Compute selection in assist?
+    self.compute = ko.observable();
+    if (self.namespace.computes.length) {
+      self.compute(self.namespace.computes[0]);
+    }
+    self.name = options.namespace.name;
+
+    self.dbIndex = {};
+    self.databases = ko.observableArray();
+    self.selectedDatabase = ko.observable();
+
+    self.highlight = ko.observable(false);
+
+    self.loadedPromise = $.Deferred();
+    self.loadedDeferred = $.Deferred();
+    self.loaded = ko.observable(false);
+    self.loading = ko.observable(false);
+    self.reloading = ko.observable(false);
+    self.hasErrors = ko.observable(false);
+    self.invalidateOnRefresh = ko.observable('cache');
+
+    self.loadingTables = ko.pureComputed(() => typeof self.selectedDatabase() !== 'undefined' && self.selectedDatabase() !== null && self.selectedDatabase().loading());
+
+    self.filter = {
+      querySpec: ko.observable({}).extend({ rateLimit: 300 })
+    };
+
+    self.hasEntries = ko.pureComputed(() => self.databases().length > 0);
+
+    self.filteredEntries = ko.pureComputed(() => {
+      if (!self.filter.querySpec() || typeof self.filter.querySpec().query === 'undefined' || !self.filter.querySpec().query) {
+        return self.databases();
+      }
+      return self.databases().filter(database => database.catalogEntry.name.toLowerCase().indexOf(self.filter.querySpec().query.toLowerCase()) !== -1);
+    });
+
+    self.autocompleteFromEntries = (nonPartial, partial) => {
+      let result = [];
+      let partialLower = partial.toLowerCase();
+      self.databases().forEach(db => {
+        if (db.catalogEntry.name.toLowerCase().indexOf(partialLower) === 0) {
+          result.push(nonPartial + partial + db.catalogEntry.name.substring(partial.length))
+        }
+      });
+      return result;
+    };
+
+    self.selectedDatabase.subscribe(() => {
+      let db = self.selectedDatabase();
+      if (window.HAS_OPTIMIZER && db && !db.popularityIndexSet && !self.nonSqlType) {
+        db.catalogEntry.loadNavOptPopularityForChildren({ silenceErrors: true }).done(() => {
+          let applyPopularity = () => {
+            db.entries().forEach(entry => {
+              if (entry.catalogEntry.navOptPopularity && entry.catalogEntry.navOptPopularity.popularity >= 5) {
+                entry.popularity(entry.catalogEntry.navOptPopularity.popularity )
+              }
+            });
+          };
+
+          if (db.loading()) {
+            let subscription = db.loading.subscribe(() => {
+              subscription.dispose();
+              applyPopularity();
+            });
+          } else if (db.entries().length === 0) {
+            let subscription = db.entries.subscribe(newEntries => {
+              if (newEntries.length > 0) {
+                subscription.dispose();
+                applyPopularity();
+              }
+            });
+          } else {
+            applyPopularity();
+          }
+        });
+      }
+    });
+
+    self.selectedDatabaseChanged = () => {
+      if (self.selectedDatabase()) {
+        if (!self.selectedDatabase().hasEntries() && !self.selectedDatabase().loading()) {
+          self.selectedDatabase().loadEntries()
+        }
+        if (!self.navigationSettings.rightAssist) {
+          window.apiHelper.setInTotalStorage('assist_' + self.sourceType + '_' + self.namespace.id, 'lastSelectedDb', self.selectedDatabase().catalogEntry.name);
+          huePubSub.publish('assist.database.set', {
+            sourceType: self.sourceType,
+            namespace: self.namespace,
+            name: self.selectedDatabase().catalogEntry.name
+          })
+        }
+      }
+    };
+
+    let nestedFilter = {
+      querySpec: ko.observable({}).extend({ rateLimit: 300 })
+    };
+
+    self.setDatabase = databaseName => {
+      if (databaseName && self.selectedDatabase() && databaseName === self.selectedDatabase().catalogEntry.name) {
+        return;
+      }
+      if (databaseName && self.dbIndex[databaseName]) {
+        self.selectedDatabase(self.dbIndex[databaseName]);
+        self.selectedDatabaseChanged();
+        return;
+      }
+      let lastSelectedDb = window.apiHelper.getFromTotalStorage('assist_' + self.sourceType + '_' + self.namespace.id, 'lastSelectedDb', 'default');
+      if (lastSelectedDb && self.dbIndex[lastSelectedDb]) {
+        self.selectedDatabase(self.dbIndex[lastSelectedDb]);
+        self.selectedDatabaseChanged();
+      } else if (self.databases().length > 0) {
+        self.selectedDatabase(self.databases()[0]);
+        self.selectedDatabaseChanged();
+      }
+    };
+
+    self.initDatabases = callback => {
+      if (self.loading()) {
+        return;
+      }
+      self.loading(true);
+      self.hasErrors(false);
+
+      let lastSelectedDbName = self.selectedDatabase() ? self.selectedDatabase().catalogEntry.name : null;
+
+      self.selectedDatabase(null);
+      self.databases([]);
+
+      dataCatalog.getEntry({ sourceType: self.sourceType, namespace: self.namespace, compute: self.compute(), path : [], definition: { type: 'source' } }).done(catalogEntry => {
+        self.catalogEntry = catalogEntry;
+        self.catalogEntry.getChildren({ silenceErrors: self.navigationSettings.rightAssist }).done(databaseEntries => {
+          self.dbIndex = {};
+          let hasNavMeta = false;
+          let dbs = [];
+
+          databaseEntries.forEach(catalogEntry => {
+            hasNavMeta = hasNavMeta || !!catalogEntry.navigatorMeta;
+            let database = new AssistDbEntry(catalogEntry, null, self, nestedFilter, self.i18n, self.navigationSettings);
+            self.dbIndex[catalogEntry.name] = database;
+            if (catalogEntry.name === lastSelectedDbName) {
+              self.selectedDatabase(database);
+              self.selectedDatabaseChanged();
+            }
+            dbs.push(database);
+          });
+
+          if (!hasNavMeta && !self.nonSqlType) {
+            self.catalogEntry.loadNavigatorMetaForChildren({ silenceErrors: true });
+          }
+          self.databases(dbs);
+
+          if (typeof callback === 'function') {
+            callback();
+          }
+        }).fail(() => {
+          self.hasErrors(true);
+        }).always(() => {
+          self.loaded(true);
+          self.loadedDeferred.resolve();
+          self.loading(false);
+          self.reloading(false);
+        });
+      });
+    };
+
+    self.modalItem = ko.observable();
+
+    if (!self.navigationSettings.rightAssist) {
+      huePubSub.subscribe('data.catalog.entry.refreshed', details => {
+        if (self.namespace.id !== details.entry.namespace.id || details.entry.getSourceType() !== self.sourceType) {
+          return;
+        }
+        if (self.catalogEntry === details.entry) {
+          self.initDatabases();
+        } else {
+          let findAndReloadInside = entries => {
+            return entries.some(entry => {
+              if (entry.catalogEntry.path.join('.') === details.entry.path.join('.')) {
+                entry.catalogEntry = details.entry;
+                entry.loadEntries();
+                return true;
+              }
+              return findAndReloadInside(entry.entries());
+            })
+          };
+          findAndReloadInside(self.databases());
+        }
+      });
+    }
+  }
+
+  whenLoaded(callback) {
+    let self = this;
+    self.loadedDeferred.done(callback);
+  };
+
+  highlightInside(catalogEntry) {
+    let self = this;
+    let foundDb;
+    let index;
+
+    let findDatabase = () => {
+      $.each(self.databases(), (idx, db) => {
+        db.highlight(false);
+        if (db.databaseName === catalogEntry.path[0]) {
+          foundDb = db;
+          index = idx;
+        }
+      });
+
+      if (foundDb) {
+        let whenLoaded = () => {
+          if (self.selectedDatabase() !== foundDb) {
+            self.selectedDatabase(foundDb);
+          }
+          if (!foundDb.open()) {
+            foundDb.open(true);
+          }
+          window.setTimeout(() => {
+            huePubSub.subscribeOnce('assist.db.scrollToComplete', () => {
+              foundDb.highlight(true);
+              // Timeout is for animation effect
+              window.setTimeout(() => {
+                foundDb.highlight(false);
+              }, 1800);
+            });
+            if (catalogEntry.path.length > 1) {
+              foundDb.highlightInside(catalogEntry.path.slice(1));
+            } else {
+              huePubSub.publish('assist.db.scrollTo', foundDb);
+            }
+          }, 0);
+        };
+
+        if (foundDb.hasEntries()) {
+          whenLoaded();
+        } else {
+          foundDb.loadEntries(whenLoaded);
+        }
+      }
+    };
+
+    if (!self.loaded()) {
+      self.initDatabases(findDatabase);
+    } else {
+      findDatabase();
+    }
+  };
+
+  triggerRefresh() {
+    let self = this;
+    if (self.catalogEntry) {
+      self.catalogEntry.clearCache({ invalidate: self.invalidateOnRefresh() });
+    }
+  };
+}
+
+export default AssistDbNamespace;

+ 233 - 0
desktop/core/src/desktop/js/assist/assistDbSource.js

@@ -0,0 +1,233 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import $ from 'jquery'
+import ko from 'knockout'
+
+import AssistDbNamespace from './assistDbNamespace'
+import apiHelper from '../api/apiHelper'
+import contextCatalog from '../catalog/contextCatalog'
+import huePubSub from '../utils/huePubSub'
+
+class AssistDbSource {
+
+  /**
+   * @param {Object} options
+   * @param {Object} options.i18n
+   * @param {string} options.type
+   * @param {ContextNamespace} [options.initialNamespace] - Optional initial namespace to use
+   * @param {ContextCompute} [options.initialCompute] - Optional initial compute to use
+   * @param {string} options.name
+   * @param {boolean} options.nonSqlType - Optional, default false
+   * @param {Object} options.navigationSettings
+   * @constructor
+   */
+  constructor(options) {
+    let self = this;
+
+    self.sourceType = options.type;
+    self.name = options.name;
+    self.i18n = options.i18n;
+    self.nonSqlType = options.nonSqlType;
+    self.navigationSettings = options.navigationSettings;
+    self.initialNamespace = options.initialNamespace || apiHelper.getFromTotalStorage('contextSelector', 'lastSelectedNamespace');
+    self.initialCompute = options.initialCompute || apiHelper.getFromTotalStorage('contextSelector', 'lastSelectedCompute');
+
+    self.selectedNamespace = ko.observable();
+    self.namespaces = ko.observableArray();
+
+    self.loadedDeferred = $.Deferred();
+    self.loading = ko.observable(false);
+    self.hasErrors = ko.observable(false);
+
+    self.filter = {
+      querySpec: ko.observable({}).extend({ rateLimit: 300 })
+    };
+
+    self.filteredNamespaces = ko.pureComputed(() => {
+      if (!self.filter.querySpec() || typeof self.filter.querySpec().query === 'undefined' || !self.filter.querySpec().query) {
+        return self.namespaces();
+      }
+      return self.namespaces().filter(namespace => namespace.name.toLowerCase().indexOf(self.filter.querySpec().query.toLowerCase()) !== -1);
+    });
+
+    self.autocompleteFromNamespaces = (nonPartial, partial) => {
+      let result = [];
+      let partialLower = partial.toLowerCase();
+      self.namespaces().forEach(namespace => {
+        if (namespace.name.toLowerCase().indexOf(partialLower) === 0) {
+          result.push(nonPartial + partial + namespace.name.substring(partial.length))
+        }
+      });
+      return result;
+    };
+
+    let ensureDbSet = () => {
+      if (self.nonSqlType) {
+        if (!self.selectedNamespace().selectedDatabase()) {
+          self.selectedNamespace().selectedDatabase(self.selectedNamespace().databases()[0]);
+          self.selectedNamespace().selectedDatabaseChanged();
+        }
+      }
+    };
+
+    self.selectedNamespace.subscribe(namespace => {
+      if (namespace && !namespace.loaded() && !namespace.loading()) {
+        namespace.initDatabases(ensureDbSet);
+      } else {
+        ensureDbSet();
+      }
+    });
+
+    self.hasNamespaces = ko.pureComputed(()  => self.namespaces().length > 0);
+
+    huePubSub.subscribe('context.catalog.namespaces.refreshed', sourceType => {
+      if (self.sourceType !== sourceType) {
+        return;
+      }
+
+      self.loading(true);
+      contextCatalog.getNamespaces({ sourceType: self.sourceType }).done(context => {
+        let newNamespaces = [];
+        let existingNamespaceIndex = {};
+        self.namespaces().forEach(assistNamespace => {
+          existingNamespaceIndex[assistNamespace.namespace.id] = assistNamespace;
+        });
+        context.namespaces.forEach(newNamespace => {
+          if (existingNamespaceIndex[newNamespace.id]) {
+            existingNamespaceIndex[newNamespace.id].namespace = newNamespace;
+            existingNamespaceIndex[newNamespace.id].name = newNamespace.name;
+            existingNamespaceIndex[newNamespace.id].status(newNamespace.status);
+            newNamespaces.push(existingNamespaceIndex[newNamespace.id]);
+          } else {
+            newNamespaces.push(new AssistDbNamespace({
+              sourceType: self.sourceType,
+              namespace: newNamespace,
+              i18n: self.i18n,
+              nonSqlType: self.nonSqlType,
+              navigationSettings: self.navigationSettings
+            }));
+          }
+        });
+        self.namespaces(newNamespaces);
+      }).always(() => {
+        self.loading(false);
+      })
+    });
+  }
+
+  whenLoaded(callback) {
+    let self = this;
+    self.loadedDeferred.done(callback);
+  };
+
+  loadNamespaces(refresh) {
+    let self = this;
+    self.loading(true);
+
+    if (refresh) {
+      contextCatalog.getComputes({ sourceType: self.sourceType, clearCache: true });
+    }
+
+    return contextCatalog.getNamespaces({ sourceType: self.sourceType, clearCache: refresh }).done(context => {
+      let assistNamespaces = [];
+      let activeNamespace;
+      let activeCompute;
+      context.namespaces.forEach(namespace => {
+        let assistNamespace = new AssistDbNamespace({
+          sourceType: self.sourceType,
+          namespace: namespace,
+          i18n: self.i18n,
+          nonSqlType: self.nonSqlType,
+          navigationSettings: self.navigationSettings
+        });
+
+        if (self.initialNamespace && namespace.id === self.initialNamespace.id) {
+          activeNamespace = assistNamespace;
+          if (self.initialCompute) {
+            activeNamespace.namespace.computes.some(compute => {
+              if (compute.id === self.initialCompute.id) {
+                activeCompute = compute;
+              }
+            })
+          }
+        }
+        assistNamespaces.push(assistNamespace);
+      });
+      self.namespaces(assistNamespaces);
+      if (!refresh) {
+        if (activeNamespace) {
+          self.selectedNamespace(activeNamespace);
+        } else if (assistNamespaces.length) {
+          self.selectedNamespace(assistNamespaces[0]);
+        }
+        if (activeCompute) {
+          self.selectedNamespace().compute(activeCompute);
+        } else if (self.selectedNamespace() && self.selectedNamespace().namespace && self.selectedNamespace().namespace.computes && self.selectedNamespace().namespace.computes.length) {
+          self.selectedNamespace().compute(self.selectedNamespace().namespace.computes[0]);
+        }
+      }
+    }).fail(() => {
+      self.hasErrors(true);
+    }).always(() => {
+      self.loadedDeferred.resolve();
+      self.loading(false);
+    })
+  };
+
+  highlightInside(catalogEntry) {
+    let self = this;
+    if (self.navigationSettings.rightAssist) {
+      return;
+    }
+
+    let whenLoaded = () => {
+      self.namespaces().some(namespace => {
+        if (namespace.namespace.id === catalogEntry.namespace.id) {
+          if (self.selectedNamespace() !== namespace) {
+            self.selectedNamespace(namespace);
+          }
+          if (self.selectedNamespace().hasEntries()) {
+            self.selectedNamespace().highlightInside(catalogEntry);
+          } else {
+            self.selectedNamespace().initDatabases(() => {
+              self.selectedNamespace().highlightInside(catalogEntry);
+            })
+          }
+          return true;
+        }
+      })
+    };
+
+    if (self.namespaces().length) {
+      whenLoaded();
+    } else if (self.loading()) {
+      let loadingSub = self.loading.subscribe(() => {
+        loadingSub.dispose();
+        whenLoaded();
+      })
+    } else {
+      self.loadNamespaces().done(whenLoaded);
+    }
+  };
+
+  triggerRefresh() {
+    let self = this;
+    self.loadNamespaces(true);
+  };
+}
+
+export default AssistDbSource;

+ 56 - 68
desktop/core/src/desktop/static/desktop/js/assist/assistGitEntry.js → desktop/core/src/desktop/js/assist/assistGitEntry.js

@@ -14,7 +14,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-var AssistGitEntry = (function () {
+import ko from 'knockout'
+
+import huePubSub from '../utils/huePubSub'
+import apiHelper from '../api/apiHelper'
+
+class AssistGitEntry {
 
   /**
    * @param {object} options
@@ -22,14 +27,12 @@ var AssistGitEntry = (function () {
    * @param {string} options.definition.name
    * @param {string} options.definition.type (file, dir)
    * @param {AssistGitEntry} options.parent
-   * @param {ApiHelper} options.apiHelper
    * @constructor
    */
-  function AssistGitEntry (options) {
-    var self = this;
+  constructor(options) {
+    let self = this;
 
     self.definition = options.definition;
-    self.apiHelper = options.apiHelper;
     self.parent = options.parent;
     self.path = '';
     if (self.parent !== null) {
@@ -50,97 +53,82 @@ var AssistGitEntry = (function () {
     self.hasErrors = ko.observable(false);
     self.open = ko.observable(false);
 
-    self.open.subscribe(function(newValue) {
+    self.open.subscribe(newValue => {
       if (newValue && self.entries().length === 0) {
         self.loadEntries();
       }
     });
 
-    self.hasEntries = ko.computed(function() {
+    self.hasEntries = ko.pureComputed(() =>{
       return self.entries().length > 0;
     });
   }
 
-  AssistGitEntry.prototype.dblClick = function () {
-    var self = this;
+  dblClick() {
+    let self = this;
     if (self.definition.type !== 'file') {
       return;
     }
     self.hasErrors(false);
 
-    var successCallback = function(data) {
-      self.fileContent(data.content);
-      huePubSub.publish('assist.dblClickGitItem', self);
-    };
-
-    var errorCallback = function () {
-      self.hasErrors(true);
-      self.loading(false);
-    };
-
-    self.apiHelper.fetchGitContents({
+    apiHelper.fetchGitContents({
       pathParts: self.getHierarchy(),
       fileType: self.definition.type,
-      successCallback: successCallback,
-      errorCallback: errorCallback
+      successCallback: data => {
+        self.fileContent(data.content);
+        huePubSub.publish('assist.dblClickGitItem', self);
+      },
+      errorCallback: () => {
+        self.hasErrors(true);
+        self.loading(false);
+      }
     })
   };
 
-  AssistGitEntry.prototype.loadEntries = function(callback) {
-    var self = this;
+  loadEntries(callback) {
+    let self = this;
     if (self.loading()) {
       return;
     }
     self.loading(true);
     self.hasErrors(false);
 
-    var successCallback = function(data) {
-      var filteredFiles = $.grep(data.files, function (file) {
-        return file.name !== '.' && file.name !== '..';
-      });
-      self.entries($.map(filteredFiles, function (file) {
-        return new AssistGitEntry({
-          definition: file,
-          parent: self,
-          apiHelper: self.apiHelper
-        })
-      }));
-      self.loaded = true;
-      self.loading(false);
-      if (callback) {
-        callback();
-      }
-    };
-
-    var errorCallback = function () {
-      self.hasErrors(true);
-      self.loading(false);
-      if (callback) {
-        callback();
-      }
-    };
-
-    self.apiHelper.fetchGitContents({
+    apiHelper.fetchGitContents({
       pathParts: self.getHierarchy(),
       fileType: self.definition.type,
-      successCallback: successCallback,
-      errorCallback: errorCallback
+      successCallback: data => {
+        let filteredFiles = data.files.filter(file => file.name !== '.' && file.name !== '..');
+        self.entries(filteredFiles.map(file => new AssistGitEntry({
+          definition: file,
+          parent: self
+        })));
+        self.loaded = true;
+        self.loading(false);
+        if (callback) {
+          callback();
+        }
+      },
+      errorCallback: () => {
+        self.hasErrors(true);
+        self.loading(false);
+        if (callback) {
+          callback();
+        }
+      }
     })
   };
 
-  AssistGitEntry.prototype.loadDeep = function(folders, callback) {
-    var self = this;
+  loadDeep(folders, callback) {
+    let self = this;
 
     if (folders.length === 0) {
       callback(self);
       return;
     }
 
-    var findNextAndLoadDeep = function () {
-      var nextName = folders.shift();
-      var foundEntry = $.grep(self.entries(), function (entry) {
-        return entry.definition.name === nextName && entry.definition.type === 'dir';
-      });
+    let findNextAndLoadDeep = () => {
+      let nextName = folders.shift();
+      let foundEntry = self.entries().filter(entry => entry.definition.name === nextName && entry.definition.type === 'dir');
       if (foundEntry.length === 1) {
         foundEntry[0].loadDeep(folders, callback);
       } else if (! self.hasErrors()) {
@@ -155,10 +143,10 @@ var AssistGitEntry = (function () {
     }
   };
 
-  AssistGitEntry.prototype.getHierarchy = function () {
-    var self = this;
-    var parts = [];
-    var entry = self;
+  getHierarchy() {
+    let self = this;
+    let parts = [];
+    let entry = self;
     while (entry != null) {
       parts.push(entry.definition.name);
       entry = entry.parent;
@@ -167,8 +155,8 @@ var AssistGitEntry = (function () {
     return parts;
   };
 
-  AssistGitEntry.prototype.toggleOpen = function () {
-    var self = this;
+  toggleOpen() {
+    let self = this;
     if (self.definition.type !== 'dir') {
       return;
     }
@@ -181,6 +169,6 @@ var AssistGitEntry = (function () {
       huePubSub.publish('assist.selectGitEntry', self);
     }
   };
+}
 
-  return AssistGitEntry;
-})();
+export default AssistGitEntry

+ 34 - 40
desktop/core/src/desktop/static/desktop/js/assist/assistHBaseEntry.js → desktop/core/src/desktop/js/assist/assistHBaseEntry.js

@@ -14,20 +14,23 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-var AssistHBaseEntry = (function () {
+import ko from 'knockout'
+
+import apiHelper from '../api/apiHelper'
+import huePubSub from '../utils/huePubSub'
+
+class AssistHBaseEntry {
 
   /**
    * @param {object} options
    * @param {object} options.definition
    * @param {string} options.definition.name
-   * @param {ApiHelper} options.apiHelper
    * @constructor
    */
-  function AssistHBaseEntry (options) {
-    var self = this;
+  constructor(options) {
+    let self = this;
 
     self.definition = options.definition;
-    self.apiHelper = options.apiHelper;
     self.path = self.definition.name;
 
     self.entries = ko.observableArray([]);
@@ -36,59 +39,50 @@ var AssistHBaseEntry = (function () {
     self.loading = ko.observable(false);
     self.hasErrors = ko.observable(false);
 
-    self.hasEntries = ko.computed(function() {
-      return self.entries().length > 0;
-    });
+    self.hasEntries = ko.pureComputed(() => self.entries().length > 0);
   }
 
-  AssistHBaseEntry.prototype.loadEntries = function(callback) {
-    var self = this;
+  loadEntries(callback) {
+    let self = this;
     if (self.loading()) {
       return;
     }
     self.loading(true);
     self.hasErrors(false);
 
-    var successCallback = function(data) {
-      self.entries($.map(data.data, function (obj) {
-        return new AssistHBaseEntry({
-          definition: obj,
-          apiHelper: self.apiHelper
-        })
-      }));
-      self.loaded = true;
-      self.loading(false);
-      if (callback) {
-        callback();
-      }
-    };
-
-    var errorCallback = function () {
-      self.hasErrors(true);
-      self.loading(false);
-      if (callback) {
-        callback();
-      }
-    };
-
-    self.apiHelper.fetchHBase({
+    apiHelper.fetchHBase({
       parent: self.definition,
-      successCallback: successCallback,
-      errorCallback: errorCallback
+      successCallback: data => {
+        self.entries(data.data.map(obj => new AssistHBaseEntry({
+          definition: obj
+        })));
+        self.loaded = true;
+        self.loading(false);
+        if (callback) {
+          callback();
+        }
+      },
+      errorCallback: () => {
+        self.hasErrors(true);
+        self.loading(false);
+        if (callback) {
+          callback();
+        }
+      }
     })
   };
 
-  AssistHBaseEntry.prototype.open = function () {
+  open() {
     huePubSub.publish('assist.clickHBaseItem', this);
   };
 
-  AssistHBaseEntry.prototype.click = function () {
+  click() {
     huePubSub.publish('assist.clickHBaseItem', this);
   };
 
-  AssistHBaseEntry.prototype.dblClick = function () {
+  dblClick() {
     huePubSub.publish('assist.dblClickHBaseItem', this);
   };
+}
 
-  return AssistHBaseEntry;
-})();
+export default AssistHBaseEntry

+ 111 - 128
desktop/core/src/desktop/static/desktop/js/assist/assistStorageEntry.js → desktop/core/src/desktop/js/assist/assistStorageEntry.js

@@ -14,30 +14,36 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-var AssistStorageEntry = (function () {
-
-  var PAGE_SIZE = 100;
-
-  var TYPE_SPECIFICS = {
-    'adls': {
-      apiHelperFetchFunction: 'fetchAdlsPath',
-      dblClickPubSubId: 'assist.dblClickAdlsItem',
-      goHomePubSubId: 'assist.adls.go.home',
-      selectEntryPubSubId: 'assist.selectAdlsEntry'
-    },
-    'hdfs': {
-      apiHelperFetchFunction: 'fetchHdfsPath',
-      dblClickPubSubId: 'assist.dblClickHdfsItem',
-      goHomePubSubId: 'assist.hdfs.go.home',
-      selectEntryPubSubId: 'assist.selectHdfsEntry'
-    },
-    's3': {
-      apiHelperFetchFunction: 'fetchS3Path',
-      dblClickPubSubId: 'assist.dblClickS3Item',
-      goHomePubSubId: 'assist.s3.go.home',
-      selectEntryPubSubId: 'assist.selectS3Entry'
-    }
-  };
+import $ from 'jquery'
+import ko from 'knockout'
+
+import apiHelper from '../api/apiHelper'
+import huePubSub from '../utils/huePubSub'
+
+const PAGE_SIZE = 100;
+
+const TYPE_SPECIFICS = {
+  'adls': {
+    apiHelperFetchFunction: 'fetchAdlsPath',
+    dblClickPubSubId: 'assist.dblClickAdlsItem',
+    goHomePubSubId: 'assist.adls.go.home',
+    selectEntryPubSubId: 'assist.selectAdlsEntry'
+  },
+  'hdfs': {
+    apiHelperFetchFunction: 'fetchHdfsPath',
+    dblClickPubSubId: 'assist.dblClickHdfsItem',
+    goHomePubSubId: 'assist.hdfs.go.home',
+    selectEntryPubSubId: 'assist.selectHdfsEntry'
+  },
+  's3': {
+    apiHelperFetchFunction: 'fetchS3Path',
+    dblClickPubSubId: 'assist.dblClickS3Item',
+    goHomePubSubId: 'assist.s3.go.home',
+    selectEntryPubSubId: 'assist.selectS3Entry'
+  }
+};
+
+class AssistStorageEntry {
 
   /**
    * @param {object} options
@@ -47,15 +53,13 @@ var AssistStorageEntry = (function () {
    * @param {string} options.type - The storage type ('adls', 'hdfs', 's3')
    * @param {string} [options.originalType] - The original storage type ('adl', 's3a')
    * @param {AssistStorageEntry} options.parent
-   * @param {ApiHelper} options.apiHelper
    * @constructor
    */
-  function AssistStorageEntry (options) {
-    var self = this;
+  constructor(options) {
+    let self = this;
     self.type = options.type;
     self.originalType = options.originalType;
     self.definition = options.definition;
-    self.apiHelper = options.apiHelper;
     self.parent = options.parent;
     self.path = '';
     if (self.parent !== null) {
@@ -72,7 +76,7 @@ var AssistStorageEntry = (function () {
 
     self.filter = ko.observable('').extend({ rateLimit: 400 });
 
-    self.filter.subscribe(function () {
+    self.filter.subscribe(() => {
       self.currentPage = 1;
       self.hasMorePages = true;
       self.loadEntries();
@@ -87,7 +91,7 @@ var AssistStorageEntry = (function () {
     self.hasErrors = ko.observable(false);
     self.open = ko.observable(false);
 
-    self.open.subscribe(function(newValue) {
+    self.open.subscribe(newValue => {
       if (newValue && self.entries().length === 0) {
         if (self.definition.type === 'dir') {
           self.loadEntries();
@@ -97,110 +101,95 @@ var AssistStorageEntry = (function () {
       }
     });
 
-    self.hasEntries = ko.computed(function() {
-      return self.entries().length > 0;
-    });
+    self.hasEntries = ko.pureComputed(() => self.entries().length > 0);
   }
 
-  AssistStorageEntry.prototype.dblClick = function () {
-    var self = this;
-    huePubSub.publish(TYPE_SPECIFICS[self.type].dblClickPubSubId, self);
+  dblClick() {
+    huePubSub.publish(TYPE_SPECIFICS[self.type].dblClickPubSubId, this);
   };
 
-  AssistStorageEntry.prototype.loadPreview = function () {
-    var self = this;
+  loadPreview() {
+    let self = this;
     self.loading(true);
     window.apiHelper.fetchStoragePreview({
       path: self.getHierarchy(),
       type: self.type,
       silenceErrors: true
-    }).done(function (data) {
+    }).done(data => {
       self.preview(data);
-    }).fail(function (errorText) {
+    }).fail(errorText => {
       self.hasErrors(true);
       self.errorText(errorText);
-    }).always(function () {
+    }).always(() => {
       self.loading(false);
     })
   };
 
-  AssistStorageEntry.prototype.loadEntries = function(callback) {
-    var self = this;
+  loadEntries(callback) {
+    let self = this;
     if (self.loading()) {
       return;
     }
     self.loading(true);
     self.hasErrors(false);
 
-    var successCallback = function(data) {
-      self.hasMorePages = data.page.next_page_number > self.currentPage;
-      var filteredFiles = $.grep(data.files, function (file) {
-        return file.name !== '.' && file.name !== '..';
-      });
-      self.entries($.map(filteredFiles, function (file) {
-        file.url = encodeURI(file.url);
-        return new AssistStorageEntry({
-          type: self.type,
-          definition: file,
-          parent: self,
-          apiHelper: self.apiHelper
-        })
-      }));
-      self.loaded = true;
-      self.loading(false);
-      if (callback) {
-        callback();
-      }
-    };
-
-    var errorCallback = function (errorText) {
-      self.hasErrors(true);
-      self.errorText(errorText);
-      self.loading(false);
-      if (callback) {
-        callback();
-      }
-    };
-
-    self.apiHelper[TYPE_SPECIFICS[self.type].apiHelperFetchFunction]({
+    apiHelper[TYPE_SPECIFICS[self.type].apiHelperFetchFunction]({
       pageSize: PAGE_SIZE,
       page: self.currentPage,
       filter: self.filter().trim() ? self.filter() : undefined,
       pathParts: self.getHierarchy(),
-      successCallback: successCallback,
-      errorCallback: errorCallback
+      successCallback: data => {
+        self.hasMorePages = data.page.next_page_number > self.currentPage;
+        let filteredFiles = data.files.filter(file => file.name !== '.' && file.name !== '..');
+        self.entries(filteredFiles.map(file => {
+          file.url = encodeURI(file.url);
+          return new AssistStorageEntry({
+            type: self.type,
+            definition: file,
+            parent: self
+          })
+        }));
+        self.loaded = true;
+        self.loading(false);
+        if (callback) {
+          callback();
+        }
+      },
+      errorCallback: errorText => {
+        self.hasErrors(true);
+        self.errorText(errorText);
+        self.loading(false);
+        if (callback) {
+          callback();
+        }
+      }
     })
   };
 
-  AssistStorageEntry.prototype.goHome = function () {
-    var self = this;
-    huePubSub.publish(TYPE_SPECIFICS[self.type].goHomePubSubId);
+  goHome() {
+    huePubSub.publish(TYPE_SPECIFICS[this.type].goHomePubSubId);
   };
 
-  AssistStorageEntry.prototype.loadDeep = function(folders, callback) {
-    var self = this;
+  loadDeep(folders, callback) {
+    let self = this;
 
     if (folders.length === 0) {
       callback(self);
       return;
     }
 
-    var nextName = folders.shift();
-    var loadedPages = 0;
-    var findNextAndLoadDeep = function () {
+    let nextName = folders.shift();
+    let loadedPages = 0;
 
-      var foundEntry = $.grep(self.entries(), function (entry) {
-        return entry.definition.name === nextName;
-      });
-      var passedAlphabetically = self.entries().length > 0 && self.entries()[self.entries().length - 1].definition.name.localeCompare(nextName) > 0;
+    let findNextAndLoadDeep = () =>{
+      let foundEntry = self.entries().filter(entry => entry.definition.name === nextName);
+      let passedAlphabetically = self.entries().length > 0 && self.entries()[self.entries().length - 1].definition.name.localeCompare(nextName) > 0;
 
       if (foundEntry.length === 1) {
         foundEntry[0].loadDeep(folders, callback);
       } else if (!passedAlphabetically && self.hasMorePages && loadedPages < 50) {
         loadedPages++;
-        self.fetchMore(function () {
-          findNextAndLoadDeep();
-        }, function () {
+        self.fetchMore(findNextAndLoadDeep, () => {
           callback(self);
         });
       } else {
@@ -215,10 +204,10 @@ var AssistStorageEntry = (function () {
     }
   };
 
-  AssistStorageEntry.prototype.getHierarchy = function () {
-    var self = this;
-    var parts = [];
-    var entry = self;
+  getHierarchy() {
+    let self = this;
+    let parts = [];
+    let entry = self;
     while (entry != null) {
       parts.push(entry.definition.name);
       entry = entry.parent;
@@ -227,8 +216,8 @@ var AssistStorageEntry = (function () {
     return parts;
   };
 
-  AssistStorageEntry.prototype.toggleOpen = function (data, event) {
-    var self = this;
+  toggleOpen(data, event) {
+    let self = this;
     if (self.definition.type === 'file') {
       if (IS_HUE_4) {
         if (event.ctrlKey || event.metaKey || event.which === 2) {
@@ -251,8 +240,8 @@ var AssistStorageEntry = (function () {
     }
   };
 
-  AssistStorageEntry.prototype.fetchMore = function (successCallback, errorCallback) {
-    var self = this;
+  fetchMore(successCallback, errorCallback) {
+    let self = this;
     if (!self.hasMorePages || self.loadingMore()) {
       return;
     }
@@ -260,30 +249,25 @@ var AssistStorageEntry = (function () {
     self.loadingMore(true);
     self.hasErrors(false);
 
-    self.apiHelper[TYPE_SPECIFICS[self.type].apiHelperFetchFunction]({
+    apiHelper[TYPE_SPECIFICS[self.type].apiHelperFetchFunction]({
       pageSize: PAGE_SIZE,
       page: self.currentPage,
       filter: self.filter().trim() ? self.filter() : undefined,
       pathParts: self.getHierarchy(),
-      successCallback: function (data) {
+      successCallback: data => {
         self.hasMorePages = data.page.next_page_number > self.currentPage;
-        var filteredFiles = $.grep(data.files, function (file) {
-          return file.name !== '.' && file.name !== '..';
-        });
-        self.entries(self.entries().concat($.map(filteredFiles, function (file) {
-          return new AssistStorageEntry({
-            type: self.type,
-            definition: file,
-            parent: self,
-            apiHelper: self.apiHelper
-          })
-        })));
+        let filteredFiles = data.files.filter(file => file.name !== '.' && file.name !== '..');
+        self.entries(self.entries().concat(filteredFiles.map(file => new AssistStorageEntry({
+          type: self.type,
+          definition: file,
+          parent: self
+        }))));
         self.loadingMore(false);
         if (successCallback) {
           successCallback();
         }
       },
-      errorCallback: function () {
+      errorCallback: () => {
         self.hasErrors(true);
         if (errorCallback) {
           errorCallback();
@@ -292,9 +276,9 @@ var AssistStorageEntry = (function () {
     });
   };
 
-  AssistStorageEntry.prototype.showContextPopover = function (entry, event, positionAdjustment) {
-    var $source = $(event.target);
-    var offset = $source.offset();
+  showContextPopover(entry, event, positionAdjustment) {
+    let $source = $(event.target);
+    let offset = $source.offset();
     entry.contextPopoverVisible(true);
 
     if (positionAdjustment) {
@@ -302,7 +286,6 @@ var AssistStorageEntry = (function () {
       offset.top += positionAdjustment.top;
     }
 
-
     huePubSub.publish('context.popover.show', {
       data: {
         type: 'storageEntry',
@@ -319,12 +302,12 @@ var AssistStorageEntry = (function () {
       }
     });
 
-    huePubSub.subscribeOnce('context.popover.hidden', function () {
+    huePubSub.subscribeOnce('context.popover.hidden', () => {
       entry.contextPopoverVisible(false);
     });
   };
 
-  AssistStorageEntry.prototype.openInImporter = function () {
+  openInImporter() {
     huePubSub.publish('open.in.importer', this.definition.path);
   };
 
@@ -336,14 +319,14 @@ var AssistStorageEntry = (function () {
    * @param {string} [type] - Optional type, if not specified here or in the path 'hdfs' will be used.
    * @return {Promise}
    */
-  AssistStorageEntry.getEntry = function (path, type) {
-    var deferred = $.Deferred();
-    var typeMatch = path.match(/^([^:]+):\/(\/.*)\/?/i);
-    var type = typeMatch ? typeMatch[1] : (type || 'hdfs');
+  static getEntry(path, type) {
+    let deferred = $.Deferred();
+    let typeMatch = path.match(/^([^:]+):\/(\/.*)\/?/i);
+    type = typeMatch ? typeMatch[1] : (type || 'hdfs');
     type = type.replace(/s3.*/i, 's3');
-    type = type.replace(/adl.*/i, 'adls')
+    type = type.replace(/adl.*/i, 'adls');
 
-    var rootEntry = new AssistStorageEntry({
+    let rootEntry = new AssistStorageEntry({
       type: type.toLowerCase(),
       originalType: typeMatch && typeMatch[1],
       definition: {
@@ -354,12 +337,12 @@ var AssistStorageEntry = (function () {
       apiHelper: window.apiHelper
     });
 
-    var path = (typeMatch ? typeMatch[2] : path).replace(/(?:^\/)|(?:\/$)/g, '').split('/');
+    path = (typeMatch ? typeMatch[2] : path).replace(/(?:^\/)|(?:\/$)/g, '').split('/');
 
     rootEntry.loadDeep(path, deferred.resolve);
 
     return deferred.promise();
   };
+}
 
-  return AssistStorageEntry;
-})();
+export default AssistStorageEntry

+ 28 - 0
desktop/core/src/desktop/js/assist/assistViewModel.js

@@ -0,0 +1,28 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import AssistDbEntry from 'assist/assistDbEntry'
+import AssistDbSource from 'assist/assistDbSource'
+import AssistGitEntry from 'assist/assistGitEntry'
+import AssistHBaseEntry from 'assist/assistHBaseEntry'
+import AssistStorageEntry from 'assist/assistStorageEntry'
+
+// TODO: Get rid of
+window.AssistDbEntry = AssistDbEntry;
+window.AssistDbSource = AssistDbSource;
+window.AssistGitEntry = AssistGitEntry;
+window.AssistHBaseEntry = AssistHBaseEntry;
+window.AssistStorageEntry = AssistStorageEntry;

+ 1 - 0
desktop/core/src/desktop/js/ext/ko.selectize.custom.js

@@ -16,6 +16,7 @@
 
 // Based on https://gist.githubusercontent.com/xtranophilist/8001624/raw/ko_selectize.js
 
+import $ from 'jquery';
 import ko from 'knockout';
 
 var inject_binding = function (allBindings, key, value) {

+ 6 - 1
desktop/core/src/desktop/js/hue.js

@@ -30,7 +30,7 @@ import 'knockout-switch-case'
 import 'knockout-sortable'
 import 'knockout.validation'
 
-import apiHelper from 'api/apiHelper';
+import apiHelper from 'api/apiHelper'
 import CancellablePromise from 'api/cancellablePromise'
 import contextCatalog from 'catalog/contextCatalog'
 import dataCatalog from 'catalog/dataCatalog'
@@ -40,6 +40,10 @@ import hueDrop from 'utils/hueDrop'
 import huePubSub from 'utils/huePubSub'
 import hueUtils from 'utils/hueUtils'
 
+import sqlUtils from 'sql/sqlUtils'
+
+import 'assist/assistViewModel'
+
 // TODO: Migrate away
 window._ = _;
 window.apiHelper = apiHelper;
@@ -56,4 +60,5 @@ window.ko = ko;
 window.ko.mapping = komapping;
 window.localforage = localforage;
 window.page = page;
+window.sqlUtils = sqlUtils;
 window.qq = qq;

+ 293 - 0
desktop/core/src/desktop/js/sql/sqlUtils.js

@@ -0,0 +1,293 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import $ from 'jquery'
+import CancellablePromise from '../api/cancellablePromise'
+import dataCatalog from '../catalog/dataCatalog'
+
+const hiveReservedKeywords = {
+  ALL: true, ALTER: true, AND: true, ARRAY: true, AS: true, AUTHORIZATION: true, BETWEEN: true, BIGINT: true, BINARY: true, BOOLEAN: true, BOTH: true, BY: true, CACHE: true, CASE: true,
+  CAST: true, CHAR: true, COLUMN: true, COMMIT: true, CONF: true, CONSTRAINT: true, CREATE: true, CROSS: true, CUBE: true, CURRENT: true, CURRENT_DATE: true, CURRENT_TIMESTAMP: true,
+  CURSOR: true, DATABASE: true, DATE: true, DAYOFWEEK: true, DECIMAL: true, DELETE: true, DESCRIBE: true, DISTINCT: true, DIV: true, DOUBLE: true, DROP: true, ELSE: true, END: true,
+  EXCHANGE: true, EXTRACT: true, EXISTS: true, EXTENDED: true, EXTERNAL: true, FALSE: true, FETCH: true, FLOAT: true, FLOOR: true, FOLLOWING: true, FOR: true, FOREIGN: true,FROM: true,
+  FULL: true, FUNCTION: true, GRANT: true, GROUP: true, GROUPING: true, HAVING: true, IF: true, IMPORT: true, IN: true, INNER: true, INSERT: true, INT: true, INTEGER: true,
+  INTERSECT: true, INTERVAL: true, INTO: true, IS: true, JOIN: true, LATERAL: true, LEFT: true, LESS: true, LIKE: true, LOCAL: true, MACRO: true, MAP: true, MORE: true, NONE: true,
+  NOT: true, NULL: true, NUMERIC: true, OF: true, ON: true, ONLY: true, OR: true, ORDER: true, OUT: true, OUTER: true, OVER: true, PARTIALSCAN: true, PARTITION: true, PERCENT: true,
+  PRECEDING: true, PRECISION: true, PRESERVE: true, PRIMARY: true, PROCEDURE: true, RANGE: true, READS: true, REDUCE: true, REFERENCES: true, REGEXP: true, REVOKE: true, RIGHT: true,
+  RLIKE: true, ROLLBACK: true, ROLLUP: true, ROW: true, ROWS: true, SELECT: true, SET: true, SMALLINT: true, START: true, TABLE: true, TABLESAMPLE: true, THEN: true, TIME: true,
+  TIMESTAMP: true, TO: true, TRANSFORM: true, TRIGGER: true, TRUE: true, TRUNCATE: true, UNBOUNDED: true, UNION: true, UNIQUEJOIN: true, UPDATE: true, USER: true, USING: true,
+  VALUES: true, VARCHAR: true, VIEWS: true, WHEN: true, WHERE: true, WINDOW: true, WITH: true
+};
+
+const extraHiveReservedKeywords = {
+  ASC: true, CLUSTER: true, DESC: true, DISTRIBUTE: true, FORMATTED: true, FUNCTION: true, INDEX: true, INDEXES: true, LIMIT: true, LOCK: true, SCHEMA: true, SORT: true
+};
+
+const impalaReservedKeywords = {
+  ADD: true, AGGREGATE: true, ALL: true, ALLOCATE: true, ALTER: true, ANALYTIC: true, AND: true, ANTI: true, ANY: true, API_VERSION: true, ARE: true, ARRAY: true, ARRAY_AGG: true,
+  ARRAY_MAX_CARDINALITY: true, AS: true, ASC: true, ASENSITIVE: true, ASYMMETRIC: true, AT: true, ATOMIC: true, AUTHORIZATION: true, AVRO: true, BEGIN_FRAME: true, BEGIN_PARTITION: true,
+  BETWEEN: true, BIGINT: true, BINARY: true, BLOB: true, BLOCK_SIZE: true, BOOLEAN: true, BOTH: true, BY: true, CACHED: true, CALLED: true, CARDINALITY: true, CASCADE: true,
+  CASCADED: true, CASE: true, CAST: true, CHANGE: true, CHAR: true, CHARACTER: true, CLASS: true, CLOB: true, CLOSE_FN: true, COLLATE: true, COLLECT: true, COLUMN: true, COLUMNS: true,
+  COMMENT: true, COMMIT: true, COMPRESSION: true, COMPUTE: true, CONDITION: true, CONNECT: true, CONSTRAINT: true, CONTAINS: true, CONVERT: true, COPY: true, CORR: true,
+  CORRESPONDING: true, COVAR_POP: true, COVAR_SAMP: true, CREATE: true, CROSS: true, CUBE: true, CURRENT: true, CURRENT_DATE: true, CURRENT_DEFAULT_TRANSFORM_GROUP: true,
+  CURRENT_PATH: true, CURRENT_ROLE: true, CURRENT_ROW: true, CURRENT_SCHEMA: true, CURRENT_TIME: true, CURRENT_TRANSFORM_GROUP_FOR_TYPE: true, CURSOR: true, CYCLE: true, DATA: true,
+  DATABASE: true, DATABASES: true, DATE: true, DATETIME: true, DEALLOCATE: true, DEC: true, DECFLOAT: true, DECIMAL: true, DECLARE: true, DEFINE: true, DELETE: true, DELIMITED: true,
+  DEREF: true, DESC: true, DESCRIBE: true, DETERMINISTIC: true, DISCONNECT: true, DISTINCT: true, DIV: true, DOUBLE: true, DROP: true, DYNAMIC: true, EACH: true, ELEMENT: true,
+  ELSE: true, EMPTY: true, ENCODING: true, END: true, END_FRAME: true, END_PARTITION: true, EQUALS: true, ESCAPE: true, ESCAPED: true, EVERY: true, EXCEPT: true, EXEC: true,
+  EXECUTE: true, EXISTS: true, EXPLAIN: true, EXTENDED: true, EXTERNAL: true, FALSE: true, FETCH: true, FIELDS: true, FILEFORMAT: true, FILES: true, FILTER: true, FINALIZE_FN: true,
+  FIRST: true, FLOAT: true, FOLLOWING: true, FOR: true, FOREIGN: true, FORMAT: true, FORMATTED: true, FRAME_ROW: true, FREE: true, FROM: true, FULL: true, FUNCTION: true,
+  FUNCTIONS: true, FUSION: true, GET: true, GLOBAL: true, GRANT: true, GROUP: true, GROUPING: true, GROUPS: true, HASH: true, HAVING: true, HOLD: true, IF: true, IGNORE: true,
+  ILIKE: true, IN: true, INCREMENTAL: true, INDICATOR: true, INIT_FN: true, INITIAL: true, INNER: true, INOUT: true, INPATH: true, INSENSITIVE: true, INSERT: true, INT: true,
+  INTEGER: true, INTERMEDIATE: true, INTERSECT: true, INTERSECTION: true, INTERVAL: true, INTO: true, INVALIDATE: true, IREGEXP: true, IS: true, JOIN: true, JSON_ARRAY: true,
+  JSON_ARRAYAGG: true, JSON_EXISTS: true, JSON_OBJECT: true, JSON_OBJECTAGG: true, JSON_QUERY: true, JSON_TABLE: true, JSON_TABLE_PRIMITIVE: true, JSON_VALUE: true, KEY: true,
+  KUDU: true, LARGE: true, LAST: true, LATERAL: true, LEADING: true, LEFT: true, LIKE: true, LIKE_REGEX: true, LIMIT: true, LINES: true, LISTAGG: true, LOAD: true, LOCAL: true,
+  LOCALTIMESTAMP: true, LOCATION: true, MAP: true, MATCH: true, MATCH_NUMBER: true, MATCH_RECOGNIZE: true, MATCHES: true, MERGE: true, MERGE_FN: true, METADATA: true, METHOD: true,
+  MODIFIES: true, MULTISET: true, NATIONAL: true, NATURAL: true, NCHAR: true, NCLOB: true, NO: true, NONE: true, NORMALIZE: true, NOT: true, NTH_VALUE: true, NULL: true, NULLS: true,
+  NUMERIC: true, OCCURRENCES_REGEX: true, OCTET_LENGTH: true, OF: true, OFFSET: true, OMIT: true, ON: true, ONE: true, ONLY: true, OR: true, ORDER: true, OUT: true, OUTER: true,
+  OVER: true, OVERLAPS: true, OVERLAY: true, OVERWRITE: true, PARQUET: true, PARQUETFILE: true, PARTITION: true, PARTITIONED: true, PARTITIONS: true, PATTERN: true, PER: true,
+  PERCENT: true, PERCENTILE_CONT: true, PERCENTILE_DISC: true, PORTION: true, POSITION: true, POSITION_REGEX: true, PRECEDES: true, PRECEDING: true, PREPARE: true, PREPARE_FN: true,
+  PRIMARY: true, PROCEDURE: true, PRODUCED: true, PTF: true, PURGE: true, RANGE: true, RCFILE: true, READS: true, REAL: true, RECOVER: true, RECURSIVE: true, REF: true,
+  REFERENCES: true, REFERENCING: true, REFRESH: true, REGEXP: true, REGR_AVGX: true, REGR_AVGY: true, REGR_COUNT: true, REGR_INTERCEPT: true, REGR_R2: true,REGR_SLOPE: true,
+  REGR_SXX: true, REGR_SXY: true, REGR_SYY: true, RELEASE: true, RENAME: true, REPEATABLE: true, REPLACE: true, REPLICATION: true, RESTRICT: true, RETURNS: true, REVOKE: true,
+  RIGHT: true, RLIKE: true, ROLE: true, ROLES: true, ROLLBACK: true, ROLLUP: true, ROW: true, ROWS: true, RUNNING: true, SAVEPOINT: true, SCHEMA: true, SCHEMAS: true, SCOPE: true,
+  SCROLL: true, SEARCH: true, SEEK: true, SELECT: true, SEMI: true, SENSITIVE: true, SEQUENCEFILE: true, SERDEPROPERTIES: true, SERIALIZE_FN: true, SET: true, SHOW: true,
+  SIMILAR: true, SKIP: true, SMALLINT: true, SOME: true, SORT: true, SPECIFIC: true, SPECIFICTYPE: true, SQLEXCEPTION: true, SQLSTATE: true, SQLWARNING: true, STATIC: true,
+  STATS: true, STORED: true, STRAIGHT_JOIN: true, STRING: true, STRUCT: true, SUBMULTISET: true, SUBSET: true, SUBSTRING_REGEX: true, SUCCEEDS: true, SYMBOL: true, SYMMETRIC: true,
+  SYSTEM_TIME: true, SYSTEM_USER: true, TABLE: true, TABLES: true, TABLESAMPLE: true, TBLPROPERTIES: true, TERMINATED: true, TEXTFILE: true, THEN: true, TIMESTAMP: true,
+  TIMEZONE_HOUR: true, TIMEZONE_MINUTE: true, TINYINT: true, TO: true, TRAILING: true, TRANSLATE_REGEX: true, TRANSLATION: true, TREAT: true, TRIGGER: true, TRIM_ARRAY: true,
+  TRUE: true, TRUNCATE: true, UESCAPE: true, UNBOUNDED: true, UNCACHED: true, UNION: true, UNIQUE: true, UNKNOWN: true, UNNEST: true, UPDATE: true, UPDATE_FN: true, UPSERT: true,
+  USE: true, USER: true, USING: true, VALUE_OF: true, VALUES: true, VARBINARY: true, VARCHAR: true, VARYING: true, VERSIONING: true, VIEW: true, WHEN: true, WHENEVER: true,
+  WHERE: true, WIDTH_BUCKET: true, WINDOW: true, WITH: true, WITHIN: true, WITHOUT: true
+};
+
+let identifierEquals = (a, b) => a && b && a.replace(/^\s*`/, '').replace(/`\s*$/, '').toLowerCase() === b.replace(/^\s*`/, '').replace(/`\s*$/, '').toLowerCase();
+
+let autocompleteFilter = (filter, entries) => {
+  let lowerCaseFilter = filter.toLowerCase();
+  return entries.filter(function (suggestion) {
+    // TODO: Extend with fuzzy matches
+    var foundIndex = suggestion.value.toLowerCase().indexOf(lowerCaseFilter);
+    if (foundIndex !== -1) {
+      if (foundIndex === 0 || (suggestion.filterValue && suggestion.filterValue.toLowerCase().indexOf(lowerCaseFilter) === 0)) {
+        suggestion.filterWeight = 3;
+      } else  {
+        suggestion.filterWeight = 2;
+      }
+    } else {
+      if (suggestion.details && suggestion.details.comment && lowerCaseFilter.indexOf(' ') === -1) {
+        foundIndex = suggestion.details.comment.toLowerCase().indexOf(lowerCaseFilter);
+        if (foundIndex !== -1) {
+          suggestion.filterWeight = 1;
+          suggestion.matchComment = true;
+        }
+      }
+    }
+    if (foundIndex !== -1) {
+      suggestion.matchIndex = foundIndex;
+      suggestion.matchLength = filter.length;
+      return true;
+    }
+    return false;
+  });
+};
+
+let sortSuggestions = (suggestions, filter, sortOverride) => {
+  suggestions.sort((a, b) => {
+    if (filter) {
+      if (typeof a.filterWeight !== 'undefined' && typeof b.filterWeight !== 'undefined' && b.filterWeight !== a.filterWeight) {
+        return b.filterWeight - a.filterWeight;
+      }
+      if (typeof a.filterWeight !== 'undefined' && typeof b.filterWeight === 'undefined') {
+        return -1;
+      }
+      if (typeof a.filterWeight === 'undefined' && typeof b.filterWeight !== 'undefined') {
+        return 1;
+      }
+    }
+    if (sortOverride && sortOverride.partitionColumnsFirst) {
+      if (a.partitionKey && !b.partitionKey) {
+        return -1;
+      }
+      if (b.partitionKey && !a.partitionKey) {
+        return 1;
+      }
+    }
+    let aWeight = a.category.weight + (a.weightAdjust || 0);
+    let bWeight = b.category.weight + (b.weightAdjust || 0);
+    if (typeof aWeight !== 'undefined' && typeof bWeight !== 'undefined' && bWeight !== aWeight) {
+      return bWeight - aWeight;
+    }
+    if (typeof aWeight !== 'undefined' && typeof bWeight === 'undefined') {
+      return -1;
+    }
+    if (typeof aWeight === 'undefined' && typeof bWeight !== 'undefined') {
+      return 1;
+    }
+    return a.value.localeCompare(b.value);
+  });
+};
+
+let identifierChainToPath = (identifierChain) => identifierChain.map(identifier => identifier.name);
+
+/**
+ *
+ * @param {Object} options
+ * @param {String} options.sourceType
+ * @param {ContextNamespace} options.namespace
+ * @param {ContextCompute} options.compute
+ * @param {boolean} [options.temporaryOnly] - Default: false
+ * @param {Object[]} [options.identifierChain]
+ * @param {Object[]} [options.tables]
+ * @param {Object} [options.cancellable]
+ * @param {Object} [options.cachedOnly]
+ *
+ * @return {CancellablePromise}
+ */
+let resolveCatalogEntry = (options) => {
+  let cancellablePromises = [];
+  let deferred = $.Deferred();
+  let promise = new CancellablePromise(deferred, undefined, cancellablePromises);
+  dataCatalog.applyCancellable(promise, options);
+
+  if (!options.identifierChain) {
+    deferred.reject();
+    return promise;
+  }
+
+  let findInTree = (currentEntry, fieldsToGo) => {
+    if (fieldsToGo.length === 0) {
+      deferred.reject();
+      return;
+    }
+
+    let nextField;
+    if (currentEntry.getType() === 'map') {
+      nextField = 'value';
+    } else if (currentEntry.getType() === 'array') {
+      nextField = 'item';
+    } else {
+      nextField = fieldsToGo.shift();
+    }
+
+    cancellablePromises.push(currentEntry.getChildren({
+      cancellable: options.cancellable,
+      cachedOnly: options.cachedOnly,
+      silenceErrors: true
+    }).done(childEntries => {
+      var foundEntry = undefined;
+      childEntries.some(childEntry => {
+        if (identifierEquals(childEntry.name, nextField)) {
+          foundEntry = childEntry;
+          return true;
+        }
+      });
+      if (foundEntry && fieldsToGo.length) {
+        findInTree(foundEntry, fieldsToGo);
+      } else if (foundEntry) {
+        deferred.resolve(foundEntry);
+      } else {
+        deferred.reject();
+      }
+    }).fail(deferred.reject))
+  };
+
+  let findTable = tablesToGo => {
+    if (tablesToGo.length === 0) {
+      deferred.reject();
+      return;
+    }
+
+    let nextTable = tablesToGo.pop();
+    if (typeof nextTable.subQuery !== 'undefined') {
+      findTable(tablesToGo);
+      return;
+    }
+
+    cancellablePromises.push(dataCatalog.getChildren({
+      sourceType: options.sourceType,
+      namespace: options.namespace,
+      compute: options.compute,
+      path: identifierChainToPath(nextTable.identifierChain),
+      cachedOnly: options && options.cachedOnly,
+      cancellable: options && options.cancellable,
+      temporaryOnly: options && options.temporaryOnly,
+      silenceErrors: true
+    }).done(childEntries => {
+      var foundEntry = undefined;
+      childEntries.some(childEntry => {
+        if (identifierEquals(childEntry.name, options.identifierChain[0].name)) {
+          foundEntry = childEntry;
+          return true;
+        }
+      });
+
+      if (foundEntry && options.identifierChain.length > 1) {
+        findInTree(foundEntry, identifierChainToPath(options.identifierChain.slice(1)));
+      } else if (foundEntry) {
+        deferred.resolve(foundEntry);
+      } else {
+        findTable(tablesToGo)
+      }
+    }).fail(deferred.reject));
+  };
+
+  if (options.tables) {
+    findTable(options.tables.concat())
+  } else {
+    dataCatalog.getEntry({
+      sourceType: options.sourceType,
+      namespace: options.namespace,
+      compute: options.compute,
+      path: [],
+      cachedOnly: options && options.cachedOnly,
+      cancellable: options && options.cancellable,
+      temporaryOnly: options && options.temporaryOnly,
+      silenceErrors: true
+    }).done(function (entry) {
+      findInTree(entry, identifierChainToPath(options.identifierChain))
+    })
+  }
+
+  return promise;
+};
+
+export default {
+  autocompleteFilter : autocompleteFilter,
+  backTickIfNeeded: (sourceType, identifier) => {
+    if (identifier.indexOf('`') === 0) {
+      return identifier;
+    }
+    var upperIdentifier = identifier.toUpperCase();
+    if (sourceType === 'hive' && (hiveReservedKeywords[upperIdentifier] || extraHiveReservedKeywords[upperIdentifier])) {
+      return '`' + identifier + '`';
+    }
+    if (sourceType === 'impala' && impalaReservedKeywords[upperIdentifier]) {
+      return '`' + identifier + '`';
+    }
+    if ((sourceType !== 'impala' && sourceType !== 'hive') && (impalaReservedKeywords[upperIdentifier] || hiveReservedKeywords[upperIdentifier] || extraHiveReservedKeywords[upperIdentifier])) {
+      return '`' + identifier + '`';
+    }
+    if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(identifier)) {
+      return '`' + identifier + '`';
+    }
+    return identifier;
+  },
+  locationEquals: (a, b) => a && b && a.first_line === b.first_line && a.first_column === b.first_column && a.last_line === b.last_line && a.last_column === b.last_column,
+  identifierEquals: identifierEquals,
+  sortSuggestions: sortSuggestions,
+  resolveCatalogEntry: resolveCatalogEntry,
+  identifierChainToPath: identifierChainToPath
+}

+ 0 - 519
desktop/core/src/desktop/static/desktop/js/assist/assistDbSource.js

@@ -1,519 +0,0 @@
-// Licensed to Cloudera, Inc. under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  Cloudera, Inc. licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-var AssistDbSource = (function () {
-  /**
-   * @param {Object} options
-   * @param {Object} options.i18n
-   * @param {string} options.type
-   * @param {ContextNamespace} [options.initialNamespace] - Optional initial namespace to use
-   * @param {ContextCompute} [options.initialCompute] - Optional initial compute to use
-   * @param {string} options.name
-   * @param {boolean} options.nonSqlType - Optional, default false
-   * @param {Object} options.navigationSettings
-   * @constructor
-   */
-  function AssistDbSource (options) {
-    var self = this;
-
-    self.sourceType = options.type;
-    self.name = options.name;
-    self.i18n = options.i18n;
-    self.nonSqlType = options.nonSqlType;
-    self.navigationSettings = options.navigationSettings;
-    var apiHelper = window.apiHelper;
-    self.initialNamespace = options.initialNamespace || apiHelper.getFromTotalStorage('contextSelector', 'lastSelectedNamespace');
-    self.initialCompute = options.initialCompute || apiHelper.getFromTotalStorage('contextSelector', 'lastSelectedCompute');
-
-    self.selectedNamespace = ko.observable();
-    self.namespaces = ko.observableArray();
-
-    self.loadedDeferred = $.Deferred();
-    self.loading = ko.observable(false);
-    self.hasErrors = ko.observable(false);
-
-    self.filter = {
-      querySpec: ko.observable({}).extend({ rateLimit: 300 })
-    };
-
-    self.filteredNamespaces = ko.pureComputed(function () {
-      if (!self.filter.querySpec() || typeof self.filter.querySpec().query === 'undefined' || !self.filter.querySpec().query) {
-        return self.namespaces();
-      }
-      return self.namespaces().filter(function (namespace) {
-        return namespace.name.toLowerCase().indexOf(self.filter.querySpec().query.toLowerCase()) !== -1
-      });
-    });
-
-    self.autocompleteFromNamespaces = function (nonPartial, partial) {
-      var result = [];
-      var partialLower = partial.toLowerCase();
-      self.namespaces().forEach(function (namespace) {
-        if (namespace.name.toLowerCase().indexOf(partialLower) === 0) {
-          result.push(nonPartial + partial + namespace.name.substring(partial.length))
-        }
-      });
-      return result;
-    };
-
-    var ensureDbSet = function () {
-      if (self.nonSqlType) {
-        if (!self.selectedNamespace().selectedDatabase()) {
-          self.selectedNamespace().selectedDatabase(self.selectedNamespace().databases()[0]);
-          self.selectedNamespace().selectedDatabaseChanged();
-        }
-      }
-    };
-
-    self.selectedNamespace.subscribe(function (namespace) {
-      if (namespace && !namespace.loaded() && !namespace.loading()) {
-        namespace.initDatabases(ensureDbSet);
-      } else {
-        ensureDbSet();
-      }
-    });
-
-    self.hasNamespaces = ko.pureComputed(function () {
-      return self.namespaces().length > 0;
-    });
-
-    huePubSub.subscribe('context.catalog.namespaces.refreshed', function (sourceType) {
-      if (self.sourceType !== sourceType) {
-        return;
-      }
-
-      self.loading(true);
-      contextCatalog.getNamespaces({ sourceType: self.sourceType }).done(function (context) {
-        var newNamespaces = [];
-        var existingNamespaceIndex = {};
-        self.namespaces().forEach(function (assistNamespace) {
-          existingNamespaceIndex[assistNamespace.namespace.id] = assistNamespace;
-        });
-        context.namespaces.forEach(function(newNamespace) {
-          if (existingNamespaceIndex[newNamespace.id]) {
-            existingNamespaceIndex[newNamespace.id].namespace = newNamespace;
-            existingNamespaceIndex[newNamespace.id].name = newNamespace.name;
-            existingNamespaceIndex[newNamespace.id].status(newNamespace.status);
-            newNamespaces.push(existingNamespaceIndex[newNamespace.id]);
-          } else {
-            newNamespaces.push(new AssistDbNamespace({
-              sourceType: self.sourceType,
-              namespace: newNamespace,
-              i18n: self.i18n,
-              nonSqlType: self.nonSqlType,
-              navigationSettings: self.navigationSettings
-            }));
-          }
-        });
-        self.namespaces(newNamespaces);
-      }).always(function () {
-        self.loading(false);
-      })
-    });
-  }
-
-  AssistDbSource.prototype.whenLoaded = function (callback) {
-    var self = this;
-    self.loadedDeferred.done(callback);
-  };
-
-  AssistDbSource.prototype.loadNamespaces = function (refresh) {
-    var self = this;
-    self.loading(true);
-
-    if (refresh) {
-      contextCatalog.getComputes({ sourceType: self.sourceType, clearCache: true });
-    }
-
-    return contextCatalog.getNamespaces({ sourceType: self.sourceType, clearCache: refresh }).done(function (context) {
-      var assistNamespaces = [];
-      var activeNamespace;
-      var activeCompute;
-      context.namespaces.forEach(function (namespace) {
-        var assistNamespace = new AssistDbNamespace({
-          sourceType: self.sourceType,
-          namespace: namespace,
-          i18n: self.i18n,
-          nonSqlType: self.nonSqlType,
-          navigationSettings: self.navigationSettings
-        });
-
-        if (self.initialNamespace && namespace.id === self.initialNamespace.id) {
-          activeNamespace = assistNamespace;
-          if (self.initialCompute) {
-            activeNamespace.namespace.computes.some(function (compute) {
-              if (compute.id === self.initialCompute.id) {
-                activeCompute = compute;
-              }
-            })
-          }
-        }
-        assistNamespaces.push(assistNamespace);
-      });
-      self.namespaces(assistNamespaces);
-      if (!refresh) {
-        if (activeNamespace) {
-          self.selectedNamespace(activeNamespace);
-        } else if (assistNamespaces.length) {
-          self.selectedNamespace(assistNamespaces[0]);
-        }
-        if (activeCompute) {
-          self.selectedNamespace().compute(activeCompute);
-        } else if (self.selectedNamespace() && self.selectedNamespace().namespace && self.selectedNamespace().namespace.computes && self.selectedNamespace().namespace.computes.length) {
-          self.selectedNamespace().compute(self.selectedNamespace().namespace.computes[0]);
-        }
-      }
-    }).fail(function () {
-      self.hasErrors(true);
-    }).always(function () {
-      self.loadedDeferred.resolve();
-      self.loading(false);
-    })
-  };
-
-  AssistDbSource.prototype.highlightInside = function (catalogEntry) {
-    var self = this;
-    if (self.navigationSettings.rightAssist) {
-      return;
-    }
-
-    var whenLoaded = function () {
-      self.namespaces().some(function (namespace) {
-        if (namespace.namespace.id === catalogEntry.namespace.id) {
-          if (self.selectedNamespace() !== namespace) {
-            self.selectedNamespace(namespace);
-          }
-          if (self.selectedNamespace().hasEntries()) {
-            self.selectedNamespace().highlightInside(catalogEntry);
-          } else {
-            self.selectedNamespace().initDatabases(function () {
-              self.selectedNamespace().highlightInside(catalogEntry);
-            })
-          }
-          return true;
-        }
-      })
-    };
-
-    if (self.namespaces().length) {
-      whenLoaded();
-    } else if (self.loading()) {
-      var loadingSub = self.loading.subscribe(function () {
-        loadingSub.dispose();
-        whenLoaded();
-      })
-    } else {
-      self.loadNamespaces().done(whenLoaded);
-    }
-  };
-
-  AssistDbSource.prototype.triggerRefresh = function (data, event) {
-    var self = this;
-    self.loadNamespaces(true);
-  };
-
-  return AssistDbSource;
-})();
-
-
-
-var AssistDbNamespace = (function () {
-
-  /**
-   * @param {Object} options
-   * @param {Object} options.i18n
-   * @param {string} options.sourceType
-   * @param {ContextNamespace} options.namespace
-   * @param {boolean} options.nonSqlType - Optional, default false
-   * @param {Object} options.navigationSettings
-   * @constructor
-   */
-  function AssistDbNamespace (options) {
-    var self = this;
-
-    self.i18n = options.i18n;
-    self.navigationSettings = options.navigationSettings;
-    self.sourceType = options.sourceType;
-    self.nonSqlType = options.nonSqlType;
-
-    self.namespace = options.namespace;
-    self.status = ko.observable(options.namespace.status);
-    // TODO: Compute selection in assist?
-    self.compute = ko.observable();
-    if (self.namespace.computes.length) {
-      self.compute(self.namespace.computes[0]);
-    }
-    self.name = options.namespace.name;
-
-    self.dbIndex = {};
-    self.databases = ko.observableArray();
-    self.selectedDatabase = ko.observable();
-
-    self.highlight = ko.observable(false);
-
-    self.loadedPromise = $.Deferred();
-    self.loadedDeferred = $.Deferred();
-    self.loaded = ko.observable(false);
-    self.loading = ko.observable(false);
-    self.reloading = ko.observable(false);
-    self.hasErrors = ko.observable(false);
-    self.invalidateOnRefresh = ko.observable('cache');
-
-    self.loadingTables = ko.pureComputed(function() {
-      return typeof self.selectedDatabase() !== 'undefined' && self.selectedDatabase() !== null && self.selectedDatabase().loading();
-    });
-
-    self.filter = {
-      querySpec: ko.observable({}).extend({ rateLimit: 300 })
-    };
-
-    self.hasEntries = ko.pureComputed(function() {
-      return self.databases().length > 0;
-    });
-
-    self.filteredEntries = ko.pureComputed(function () {
-      if (!self.filter.querySpec() || typeof self.filter.querySpec().query === 'undefined' || !self.filter.querySpec().query) {
-        return self.databases();
-      }
-      return self.databases().filter(function (database) {
-        return database.catalogEntry.name.toLowerCase().indexOf(self.filter.querySpec().query.toLowerCase()) !== -1
-      });
-    });
-
-    self.autocompleteFromEntries = function (nonPartial, partial) {
-      var result = [];
-      var partialLower = partial.toLowerCase();
-      self.databases().forEach(function (db) {
-        if (db.catalogEntry.name.toLowerCase().indexOf(partialLower) === 0) {
-          result.push(nonPartial + partial + db.catalogEntry.name.substring(partial.length))
-        }
-      });
-      return result;
-    };
-
-    self.selectedDatabase.subscribe(function () {
-      var db = self.selectedDatabase();
-      if (HAS_OPTIMIZER && db && !db.popularityIndexSet && !self.nonSqlType) {
-        db.catalogEntry.loadNavOptPopularityForChildren({ silenceErrors: true }).done(function () {
-          var applyPopularity = function () {
-            db.entries().forEach(function (entry) {
-              if (entry.catalogEntry.navOptPopularity && entry.catalogEntry.navOptPopularity.popularity >= 5) {
-                entry.popularity(entry.catalogEntry.navOptPopularity.popularity )
-              }
-            });
-          };
-
-          if (db.loading()) {
-            var subscription = db.loading.subscribe(function () {
-              subscription.dispose();
-              applyPopularity();
-            });
-          } else if (db.entries().length === 0) {
-            var subscription = db.entries.subscribe(function (newEntries) {
-              if (newEntries.length > 0) {
-                subscription.dispose();
-                applyPopularity();
-              }
-            });
-          } else {
-            applyPopularity();
-          }
-        });
-      }
-    });
-
-    self.selectedDatabaseChanged = function () {
-      if (self.selectedDatabase()) {
-        if (!self.selectedDatabase().hasEntries() && !self.selectedDatabase().loading()) {
-          self.selectedDatabase().loadEntries()
-        }
-        if (!self.navigationSettings.rightAssist) {
-          window.apiHelper.setInTotalStorage('assist_' + self.sourceType + '_' + self.namespace.id, 'lastSelectedDb', self.selectedDatabase().catalogEntry.name);
-          huePubSub.publish('assist.database.set', {
-            sourceType: self.sourceType,
-            namespace: self.namespace,
-            name: self.selectedDatabase().catalogEntry.name
-          })
-        }
-      }
-    };
-
-    var nestedFilter = {
-      querySpec: ko.observable({}).extend({ rateLimit: 300 })
-    };
-
-    self.setDatabase = function (databaseName) {
-      if (databaseName && self.selectedDatabase() && databaseName === self.selectedDatabase().catalogEntry.name) {
-        return;
-      }
-      if (databaseName && self.dbIndex[databaseName]) {
-        self.selectedDatabase(self.dbIndex[databaseName]);
-        self.selectedDatabaseChanged();
-        return;
-      }
-      var lastSelectedDb = window.apiHelper.getFromTotalStorage('assist_' + self.sourceType + '_' + self.namespace.id, 'lastSelectedDb', 'default');
-      if (lastSelectedDb && self.dbIndex[lastSelectedDb]) {
-        self.selectedDatabase(self.dbIndex[lastSelectedDb]);
-        self.selectedDatabaseChanged();
-      } else if (self.databases().length > 0) {
-        self.selectedDatabase(self.databases()[0]);
-        self.selectedDatabaseChanged();
-      }
-    };
-
-    self.initDatabases = function (callback) {
-      if (self.loading()) {
-        return;
-      }
-      self.loading(true);
-      self.hasErrors(false);
-
-      var lastSelectedDbName = self.selectedDatabase() ? self.selectedDatabase().catalogEntry.name : null;
-
-      self.selectedDatabase(null);
-      self.databases([]);
-
-      dataCatalog.getEntry({ sourceType: self.sourceType, namespace: self.namespace, compute: self.compute(), path : [], definition: { type: 'source' } }).done(function (catalogEntry) {
-        self.catalogEntry = catalogEntry;
-        self.catalogEntry.getChildren({ silenceErrors: self.navigationSettings.rightAssist }).done(function (databaseEntries) {
-          self.dbIndex = {};
-          var hasNavMeta = false;
-          var dbs = [];
-
-          databaseEntries.forEach(function (catalogEntry) {
-            hasNavMeta = hasNavMeta || !!catalogEntry.navigatorMeta;
-            var database = new AssistDbEntry(catalogEntry, null, self, nestedFilter, self.i18n, self.navigationSettings);
-            self.dbIndex[catalogEntry.name] = database;
-            if (catalogEntry.name === lastSelectedDbName) {
-              self.selectedDatabase(database);
-              self.selectedDatabaseChanged();
-            }
-            dbs.push(database);
-          });
-
-          if (!hasNavMeta && !self.nonSqlType) {
-            self.catalogEntry.loadNavigatorMetaForChildren({ silenceErrors: true });
-          }
-          self.databases(dbs);
-
-          if (typeof callback === 'function') {
-            callback();
-          }
-        }).fail(function () {
-          self.hasErrors(true);
-        }).always(function () {
-          self.loaded(true);
-          self.loadedDeferred.resolve();
-          self.loading(false);
-          self.reloading(false);
-        });
-
-      });
-    };
-
-    self.modalItem = ko.observable();
-
-    if (!self.navigationSettings.rightAssist) {
-      huePubSub.subscribe('data.catalog.entry.refreshed', function (details) {
-        if (self.namespace.id !== details.entry.namespace.id || details.entry.getSourceType() !== self.sourceType) {
-          return;
-        }
-        if (self.catalogEntry === details.entry) {
-          self.initDatabases();
-        } else {
-          var findAndReloadInside = function (entries) {
-            return entries.some(function (entry) {
-              if (entry.catalogEntry.path.join('.') === details.entry.path.join('.')) {
-                entry.catalogEntry = details.entry;
-                entry.loadEntries();
-                return true;
-              }
-              return findAndReloadInside(entry.entries());
-            })
-          };
-          findAndReloadInside(self.databases());
-        }
-      });
-    }
-  }
-
-  AssistDbNamespace.prototype.whenLoaded = function (callback) {
-    var self = this;
-    self.loadedDeferred.done(callback);
-  };
-
-  AssistDbNamespace.prototype.highlightInside = function (catalogEntry) {
-    var self = this;
-    var foundDb;
-    var index;
-
-    var findDatabase = function () {
-      $.each(self.databases(), function (idx, db) {
-        db.highlight(false);
-        if (db.databaseName === catalogEntry.path[0]) {
-          foundDb = db;
-          index = idx;
-        }
-      });
-
-      if (foundDb) {
-        var whenLoaded = function () {
-          if (self.selectedDatabase() !== foundDb) {
-            self.selectedDatabase(foundDb);
-          }
-          if (!foundDb.open()) {
-            foundDb.open(true);
-          }
-          window.setTimeout(function () {
-            huePubSub.subscribeOnce('assist.db.scrollToComplete', function () {
-              foundDb.highlight(true);
-              // Timeout is for animation effect
-              window.setTimeout(function () {
-                foundDb.highlight(false);
-              }, 1800);
-            });
-            if (catalogEntry.path.length > 1) {
-              foundDb.highlightInside(catalogEntry.path.slice(1));
-            } else {
-              huePubSub.publish('assist.db.scrollTo', foundDb);
-            }
-          }, 0);
-        };
-
-        if (foundDb.hasEntries()) {
-          whenLoaded();
-        } else {
-          foundDb.loadEntries(whenLoaded);
-        }
-      }
-    };
-
-    if (!self.loaded()) {
-      self.initDatabases(findDatabase);
-    } else {
-      findDatabase();
-    }
-  };
-
-  AssistDbNamespace.prototype.triggerRefresh = function (data, event) {
-    var self = this;
-    if (self.catalogEntry) {
-      self.catalogEntry.clearCache({ invalidate: self.invalidateOnRefresh() });
-    }
-  };
-
-  return AssistDbNamespace;
-})();

+ 1 - 1
desktop/core/src/desktop/static/desktop/js/autocomplete/sqlParseSupport.js

@@ -534,7 +534,7 @@ var SqlParseSupport = (function () {
         if (location.type === 'table' && typeof location.identifierChain !== 'undefined' && location.identifierChain.length === 1 && location.identifierChain[0].name) {
           // Could be a cte reference
           parser.yy.locations.some(function (otherLocation) {
-            if (otherLocation.type === 'alias' && otherLocation.source === 'cte' && SqlUtils.identifierEquals(otherLocation.alias, location.identifierChain[0].name)) {
+            if (otherLocation.type === 'alias' && otherLocation.source === 'cte' && sqlUtils.identifierEquals(otherLocation.alias, location.identifierChain[0].name)) {
               // TODO: Possibly add the other location if we want to show the link in the future.
               //       i.e. highlight select definition on hover over alias, also for subquery references.
               location.type = 'alias';

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2040 - 0
desktop/core/src/desktop/static/desktop/js/hue-bundle-8e5037d20b056ab61e91.js


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/hue-bundle-8e5037d20b056ab61e91.js.map


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/hue-bundle-fbeb7ee72f1233caa7d8.js.map


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

@@ -4057,7 +4057,7 @@
           return location.resolvePathPromise;
         }
 
-        var promise = SqlUtils.resolveCatalogEntry({
+        var promise = sqlUtils.resolveCatalogEntry({
           sourceType: sourceType,
           namespace: namespace,
           compute: compute,
@@ -4704,7 +4704,7 @@
 
         var suppressedRules = window.apiHelper.getFromTotalStorage('hue.syntax.checker', 'suppressedRules', {});
         if (e.data.syntaxError && e.data.syntaxError.ruleId && !suppressedRules[e.data.syntaxError.ruleId.toString() + e.data.syntaxError.text.toLowerCase()]) {
-          if (self.snippet.positionStatement() && SqlUtils.locationEquals(e.data.statementLocation, self.snippet.positionStatement().location)) {
+          if (self.snippet.positionStatement() && sqlUtils.locationEquals(e.data.statementLocation, self.snippet.positionStatement().location)) {
             self.snippet.positionStatement().syntaxError = true;
           }
           if (hueDebug.showSyntaxParseResult) {
@@ -4851,7 +4851,7 @@
                 silenceErrors: true
               }).done(function (entries) {
                 var containsColumn = entries.some(function (entry) {
-                  return SqlUtils.identifierEquals(entry.name, location.identifierChain[0].name);
+                  return sqlUtils.identifierEquals(entry.name, location.identifierChain[0].name);
                 });
 
                 if (containsColumn) {
@@ -4938,7 +4938,7 @@
             var uniqueIndex = {};
             var uniqueValues = [];
             for (var i = 0; i < possibleValues.length; i++) {
-              possibleValues[i].name = SqlUtils.backTickIfNeeded(self.snippet.type(), possibleValues[i].name);
+              possibleValues[i].name = sqlUtils.backTickIfNeeded(self.snippet.type(), possibleValues[i].name);
               var nameLower = possibleValues[i].name.toLowerCase();
               if ((nameLower === tokenValLower) || (tokenValLower.indexOf('`') === 0 && tokenValLower.replace(/`/g, '') === nameLower)) {
                 // Break if found
@@ -5796,28 +5796,28 @@
       var menu = ko.bindingHandlers.contextMenu.initContextMenu($tableDropMenu, $('.content-panel'));
 
       $tableDropMenu.find('.editor-drop-value').click(function () {
-        insertSqlAtCursor(SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ' ', 0, menu);
+        insertSqlAtCursor(sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ' ', 0, menu);
       });
 
       $tableDropMenu.find('.editor-drop-select').click(function () {
-        insertSqlAtCursor('SELECT * FROM ' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ' LIMIT 100;', -1, menu);
+        insertSqlAtCursor('SELECT * FROM ' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ' LIMIT 100;', -1, menu);
         $tableDropMenu.hide();
       });
 
       $tableDropMenu.find('.editor-drop-insert').click(function () {
-        insertSqlAtCursor('INSERT INTO ' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ' VALUES ();', -2, menu);
+        insertSqlAtCursor('INSERT INTO ' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ' VALUES ();', -2, menu);
       });
 
       $tableDropMenu.find('.editor-drop-update').click(function () {
-        insertSqlAtCursor('UPDATE ' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ' SET ', 0, menu);
+        insertSqlAtCursor('UPDATE ' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ' SET ', 0, menu);
       });
 
       $tableDropMenu.find('.editor-drop-view').click(function () {
-        insertSqlAtCursor('DROP VIEW ' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ';', -1, menu);
+        insertSqlAtCursor('DROP VIEW ' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ';', -1, menu);
       });
 
       $tableDropMenu.find('.editor-drop-drop').click(function () {
-        insertSqlAtCursor('DROP TABLE ' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + SqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ';', -1, menu);
+        insertSqlAtCursor('DROP TABLE ' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.database) + '.' + sqlUtils.backTickIfNeeded(lastMeta.type, lastMeta.table) + ';', -1, menu);
       });
 
       $el.droppable({

+ 9 - 9
desktop/core/src/desktop/static/desktop/js/sqlAutocompleter3.js

@@ -177,7 +177,7 @@ var AutocompleteResults = (function () {
       var result = self.entries();
 
       if (self.filter()) {
-        result = SqlUtils.autocompleteFilter(self.filter(), result);
+        result = sqlUtils.autocompleteFilter(self.filter(), result);
         huePubSub.publish('hue.ace.autocompleter.match.updated');
       }
 
@@ -199,7 +199,7 @@ var AutocompleteResults = (function () {
         return activeCategory === CATEGORIES.ALL || activeCategory === suggestion.category || (activeCategory === CATEGORIES.POPULAR && suggestion.popular());
       });
 
-      SqlUtils.sortSuggestions(result, self.filter(), self.sortOverride);
+      sqlUtils.sortSuggestions(result, self.filter(), self.sortOverride);
       self.sortOverride = null;
       return result;
     }).extend({ rateLimit: 200 });
@@ -515,7 +515,7 @@ var AutocompleteResults = (function () {
       databasesDeferred.done(function (catalogEntries) {
         catalogEntries.forEach(function (dbEntry) {
           databaseSuggestions.push({
-            value: prefix + SqlUtils.backTickIfNeeded(self.snippet.type(), dbEntry.name) + (suggestDatabases.appendDot ? '.' : ''),
+            value: prefix + sqlUtils.backTickIfNeeded(self.snippet.type(), dbEntry.name) + (suggestDatabases.appendDot ? '.' : ''),
             filterValue: dbEntry.name,
             meta: HUE_I18n.autocomplete.meta.database,
             category: CATEGORIES.DATABASE,
@@ -556,7 +556,7 @@ var AutocompleteResults = (function () {
                 return;
               }
               tableSuggestions.push({
-                value: prefix + SqlUtils.backTickIfNeeded(self.snippet.type(), tableEntry.name),
+                value: prefix + sqlUtils.backTickIfNeeded(self.snippet.type(), tableEntry.name),
                 filterValue: tableEntry.name,
                 tableName: tableEntry.name,
                 meta: HUE_I18n.autocomplete.meta[tableEntry.getType().toLowerCase()],
@@ -666,7 +666,7 @@ var AutocompleteResults = (function () {
               var type = typeof column.type !== 'undefined' && column.type !== 'COLREF' ? column.type : 'T';
               if (typeof column.alias !== 'undefined') {
                 columnSuggestions.push({
-                  value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.alias),
+                  value: sqlUtils.backTickIfNeeded(self.snippet.type(), column.alias),
                   filterValue: column.alias,
                   meta: type,
                   category: CATEGORIES.COLUMN,
@@ -676,7 +676,7 @@ var AutocompleteResults = (function () {
                 })
               } else if (typeof column.identifierChain !== 'undefined' && column.identifierChain.length > 0 && typeof column.identifierChain[column.identifierChain.length - 1].name !== 'undefined') {
                 columnSuggestions.push({
-                  value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.identifierChain[column.identifierChain.length - 1].name),
+                  value: sqlUtils.backTickIfNeeded(self.snippet.type(), column.identifierChain[column.identifierChain.length - 1].name),
                   filterValue: column.identifierChain[column.identifierChain.length - 1].name,
                   meta: type,
                   category: CATEGORIES.COLUMN,
@@ -702,7 +702,7 @@ var AutocompleteResults = (function () {
             var type = typeof column.type !== 'undefined' && column.type !== 'COLREF' ? column.type : 'T';
             if (column.alias) {
               columnSuggestions.push({
-                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.alias),
+                value: sqlUtils.backTickIfNeeded(self.snippet.type(), column.alias),
                 filterValue: column.alias,
                 meta: type,
                 category: CATEGORIES.COLUMN,
@@ -712,7 +712,7 @@ var AutocompleteResults = (function () {
               })
             } else if (column.identifierChain && column.identifierChain.length > 0) {
               columnSuggestions.push({
-                value: SqlUtils.backTickIfNeeded(self.snippet.type(), column.identifierChain[column.identifierChain.length - 1].name),
+                value: sqlUtils.backTickIfNeeded(self.snippet.type(), column.identifierChain[column.identifierChain.length - 1].name),
                 filterValue: column.identifierChain[column.identifierChain.length - 1].name,
                 meta: type,
                 category: CATEGORIES.COLUMN,
@@ -739,7 +739,7 @@ var AutocompleteResults = (function () {
           self.cancellablePromises.push(dataCatalogEntry.getChildren({ silenceErrors: true, cancellable: true })
             .done(function (childEntries) {
               childEntries.forEach(function (childEntry) {
-                var name = SqlUtils.backTickIfNeeded(self.snippet.type(), childEntry.name);
+                var name = sqlUtils.backTickIfNeeded(self.snippet.type(), childEntry.name);
                 if (self.snippet.type() === 'hive' && (childEntry.isArray() || childEntry.isMap())) {
                   name += '[]';
                 }

+ 5 - 5
desktop/core/src/desktop/static/desktop/js/sqlUtils.js

@@ -190,7 +190,7 @@ var SqlUtils = (function () {
       }).done(function (childEntries) {
         var foundEntry = undefined;
         childEntries.some(function (childEntry) {
-          if (SqlUtils.identifierEquals(childEntry.name, nextField)) {
+          if (sqlUtils.identifierEquals(childEntry.name, nextField)) {
             foundEntry = childEntry;
             return true;
           }
@@ -221,7 +221,7 @@ var SqlUtils = (function () {
         sourceType: options.sourceType,
         namespace: options.namespace,
         compute: options.compute,
-        path: SqlUtils.identifierChainToPath(nextTable.identifierChain),
+        path: sqlUtils.identifierChainToPath(nextTable.identifierChain),
         cachedOnly: options && options.cachedOnly,
         cancellable: options && options.cancellable,
         temporaryOnly: options && options.temporaryOnly,
@@ -229,14 +229,14 @@ var SqlUtils = (function () {
       }).done(function (childEntries) {
         var foundEntry = undefined;
         childEntries.some(function (childEntry) {
-          if (SqlUtils.identifierEquals(childEntry.name, options.identifierChain[0].name)) {
+          if (sqlUtils.identifierEquals(childEntry.name, options.identifierChain[0].name)) {
             foundEntry = childEntry;
             return true;
           }
         });
 
         if (foundEntry && options.identifierChain.length > 1) {
-          findInTree(foundEntry, SqlUtils.identifierChainToPath(options.identifierChain.slice(1)));
+          findInTree(foundEntry, sqlUtils.identifierChainToPath(options.identifierChain.slice(1)));
         } else if (foundEntry) {
           deferred.resolve(foundEntry);
         } else {
@@ -258,7 +258,7 @@ var SqlUtils = (function () {
         temporaryOnly: options && options.temporaryOnly,
         silenceErrors: true
       }).done(function (entry) {
-        findInTree(entry, SqlUtils.identifierChainToPath(options.identifierChain))
+        findInTree(entry, sqlUtils.identifierChainToPath(options.identifierChain))
       })
     }
 

+ 0 - 5
desktop/core/src/desktop/templates/assist.mako

@@ -34,11 +34,6 @@ from desktop.views import _ko
 <%namespace name="impalaDocIndex" file="/impala_doc_index.mako" />
 
 <%def name="assistJSModels()">
-<script src="${ static('desktop/js/assist/assistDbEntry.js') }"></script>
-<script src="${ static('desktop/js/assist/assistDbSource.js') }"></script>
-<script src="${ static('desktop/js/assist/assistStorageEntry.js') }"></script>
-<script src="${ static('desktop/js/assist/assistGitEntry.js') }"></script>
-<script src="${ static('desktop/js/assist/assistHBaseEntry.js') }"></script>
 <script src="${ static('desktop/js/document/hueDocument.js') }"></script>
 <script src="${ static('desktop/js/document/hueFileEntry.js') }"></script>
 </%def>

+ 0 - 1
desktop/core/src/desktop/templates/hue.mako

@@ -485,7 +485,6 @@ ${ render_bundle('hue') | n,unicode }
 <script src="${ static('desktop/js/hue.colors.js') }"></script>
 
 <script src="${ static('desktop/js/ko.hue-bindings.js') }"></script>
-<script src="${ static('desktop/js/sqlUtils.js') }"></script>
 <script src="${ static('desktop/js/sqlFunctions.js') }"></script>
 
 <script src="${ static('desktop/js/autocomplete/sqlParseSupport.js') }"></script>

+ 3 - 3
desktop/core/src/desktop/templates/ko_components/ko_context_popover.mako

@@ -736,12 +736,12 @@ from metadata.conf import has_navigator
             location: data.location,
             text: $.map(colsToExpand, function (column) {
               if (column.tableAlias) {
-                return SqlUtils.backTickIfNeeded(sourceType, column.tableAlias) + '.' + SqlUtils.backTickIfNeeded(sourceType, column.name);
+                return sqlUtils.backTickIfNeeded(sourceType, column.tableAlias) + '.' + sqlUtils.backTickIfNeeded(sourceType, column.name);
               }
               if (colIndex[column.name]) {
-                return SqlUtils.backTickIfNeeded(sourceType, column.table) + '.' + SqlUtils.backTickIfNeeded(sourceType, column.name);
+                return sqlUtils.backTickIfNeeded(sourceType, column.table) + '.' + sqlUtils.backTickIfNeeded(sourceType, column.name);
               }
-              return SqlUtils.backTickIfNeeded(sourceType, column.name)
+              return sqlUtils.backTickIfNeeded(sourceType, column.name)
             }).join(', ')
           });
           huePubSub.publish('context.popover.hide');

+ 4 - 4
desktop/core/src/desktop/templates/ko_components/ko_simple_ace_editor.mako

@@ -485,11 +485,11 @@ from desktop.views import _ko
             var result = self.entries();
 
             if (self.filter()) {
-              result = SqlUtils.autocompleteFilter(self.filter(), result);
+              result = sqlUtils.autocompleteFilter(self.filter(), result);
               huePubSub.publish('hue.ace.autocompleter.match.updated');
             }
 
-            SqlUtils.sortSuggestions(result, self.filter(), self.sortOverride);
+            sqlUtils.sortSuggestions(result, self.filter(), self.sortOverride);
 
             return result;
           });
@@ -604,11 +604,11 @@ from desktop.views import _ko
             var result = self.entries();
 
             if (self.filter()) {
-              result = SqlUtils.autocompleteFilter(self.filter(), result);
+              result = sqlUtils.autocompleteFilter(self.filter(), result);
               huePubSub.publish('hue.ace.autocompleter.match.updated');
             }
 
-            SqlUtils.sortSuggestions(result, self.filter(), self.sortOverride);
+            sqlUtils.sortSuggestions(result, self.filter(), self.sortOverride);
 
             return result;
           });

+ 3 - 3
desktop/libs/indexer/src/indexer/templates/importer.mako

@@ -1641,7 +1641,7 @@ ${ assist.assistPanel() }
           var statementCols = [];
           var temporaryColumns = [];
           sampleCols.forEach(function (sampleCol) {
-            statementCols.push(SqlUtils.backTickIfNeeded(self.sourceType, sampleCol.name()));
+            statementCols.push(sqlUtils.backTickIfNeeded(self.sourceType, sampleCol.name()));
             var col = {
               name: sampleCol.name(),
               type: sampleCol.type()
@@ -1661,12 +1661,12 @@ ${ assist.assistPanel() }
 
           var statement = 'SELECT ';
           statement += statementCols.join(',\n    ');
-          statement += '\n FROM ' + SqlUtils.backTickIfNeeded(self.sourceType, tableName) + ';';
+          statement += '\n FROM ' + sqlUtils.backTickIfNeeded(self.sourceType, tableName) + ';';
           if (!wizard.destination.fieldEditorValue() || wizard.destination.fieldEditorValue() === lastStatement) {
             wizard.destination.fieldEditorValue(statement);
           }
           lastStatement = statement;
-          wizard.destination.fieldEditorPlaceHolder('${ _('Example: SELECT') }' + ' * FROM ' + SqlUtils.backTickIfNeeded(self.sourceType, tableName));
+          wizard.destination.fieldEditorPlaceHolder('${ _('Example: SELECT') }' + ' * FROM ' + sqlUtils.backTickIfNeeded(self.sourceType, tableName));
 
           var handle = dataCatalog.addTemporaryTable({
             sourceType: self.sourceType,

+ 1 - 1
webpack-stats.json

@@ -1 +1 @@
-{"status":"done","chunks":{"hue":[{"name":"hue-bundle-fbeb7ee72f1233caa7d8.js","path":"/Users/jahlen/dev/hue/desktop/core/src/desktop/static/desktop/js/hue-bundle-fbeb7ee72f1233caa7d8.js"},{"name":"hue-bundle-fbeb7ee72f1233caa7d8.js.map","path":"/Users/jahlen/dev/hue/desktop/core/src/desktop/static/desktop/js/hue-bundle-fbeb7ee72f1233caa7d8.js.map"}]}}
+{"status":"done","chunks":{"hue":[{"name":"hue-bundle-8e5037d20b056ab61e91.js","path":"/Users/jahlen/dev/hue/desktop/core/src/desktop/static/desktop/js/hue-bundle-8e5037d20b056ab61e91.js"},{"name":"hue-bundle-8e5037d20b056ab61e91.js.map","path":"/Users/jahlen/dev/hue/desktop/core/src/desktop/static/desktop/js/hue-bundle-8e5037d20b056ab61e91.js.map"}]}}

Vissa filer visades inte eftersom för många filer har ändrats