Browse Source

HUE-8687 [frontend] Fix linting issues

Johan Ahlen 6 years ago
parent
commit
cee13ff7ae
30 changed files with 3675 additions and 1785 deletions
  1. 2 2
      .eslintrc.js
  2. 298 214
      desktop/core/src/desktop/js/api/apiHelper.js
  3. 8 8
      desktop/core/src/desktop/js/api/apiQueueManager.js
  4. 28 26
      desktop/core/src/desktop/js/api/cancellablePromise.js
  5. 185 118
      desktop/core/src/desktop/js/assist/assistDbEntry.js
  6. 126 74
      desktop/core/src/desktop/js/assist/assistDbNamespace.js
  7. 122 96
      desktop/core/src/desktop/js/assist/assistDbSource.js
  8. 37 31
      desktop/core/src/desktop/js/assist/assistGitEntry.js
  9. 19 15
      desktop/core/src/desktop/js/assist/assistHBaseEntry.js
  10. 81 67
      desktop/core/src/desktop/js/assist/assistStorageEntry.js
  11. 6 6
      desktop/core/src/desktop/js/assist/assistViewModel.js
  12. 4 5
      desktop/core/src/desktop/js/catalog/catalogUtils.js
  13. 73 55
      desktop/core/src/desktop/js/catalog/contextCatalog.js
  14. 385 255
      desktop/core/src/desktop/js/catalog/dataCatalog.js
  15. 463 290
      desktop/core/src/desktop/js/catalog/dataCatalogEntry.js
  16. 47 29
      desktop/core/src/desktop/js/catalog/generalDataCatalog.js
  17. 80 30
      desktop/core/src/desktop/js/catalog/multiTableEntry.js
  18. 26 26
      desktop/core/src/desktop/js/hue.js
  19. 673 139
      desktop/core/src/desktop/js/sql/sqlUtils.js
  20. 5 5
      desktop/core/src/desktop/js/utils/hueAnalytics.js
  21. 10 8
      desktop/core/src/desktop/js/utils/hueDebug.js
  22. 22 23
      desktop/core/src/desktop/js/utils/hueDrop.js
  23. 45 31
      desktop/core/src/desktop/js/utils/huePubSub.js
  24. 121 92
      desktop/core/src/desktop/js/utils/hueUtils.js
  25. 138 138
      desktop/core/src/desktop/static/desktop/js/hue-bundle-72c7c47509e0d902668b.js
  26. 0 0
      desktop/core/src/desktop/static/desktop/js/hue-bundle-72c7c47509e0d902668b.js.map
  27. 0 0
      desktop/core/src/desktop/static/desktop/js/hue-bundle-8e5037d20b056ab61e91.js.map
  28. 669 0
      package-lock.json
  29. 1 1
      package.json
  30. 1 1
      webpack-stats.json

+ 2 - 2
.eslintrc.js

@@ -4,13 +4,13 @@ const normalGlobals = [
 ];
 const hueGlobals = [
   // global_js_constants.mako
-  'IS_HUE_4', 'AUTOCOMPLETE_TIMEOUT','CSRF_TOKEN','HAS_MULTI_CLUSTER',
+  'IS_HUE_4', 'AUTOCOMPLETE_TIMEOUT','CACHEABLE_TTL','CSRF_TOKEN','HAS_MULTI_CLUSTER',
   'DROPZONE_HOME_DIR', 'ENABLE_SQL_SYNTAX_CHECK', 'HAS_NAVIGATOR', 'HAS_OPTIMIZER', 'HAS_WORKLOAD_ANALYTICS',
   'HUE_CONTAINER', 'IS_EMBEDDED', 'IS_K8S_ONLY', 'HUE_VERSION', 'IS_NEW_INDEXER_ENABLED',
   'IS_S3_ENABLED', 'DOCUMENT_TYPES', 'LOGGED_USERNAME', 'USER_HOME_DIR', 'LOGGED_USERGROUPS', 'METASTORE_PARTITION_LIMIT',
 
   // other misc, TODO
-  'huePubSub', 'ApiHelper', 'SqlUtils', 'ContextCatalog', 'DataCatalog'
+  'huePubSub', 'ApiHelper', 'SqlUtils', 'trackOnGA', 'ContextCatalog', 'DataCatalog'
 ];
 
 const globals = normalGlobals.concat(hueGlobals).reduce((acc, key) => {

File diff suppressed because it is too large
+ 298 - 214
desktop/core/src/desktop/js/api/apiHelper.js


+ 8 - 8
desktop/core/src/desktop/js/api/apiQueueManager.js

@@ -16,24 +16,24 @@
 
 class ApiQueueManager {
   constructor() {
-    var self = this;
+    const self = this;
     self.callQueue = {};
   }
 
   getQueued(url, hash) {
-    var self = this;
+    const self = this;
     return self.callQueue[url + (hash || '')];
-  };
+  }
 
   addToQueue(promise, url, hash) {
-    var self = this;
+    const self = this;
     self.callQueue[url + (hash || '')] = promise;
-    promise.always(function () {
+    promise.always(() => {
       delete self.callQueue[url + (hash || '')];
-    })
-  };
+    });
+  }
 }
 
-let apiQueueManager = new ApiQueueManager();
+const apiQueueManager = new ApiQueueManager();
 
 export default apiQueueManager;

+ 28 - 26
desktop/core/src/desktop/js/api/cancellablePromise.js

@@ -14,12 +14,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import apiHelper from './apiHelper'
+import apiHelper from 'api/apiHelper';
 
 class CancellablePromise {
-
-  constructor (deferred, request, otherCancellables) {
-    var self = this;
+  constructor(deferred, request, otherCancellables) {
+    const self = this;
     self.cancelCallbacks = [];
     self.deferred = deferred;
     self.request = request;
@@ -38,13 +37,13 @@ class CancellablePromise {
    * @returns {CancellablePromise}
    */
   preventCancel() {
-    var self = this;
+    const self = this;
     self.cancelPrevented = true;
     return self;
-  };
+  }
 
   cancel() {
-    var self = this;
+    const self = this;
     if (self.cancelPrevented || self.cancelled || self.state() !== 'pending') {
       return;
     }
@@ -58,66 +57,69 @@ class CancellablePromise {
     }
 
     if (self.otherCancellables) {
-      self.otherCancellables.forEach(function (cancellable) { if (cancellable.cancel) { cancellable.cancel() } });
+      self.otherCancellables.forEach(cancellable => {
+        if (cancellable.cancel) {
+          cancellable.cancel();
+        }
+      });
     }
 
     while (self.cancelCallbacks.length) {
       self.cancelCallbacks.pop()();
     }
     return self;
-  };
+  }
 
   onCancel(callback) {
-    var self = this;
+    const self = this;
     if (self.cancelled) {
       callback();
     } else {
       self.cancelCallbacks.push(callback);
     }
     return self;
-  };
+  }
 
   then() {
-    var self = this;
+    const self = this;
     self.deferred.then.apply(self.deferred, arguments);
     return self;
-  };
+  }
 
   done(callback) {
-    var self = this;
+    const self = this;
     self.deferred.done.apply(self.deferred, arguments);
     return self;
-  };
+  }
 
   fail(callback) {
-    var self = this;
+    const self = this;
     self.deferred.fail.apply(self.deferred, arguments);
     return self;
-  };
+  }
 
   always(callback) {
-    var self = this;
+    const self = this;
     self.deferred.always.apply(self.deferred, arguments);
     return self;
-  };
+  }
 
   pipe(callback) {
-    var self = this;
+    const self = this;
     self.deferred.pipe.apply(self.deferred, arguments);
     return self;
-  };
+  }
 
   progress(callback) {
-    var self = this;
+    const self = this;
     self.deferred.progress.apply(self.deferred, arguments);
     return self;
-  };
+  }
 
   state() {
-    var self = this;
+    const self = this;
     return self.deferred.state && self.deferred.state();
-  };
+  }
 }
 
 export default CancellablePromise;
-

+ 185 - 118
desktop/core/src/desktop/js/assist/assistDbEntry.js

@@ -14,14 +14,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import $ from 'jquery'
-import ko from 'knockout'
+import $ from 'jquery';
+import ko from 'knockout';
 
-import huePubSub from '../utils/huePubSub'
-import sqlUtils from '../sql/sqlUtils'
+import huePubSub from 'utils/huePubSub';
+import sqlUtils from 'sql/sqlUtils';
 
 const findNameInHierarchy = (entry, searchCondition) => {
-  let sourceType = entry.sourceType;
+  const sourceType = entry.sourceType;
   while (entry && !searchCondition(entry)) {
     entry = entry.parent;
   }
@@ -42,8 +42,8 @@ class AssistDbEntry {
    * @param {Object} navigationSettings
    * @constructor
    */
-  constructor (catalogEntry, parent, assistDbNamespace, filter, i18n, navigationSettings) {
-    let self = this;
+  constructor(catalogEntry, parent, assistDbNamespace, filter, i18n, navigationSettings) {
+    const self = this;
     self.catalogEntry = catalogEntry;
     self.parent = parent;
     self.assistDbNamespace = assistDbNamespace;
@@ -52,7 +52,7 @@ class AssistDbEntry {
     self.navigationSettings = navigationSettings;
 
     self.sourceType = assistDbNamespace.sourceType;
-    self.invalidateOnRefresh =  assistDbNamespace.invalidateOnRefresh;
+    self.invalidateOnRefresh = assistDbNamespace.invalidateOnRefresh;
 
     self.expandable = self.catalogEntry.hasPossibleChildren();
 
@@ -87,13 +87,15 @@ class AssistDbEntry {
       }
     });
 
-    self.hasEntries = ko.pureComputed(() => self.entries().length > 0)
+    self.hasEntries = ko.pureComputed(() => self.entries().length > 0);
 
     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
+      const facets = self.filter.querySpec().facets;
+      const 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
-      let textMatch = (!self.catalogEntry.isDatabase() && !self.filterColumnNames()) || (!self.filter.querySpec().text || self.filter.querySpec().text.length === 0);
+      const textMatch =
+        (!self.catalogEntry.isDatabase() && !self.filterColumnNames()) ||
+        (!self.filter.querySpec().text || self.filter.querySpec().text.length === 0);
 
       if (facetMatch && textMatch) {
         return self.entries();
@@ -104,17 +106,26 @@ class AssistDbEntry {
 
         if (match && !facetMatch) {
           if (entry.catalogEntry.isField()) {
-            match = (Object.keys(facets['type']).length === 2 && facets['type']['table'] && facets['type']['view'])
-              || (Object.keys(facets['type']).length === 1 && (facets['type']['table'] || facets['type']['view']))
-              || facets['type'][entry.catalogEntry.getType()];
+            match =
+              (Object.keys(facets['type']).length === 2 &&
+                facets['type']['table'] &&
+                facets['type']['view']) ||
+              (Object.keys(facets['type']).length === 1 &&
+                (facets['type']['table'] || facets['type']['view'])) ||
+              facets['type'][entry.catalogEntry.getType()];
           } else if (entry.catalogEntry.isTableOrView()) {
-            match = (!facets['type']['table'] && !facets['type']['view']) || (facets['type']['table'] && entry.catalogEntry.isTable()) || (facets['type']['view'] && entry.catalogEntry.isView());
+            match =
+              (!facets['type']['table'] && !facets['type']['view']) ||
+              (facets['type']['table'] && entry.catalogEntry.isTable()) ||
+              (facets['type']['view'] && entry.catalogEntry.isView());
           }
         }
 
         if (match && !textMatch) {
-          let nameLower = entry.catalogEntry.name.toLowerCase();
-          match = self.filter.querySpec().text.every(text => nameLower.indexOf(text.toLowerCase()) !== -1)
+          const nameLower = entry.catalogEntry.name.toLowerCase();
+          match = self.filter
+            .querySpec()
+            .text.every(text => nameLower.indexOf(text.toLowerCase()) !== -1);
         }
 
         return match;
@@ -122,11 +133,11 @@ class AssistDbEntry {
     });
 
     self.autocompleteFromEntries = (nonPartial, partial) => {
-      let result = [];
-      let partialLower = partial.toLowerCase();
+      const result = [];
+      const 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))
+          result.push(nonPartial + partial + entry.catalogEntry.name.substring(partial.length));
         }
       });
       return result;
@@ -157,15 +168,16 @@ class AssistDbEntry {
   }
 
   knownFacetValues() {
-    let self = this;
-    let types = {};
-    if (self.parent === null) { // Only find facets on the DB level
+    const self = this;
+    const types = {};
+    if (self.parent === null) {
+      // Only find facets on the DB level
       self.entries().forEach(tableEntry => {
         if (!self.assistDbNamespace.nonSqlType) {
           if (tableEntry.catalogEntry.isTable()) {
-            types.table =  types.table ? types.table + 1 : 1;
+            types.table = types.table ? types.table + 1 : 1;
           } else if (tableEntry.catalogEntry.isView()) {
-            types.view =  types.view ? types.view + 1 : 1;
+            types.view = types.view ? types.view + 1 : 1;
           }
         }
         if (tableEntry.open()) {
@@ -175,54 +187,54 @@ class AssistDbEntry {
             } else {
               types[colEntry.catalogEntry.getType()]++;
             }
-          })
+          });
         }
       });
     }
     if (Object.keys(types).length) {
-      return { type: types }
+      return { type: types };
     }
     return {};
-  };
+  }
 
   getDatabaseName() {
     return findNameInHierarchy(this, entry => entry.catalogEntry.isDatabase());
-  };
+  }
 
   getTableName() {
     return findNameInHierarchy(this, entry => entry.catalogEntry.isTableOrView());
-  };
+  }
 
   getColumnName() {
     return findNameInHierarchy(this, entry => entry.catalogEntry.isColumn());
-  };
+  }
 
   getComplexName() {
     let entry = this;
-    let sourceType = entry.sourceType;
-    let parts = [];
+    const sourceType = entry.sourceType;
+    const parts = [];
     while (entry != null) {
       if (entry.catalogEntry.isTableOrView()) {
         break;
       }
       if (entry.catalogEntry.isArray() || entry.catalogEntry.isMapValue()) {
         if (sourceType === 'hive') {
-          parts.push("[]");
+          parts.push('[]');
         }
       } else {
         parts.push(sqlUtils.backTickIfNeeded(sourceType, entry.catalogEntry.name));
-        parts.push(".");
+        parts.push('.');
       }
       entry = entry.parent;
     }
     parts.reverse();
-    return parts.slice(1).join("");
-  };
+    return parts.slice(1).join('');
+  }
 
   showContextPopover(entry, event, positionAdjustment) {
-    let self = this;
-    let $source = $(event.target);
-    let offset = $source.offset();
+    const self = this;
+    const $source = $(event.target);
+    const offset = $source.offset();
     if (positionAdjustment) {
       offset.left += positionAdjustment.left;
       offset.top += positionAdjustment.top;
@@ -248,17 +260,17 @@ class AssistDbEntry {
     huePubSub.subscribeOnce('context.popover.hidden', () => {
       self.statsVisible(false);
     });
-  };
+  }
 
   triggerRefresh() {
-    let self = this;
+    const self = this;
     self.catalogEntry.clearCache({ invalidate: self.invalidateOnRefresh(), cascade: true });
-  };
+  }
 
   highlightInside(path) {
-    let self = this;
+    const self = this;
 
-    let searchEntry = () => {
+    const searchEntry = () => {
       let foundEntry;
       self.entries().forEach(entry => {
         entry.highlight(false);
@@ -290,7 +302,7 @@ class AssistDbEntry {
 
     if (self.entries().length === 0) {
       if (self.loading()) {
-        let subscription = self.loading.subscribe(newVal => {
+        const subscription = self.loading.subscribe(newVal => {
           if (!newVal) {
             subscription.dispose();
             searchEntry();
@@ -302,51 +314,56 @@ class AssistDbEntry {
     } else {
       searchEntry();
     }
-  };
+  }
 
   loadEntries(callback) {
-    let self = this;
+    const self = this;
     if (!self.expandable || self.loading()) {
       return;
     }
     self.loading(true);
 
-    let loadEntriesDeferred = $.Deferred();
+    const loadEntriesDeferred = $.Deferred();
 
-    let successCallback = sourceMeta => {
+    const successCallback = sourceMeta => {
       self.entries([]);
       if (!sourceMeta.notFound) {
-        self.catalogEntry.getChildren({ silenceErrors: self.navigationSettings.rightAssist }).done( catalogEntries => {
-          self.hasErrors(false);
-          self.loading(false);
-          self.loaded = true;
-          if (catalogEntries.length === 0) {
-            self.entries([]);
-            return;
-          }
-          let newEntries = [];
-          catalogEntries.forEach(catalogEntry => {
-            newEntries.push(self.createEntry(catalogEntry));
-          });
-          if (sourceMeta.type === 'array') {
-            self.entries(newEntries);
-            self.entries()[0].open(true);
-          } else {
-            self.entries(newEntries);
-          }
+        self.catalogEntry
+          .getChildren({ silenceErrors: self.navigationSettings.rightAssist })
+          .done(catalogEntries => {
+            self.hasErrors(false);
+            self.loading(false);
+            self.loaded = true;
+            if (catalogEntries.length === 0) {
+              self.entries([]);
+              return;
+            }
+            const newEntries = [];
+            catalogEntries.forEach(catalogEntry => {
+              newEntries.push(self.createEntry(catalogEntry));
+            });
+            if (sourceMeta.type === 'array') {
+              self.entries(newEntries);
+              self.entries()[0].open(true);
+            } else {
+              self.entries(newEntries);
+            }
 
-          loadEntriesDeferred.resolve(newEntries);
-          if (typeof callback === 'function') {
-            callback();
-          }
-        }).fail(() => {
-          self.loading(false);
-          self.loaded = true;
-          self.hasErrors(true);
-        });
+            loadEntriesDeferred.resolve(newEntries);
+            if (typeof callback === 'function') {
+              callback();
+            }
+          })
+          .fail(() => {
+            self.loading(false);
+            self.loaded = true;
+            self.hasErrors(true);
+          });
 
         if (!self.assistDbNamespace.nonSqlType) {
-          self.catalogEntry.loadNavigatorMetaForChildren({ silenceErrors: self.navigationSettings.rightAssist });
+          self.catalogEntry.loadNavigatorMetaForChildren({
+            silenceErrors: self.navigationSettings.rightAssist
+          });
         }
       } else {
         self.hasErrors(true);
@@ -358,77 +375,124 @@ class AssistDbEntry {
       }
     };
 
-    let errorCallback = () => {
+    const errorCallback = () => {
       self.hasErrors(true);
       self.loading(false);
       self.loaded = true;
       loadEntriesDeferred.resolve([]);
     };
 
-    if (!self.navigationSettings.rightAssist && HAS_OPTIMIZER && (self.catalogEntry.isTable() || self.catalogEntry.isDatabase()) && !self.assistDbNamespace.nonSqlType) {
+    if (
+      !self.navigationSettings.rightAssist &&
+      HAS_OPTIMIZER &&
+      (self.catalogEntry.isTable() || self.catalogEntry.isDatabase()) &&
+      !self.assistDbNamespace.nonSqlType
+    ) {
       self.catalogEntry.loadNavOptPopularityForChildren({ silenceErrors: true }).done(() => {
         loadEntriesDeferred.done(() => {
           if (!self.hasErrors()) {
             self.entries().forEach(entry => {
               if (entry.catalogEntry.navOptPopularity) {
                 if (entry.catalogEntry.navOptPopularity.popularity) {
-                  entry.popularity(entry.catalogEntry.navOptPopularity.popularity)
+                  entry.popularity(entry.catalogEntry.navOptPopularity.popularity);
                 } else if (entry.catalogEntry.navOptPopularity.column_count) {
-                  entry.popularity(entry.catalogEntry.navOptPopularity.column_count)
+                  entry.popularity(entry.catalogEntry.navOptPopularity.column_count);
                 } else if (entry.catalogEntry.navOptPopularity.selectColumn) {
                   entry.popularity(entry.catalogEntry.navOptPopularity.selectColumn.columnCount);
                 }
               }
             });
           }
-        })
+        });
       });
     }
 
-    self.catalogEntry.getSourceMeta({ silenceErrors: self.navigationSettings.rightAssist }).done(successCallback).fail(errorCallback);
-  };
+    self.catalogEntry
+      .getSourceMeta({ silenceErrors: self.navigationSettings.rightAssist })
+      .done(successCallback)
+      .fail(errorCallback);
+  }
 
   /**
    * @param {DataCatalogEntry} catalogEntry
    */
   createEntry(catalogEntry) {
-    let self = this;
-    return new AssistDbEntry(catalogEntry, self, self.assistDbNamespace, self.filter, self.i18n, self.navigationSettings)
-  };
+    const self = this;
+    return new AssistDbEntry(
+      catalogEntry,
+      self,
+      self.assistDbNamespace,
+      self.filter,
+      self.i18n,
+      self.navigationSettings
+    );
+  }
 
   getHierarchy() {
-    let self = this;
+    const self = this;
     return self.catalogEntry.path.concat();
-  };
+  }
 
   dblClick() {
-    let self = this;
+    const self = this;
     if (self.catalogEntry.isTableOrView()) {
-      huePubSub.publish('editor.insert.table.at.cursor', { name: self.getTableName(), database: self.getDatabaseName() });
+      huePubSub.publish('editor.insert.table.at.cursor', {
+        name: self.getTableName(),
+        database: self.getDatabaseName()
+      });
     } else if (self.catalogEntry.isColumn()) {
-      huePubSub.publish('editor.insert.column.at.cursor', { name: self.getColumnName(), table: self.getTableName(), database: self.getDatabaseName() });
+      huePubSub.publish('editor.insert.column.at.cursor', {
+        name: self.getColumnName(),
+        table: self.getTableName(),
+        database: self.getDatabaseName()
+      });
     } else {
-      huePubSub.publish('editor.insert.column.at.cursor', { name: self.getComplexName(), table: self.getTableName(), database: self.getDatabaseName() });
+      huePubSub.publish('editor.insert.column.at.cursor', {
+        name: self.getComplexName(),
+        table: self.getTableName(),
+        database: self.getDatabaseName()
+      });
     }
-  };
+  }
 
   explore(isSolr) {
-    let self = this;
+    const self = this;
     if (isSolr) {
       huePubSub.publish('open.link', '/hue/dashboard/browse/' + self.catalogEntry.name);
+    } else {
+      huePubSub.publish(
+        'open.link',
+        '/hue/dashboard/browse/' +
+          self.getDatabaseName() +
+          '.' +
+          self.getTableName() +
+          '?engine=' +
+          self.assistDbNamespace.sourceType
+      );
     }
-    else {
-      huePubSub.publish('open.link', '/hue/dashboard/browse/' + self.getDatabaseName() + '.' + self.getTableName() + '?engine=' + self.assistDbNamespace.sourceType);
-    }
-  };
+  }
 
   openInMetastore() {
-    let self = this;
+    const self = this;
     let url;
     if (self.catalogEntry.isDatabase()) {
-      url = '/metastore/tables/' + self.catalogEntry.name + '?source=' + self.catalogEntry.getSourceType() + '&namespace=' + self.catalogEntry.namespace.id;
+      url =
+        '/metastore/tables/' +
+        self.catalogEntry.name +
+        '?source=' +
+        self.catalogEntry.getSourceType() +
+        '&namespace=' +
+        self.catalogEntry.namespace.id;
     } else if (self.catalogEntry.isTableOrView()) {
-      url = '/metastore/table/' + self.parent.catalogEntry.name + '/' + self.catalogEntry.name + '?source=' + self.catalogEntry.getSourceType() + '&namespace=' + self.catalogEntry.namespace.id;
+      url =
+        '/metastore/table/' +
+        self.parent.catalogEntry.name +
+        '/' +
+        self.catalogEntry.name +
+        '?source=' +
+        self.catalogEntry.getSourceType() +
+        '&namespace=' +
+        self.catalogEntry.namespace.id;
     } else {
       return;
     }
@@ -438,11 +502,11 @@ class AssistDbEntry {
     } else {
       window.open(url, '_blank');
     }
-  };
+  }
 
   openInIndexer() {
-    let self = this;
-    let definitionName = self.catalogEntry.name;
+    const self = this;
+    const definitionName = self.catalogEntry.name;
     if (window.IS_NEW_INDEXER_ENABLED) {
       if (window.IS_HUE_4) {
         huePubSub.publish('open.link', '/indexer/indexes/' + definitionName);
@@ -450,16 +514,19 @@ class AssistDbEntry {
         window.open('/indexer/indexes/' + definitionName);
       }
     } else {
-      let hash = '#edit/' + definitionName;
+      const hash = '#edit/' + definitionName;
       if (window.IS_HUE_4) {
-        if (window.location.pathname.startsWith('/hue/indexer') && !window.location.pathname.startsWith('/hue/indexer/importer')) {
+        if (
+          window.location.pathname.startsWith('/hue/indexer') &&
+          !window.location.pathname.startsWith('/hue/indexer/importer')
+        ) {
           window.location.hash = hash;
         } else {
           huePubSub.subscribeOnce('app.gained.focus', app => {
             if (app === 'indexes') {
               window.setTimeout(() => {
                 window.location.hash = hash;
-              }, 0)
+              }, 0);
             }
           });
           huePubSub.publish('open.link', '/indexer');
@@ -468,30 +535,30 @@ class AssistDbEntry {
         window.open('/indexer/' + hash);
       }
     }
-  };
+  }
 
   toggleOpen() {
-    let self = this;
+    const self = this;
     self.open(!self.open());
-  };
+  }
 
   openItem() {
-    let self = this;
+    const self = this;
     if (self.catalogEntry.isTableOrView()) {
       huePubSub.publish('assist.table.selected', {
         sourceType: self.assistDbNamespace.sourceType,
         namespace: self.assistDbNamespace.namespace,
         database: self.databaseName,
         name: self.catalogEntry.name
-      })
+      });
     } else if (self.catalogEntry.isDatabase()) {
       huePubSub.publish('assist.database.selected', {
         sourceType: self.assistDbNamespace.sourceType,
         namespace: self.assistDbNamespace.namespace,
         name: self.catalogEntry.name
-      })
+      });
     }
-  };
+  }
 }
 
-export default AssistDbEntry;
+export default AssistDbEntry;

+ 126 - 74
desktop/core/src/desktop/js/assist/assistDbNamespace.js

@@ -14,15 +14,14 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import $ from 'jquery'
-import ko from 'knockout'
+import $ from 'jquery';
+import ko from 'knockout';
 
-import AssistDbEntry from './assistDbEntry'
-import dataCatalog from '../catalog/dataCatalog'
-import huePubSub from '../utils/huePubSub'
+import AssistDbEntry from 'assist/assistDbEntry';
+import dataCatalog from 'catalog/dataCatalog';
+import huePubSub from 'utils/huePubSub';
 
 class AssistDbNamespace {
-
   /**
    * @param {Object} options
    * @param {Object} options.i18n
@@ -33,7 +32,7 @@ class AssistDbNamespace {
    * @constructor
    */
   constructor(options) {
-    let self = this;
+    const self = this;
 
     self.i18n = options.i18n;
     self.navigationSettings = options.navigationSettings;
@@ -63,7 +62,12 @@ class AssistDbNamespace {
     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.loadingTables = ko.pureComputed(
+      () =>
+        typeof self.selectedDatabase() !== 'undefined' &&
+        self.selectedDatabase() !== null &&
+        self.selectedDatabase().loading()
+    );
 
     self.filter = {
       querySpec: ko.observable({}).extend({ rateLimit: 300 })
@@ -72,42 +76,56 @@ class AssistDbNamespace {
     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) {
+      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);
+      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();
+      const result = [];
+      const 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))
+          result.push(nonPartial + partial + db.catalogEntry.name.substring(partial.length));
         }
       });
       return result;
     };
 
     self.selectedDatabase.subscribe(() => {
-      let db = self.selectedDatabase();
+      const db = self.selectedDatabase();
       if (window.HAS_OPTIMIZER && db && !db.popularityIndexSet && !self.nonSqlType) {
         db.catalogEntry.loadNavOptPopularityForChildren({ silenceErrors: true }).done(() => {
-          let applyPopularity = () => {
+          const applyPopularity = () => {
             db.entries().forEach(entry => {
-              if (entry.catalogEntry.navOptPopularity && entry.catalogEntry.navOptPopularity.popularity >= 5) {
-                entry.popularity(entry.catalogEntry.navOptPopularity.popularity )
+              if (
+                entry.catalogEntry.navOptPopularity &&
+                entry.catalogEntry.navOptPopularity.popularity >= 5
+              ) {
+                entry.popularity(entry.catalogEntry.navOptPopularity.popularity);
               }
             });
           };
 
           if (db.loading()) {
-            let subscription = db.loading.subscribe(() => {
+            const subscription = db.loading.subscribe(() => {
               subscription.dispose();
               applyPopularity();
             });
           } else if (db.entries().length === 0) {
-            let subscription = db.entries.subscribe(newEntries => {
+            const subscription = db.entries.subscribe(newEntries => {
               if (newEntries.length > 0) {
                 subscription.dispose();
                 applyPopularity();
@@ -123,25 +141,33 @@ class AssistDbNamespace {
     self.selectedDatabaseChanged = () => {
       if (self.selectedDatabase()) {
         if (!self.selectedDatabase().hasEntries() && !self.selectedDatabase().loading()) {
-          self.selectedDatabase().loadEntries()
+          self.selectedDatabase().loadEntries();
         }
         if (!self.navigationSettings.rightAssist) {
-          window.apiHelper.setInTotalStorage('assist_' + self.sourceType + '_' + self.namespace.id, 'lastSelectedDb', self.selectedDatabase().catalogEntry.name);
+          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 = {
+    const nestedFilter = {
       querySpec: ko.observable({}).extend({ rateLimit: 300 })
     };
 
     self.setDatabase = databaseName => {
-      if (databaseName && self.selectedDatabase() && databaseName === self.selectedDatabase().catalogEntry.name) {
+      if (
+        databaseName &&
+        self.selectedDatabase() &&
+        databaseName === self.selectedDatabase().catalogEntry.name
+      ) {
         return;
       }
       if (databaseName && self.dbIndex[databaseName]) {
@@ -149,7 +175,11 @@ class AssistDbNamespace {
         self.selectedDatabaseChanged();
         return;
       }
-      let lastSelectedDb = window.apiHelper.getFromTotalStorage('assist_' + self.sourceType + '_' + self.namespace.id, 'lastSelectedDb', 'default');
+      const lastSelectedDb = window.apiHelper.getFromTotalStorage(
+        'assist_' + self.sourceType + '_' + self.namespace.id,
+        'lastSelectedDb',
+        'default'
+      );
       if (lastSelectedDb && self.dbIndex[lastSelectedDb]) {
         self.selectedDatabase(self.dbIndex[lastSelectedDb]);
         self.selectedDatabaseChanged();
@@ -166,59 +196,83 @@ class AssistDbNamespace {
       self.loading(true);
       self.hasErrors(false);
 
-      let lastSelectedDbName = self.selectedDatabase() ? self.selectedDatabase().catalogEntry.name : null;
+      const 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);
+      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;
+              const dbs = [];
+
+              databaseEntries.forEach(catalogEntry => {
+                hasNavMeta = hasNavMeta || !!catalogEntry.navigatorMeta;
+                const 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);
+              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) {
+        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 => {
+          const findAndReloadInside = entries => {
             return entries.some(entry => {
               if (entry.catalogEntry.path.join('.') === details.entry.path.join('.')) {
                 entry.catalogEntry = details.entry;
@@ -226,7 +280,7 @@ class AssistDbNamespace {
                 return true;
               }
               return findAndReloadInside(entry.entries());
-            })
+            });
           };
           findAndReloadInside(self.databases());
         }
@@ -235,26 +289,24 @@ class AssistDbNamespace {
   }
 
   whenLoaded(callback) {
-    let self = this;
+    const self = this;
     self.loadedDeferred.done(callback);
-  };
+  }
 
   highlightInside(catalogEntry) {
-    let self = this;
+    const self = this;
     let foundDb;
-    let index;
 
-    let findDatabase = () => {
-      $.each(self.databases(), (idx, db) => {
+    const findDatabase = () => {
+      self.databases().foreach(db => {
         db.highlight(false);
         if (db.databaseName === catalogEntry.path[0]) {
           foundDb = db;
-          index = idx;
         }
       });
 
       if (foundDb) {
-        let whenLoaded = () => {
+        const whenLoaded = () => {
           if (self.selectedDatabase() !== foundDb) {
             self.selectedDatabase(foundDb);
           }
@@ -290,14 +342,14 @@ class AssistDbNamespace {
     } else {
       findDatabase();
     }
-  };
+  }
 
   triggerRefresh() {
-    let self = this;
+    const self = this;
     if (self.catalogEntry) {
       self.catalogEntry.clearCache({ invalidate: self.invalidateOnRefresh() });
     }
-  };
+  }
 }
 
-export default AssistDbNamespace;
+export default AssistDbNamespace;

+ 122 - 96
desktop/core/src/desktop/js/assist/assistDbSource.js

@@ -14,16 +14,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import $ from 'jquery'
-import ko from 'knockout'
+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'
+import apiHelper from 'api/apiHelper';
+import AssistDbNamespace from 'assist/assistDbNamespace';
+import contextCatalog from 'catalog/contextCatalog';
+import huePubSub from 'utils/huePubSub';
 
 class AssistDbSource {
-
   /**
    * @param {Object} options
    * @param {Object} options.i18n
@@ -36,15 +35,19 @@ class AssistDbSource {
    * @constructor
    */
   constructor(options) {
-    let self = this;
+    const 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.initialNamespace =
+      options.initialNamespace ||
+      apiHelper.getFromTotalStorage('contextSelector', 'lastSelectedNamespace');
+    self.initialCompute =
+      options.initialCompute ||
+      apiHelper.getFromTotalStorage('contextSelector', 'lastSelectedCompute');
 
     self.selectedNamespace = ko.observable();
     self.namespaces = ko.observableArray();
@@ -58,24 +61,33 @@ class AssistDbSource {
     };
 
     self.filteredNamespaces = ko.pureComputed(() => {
-      if (!self.filter.querySpec() || typeof self.filter.querySpec().query === 'undefined' || !self.filter.querySpec().query) {
+      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);
+      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();
+      const result = [];
+      const partialLower = partial.toLowerCase();
       self.namespaces().forEach(namespace => {
         if (namespace.name.toLowerCase().indexOf(partialLower) === 0) {
-          result.push(nonPartial + partial + namespace.name.substring(partial.length))
+          result.push(nonPartial + partial + namespace.name.substring(partial.length));
         }
       });
       return result;
     };
 
-    let ensureDbSet = () => {
+    const ensureDbSet = () => {
       if (self.nonSqlType) {
         if (!self.selectedNamespace().selectedDatabase()) {
           self.selectedNamespace().selectedDatabase(self.selectedNamespace().databases()[0]);
@@ -92,7 +104,7 @@ class AssistDbSource {
       }
     });
 
-    self.hasNamespaces = ko.pureComputed(()  => self.namespaces().length > 0);
+    self.hasNamespaces = ko.pureComputed(() => self.namespaces().length > 0);
 
     huePubSub.subscribe('context.catalog.namespaces.refreshed', sourceType => {
       if (self.sourceType !== sourceType) {
@@ -100,101 +112,115 @@ class AssistDbSource {
       }
 
       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
-            }));
-          }
+      contextCatalog
+        .getNamespaces({ sourceType: self.sourceType })
+        .done(context => {
+          const newNamespaces = [];
+          const 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);
         });
-        self.namespaces(newNamespaces);
-      }).always(() => {
-        self.loading(false);
-      })
     });
   }
 
   whenLoaded(callback) {
-    let self = this;
+    const self = this;
     self.loadedDeferred.done(callback);
-  };
+  }
 
   loadNamespaces(refresh) {
-    let self = this;
+    const 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
-        });
+    return contextCatalog
+      .getNamespaces({ sourceType: self.sourceType, clearCache: refresh })
+      .done(context => {
+        const assistNamespaces = [];
+        let activeNamespace;
+        let activeCompute;
+        context.namespaces.forEach(namespace => {
+          const 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;
-              }
-            })
+          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]);
           }
         }
-        assistNamespaces.push(assistNamespace);
+      })
+      .fail(() => {
+        self.hasErrors(true);
+      })
+      .always(() => {
+        self.loadedDeferred.resolve();
+        self.loading(false);
       });
-      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;
+    const self = this;
     if (self.navigationSettings.rightAssist) {
       return;
     }
 
-    let whenLoaded = () => {
+    const whenLoaded = () => {
       self.namespaces().some(namespace => {
         if (namespace.namespace.id === catalogEntry.namespace.id) {
           if (self.selectedNamespace() !== namespace) {
@@ -205,29 +231,29 @@ class AssistDbSource {
           } else {
             self.selectedNamespace().initDatabases(() => {
               self.selectedNamespace().highlightInside(catalogEntry);
-            })
+            });
           }
           return true;
         }
-      })
+      });
     };
 
     if (self.namespaces().length) {
       whenLoaded();
     } else if (self.loading()) {
-      let loadingSub = self.loading.subscribe(() => {
+      const loadingSub = self.loading.subscribe(() => {
         loadingSub.dispose();
         whenLoaded();
-      })
+      });
     } else {
       self.loadNamespaces().done(whenLoaded);
     }
-  };
+  }
 
   triggerRefresh() {
-    let self = this;
+    const self = this;
     self.loadNamespaces(true);
-  };
+  }
 }
 
-export default AssistDbSource;
+export default AssistDbSource;

+ 37 - 31
desktop/core/src/desktop/js/assist/assistGitEntry.js

@@ -14,13 +14,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import ko from 'knockout'
+import ko from 'knockout';
 
-import huePubSub from '../utils/huePubSub'
-import apiHelper from '../api/apiHelper'
+import apiHelper from 'api/apiHelper';
+import huePubSub from 'utils/huePubSub';
 
 class AssistGitEntry {
-
   /**
    * @param {object} options
    * @param {object} options.definition
@@ -30,7 +29,7 @@ class AssistGitEntry {
    * @constructor
    */
   constructor(options) {
-    let self = this;
+    const self = this;
 
     self.definition = options.definition;
     self.parent = options.parent;
@@ -38,7 +37,7 @@ class AssistGitEntry {
     if (self.parent !== null) {
       self.path = self.parent.path;
       if (self.parent.path !== '/') {
-        self.path += '/'
+        self.path += '/';
       }
     }
     self.path += self.definition.name;
@@ -59,13 +58,13 @@ class AssistGitEntry {
       }
     });
 
-    self.hasEntries = ko.pureComputed(() =>{
+    self.hasEntries = ko.pureComputed(() => {
       return self.entries().length > 0;
     });
   }
 
   dblClick() {
-    let self = this;
+    const self = this;
     if (self.definition.type !== 'file') {
       return;
     }
@@ -82,11 +81,11 @@ class AssistGitEntry {
         self.hasErrors(true);
         self.loading(false);
       }
-    })
-  };
+    });
+  }
 
   loadEntries(callback) {
-    let self = this;
+    const self = this;
     if (self.loading()) {
       return;
     }
@@ -97,11 +96,16 @@ class AssistGitEntry {
       pathParts: self.getHierarchy(),
       fileType: self.definition.type,
       successCallback: data => {
-        let filteredFiles = data.files.filter(file => file.name !== '.' && file.name !== '..');
-        self.entries(filteredFiles.map(file => new AssistGitEntry({
-          definition: file,
-          parent: self
-        })));
+        const 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) {
@@ -115,37 +119,39 @@ class AssistGitEntry {
           callback();
         }
       }
-    })
-  };
+    });
+  }
 
   loadDeep(folders, callback) {
-    let self = this;
+    const self = this;
 
     if (folders.length === 0) {
       callback(self);
       return;
     }
 
-    let findNextAndLoadDeep = () => {
-      let nextName = folders.shift();
-      let foundEntry = self.entries().filter(entry => entry.definition.name === nextName && entry.definition.type === 'dir');
+    const findNextAndLoadDeep = () => {
+      const nextName = folders.shift();
+      const 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()) {
+      } else if (!self.hasErrors()) {
         callback(self);
       }
     };
 
-    if (! self.loaded) {
+    if (!self.loaded) {
       self.loadEntries(findNextAndLoadDeep);
     } else {
       findNextAndLoadDeep();
     }
-  };
+  }
 
   getHierarchy() {
-    let self = this;
-    let parts = [];
+    const self = this;
+    const parts = [];
     let entry = self;
     while (entry != null) {
       parts.push(entry.definition.name);
@@ -153,10 +159,10 @@ class AssistGitEntry {
     }
     parts.reverse();
     return parts;
-  };
+  }
 
   toggleOpen() {
-    let self = this;
+    const self = this;
     if (self.definition.type !== 'dir') {
       return;
     }
@@ -168,7 +174,7 @@ class AssistGitEntry {
     } else {
       huePubSub.publish('assist.selectGitEntry', self);
     }
-  };
+  }
 }
 
-export default AssistGitEntry
+export default AssistGitEntry;

+ 19 - 15
desktop/core/src/desktop/js/assist/assistHBaseEntry.js

@@ -14,13 +14,12 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import ko from 'knockout'
+import ko from 'knockout';
 
-import apiHelper from '../api/apiHelper'
-import huePubSub from '../utils/huePubSub'
+import apiHelper from 'api/apiHelper';
+import huePubSub from 'utils/huePubSub';
 
 class AssistHBaseEntry {
-
   /**
    * @param {object} options
    * @param {object} options.definition
@@ -28,7 +27,7 @@ class AssistHBaseEntry {
    * @constructor
    */
   constructor(options) {
-    let self = this;
+    const self = this;
 
     self.definition = options.definition;
     self.path = self.definition.name;
@@ -43,7 +42,7 @@ class AssistHBaseEntry {
   }
 
   loadEntries(callback) {
-    let self = this;
+    const self = this;
     if (self.loading()) {
       return;
     }
@@ -53,9 +52,14 @@ class AssistHBaseEntry {
     apiHelper.fetchHBase({
       parent: self.definition,
       successCallback: data => {
-        self.entries(data.data.map(obj => new AssistHBaseEntry({
-          definition: obj
-        })));
+        self.entries(
+          data.data.map(
+            obj =>
+              new AssistHBaseEntry({
+                definition: obj
+              })
+          )
+        );
         self.loaded = true;
         self.loading(false);
         if (callback) {
@@ -69,20 +73,20 @@ class AssistHBaseEntry {
           callback();
         }
       }
-    })
-  };
+    });
+  }
 
   open() {
     huePubSub.publish('assist.clickHBaseItem', this);
-  };
+  }
 
   click() {
     huePubSub.publish('assist.clickHBaseItem', this);
-  };
+  }
 
   dblClick() {
     huePubSub.publish('assist.dblClickHBaseItem', this);
-  };
+  }
 }
 
-export default AssistHBaseEntry
+export default AssistHBaseEntry;

+ 81 - 67
desktop/core/src/desktop/js/assist/assistStorageEntry.js

@@ -14,28 +14,28 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import $ from 'jquery'
-import ko from 'knockout'
+import $ from 'jquery';
+import ko from 'knockout';
 
-import apiHelper from '../api/apiHelper'
-import huePubSub from '../utils/huePubSub'
+import apiHelper from 'api/apiHelper';
+import huePubSub from 'utils/huePubSub';
 
 const PAGE_SIZE = 100;
 
 const TYPE_SPECIFICS = {
-  'adls': {
+  adls: {
     apiHelperFetchFunction: 'fetchAdlsPath',
     dblClickPubSubId: 'assist.dblClickAdlsItem',
     goHomePubSubId: 'assist.adls.go.home',
     selectEntryPubSubId: 'assist.selectAdlsEntry'
   },
-  'hdfs': {
+  hdfs: {
     apiHelperFetchFunction: 'fetchHdfsPath',
     dblClickPubSubId: 'assist.dblClickHdfsItem',
     goHomePubSubId: 'assist.hdfs.go.home',
     selectEntryPubSubId: 'assist.selectHdfsEntry'
   },
-  's3': {
+  s3: {
     apiHelperFetchFunction: 'fetchS3Path',
     dblClickPubSubId: 'assist.dblClickS3Item',
     goHomePubSubId: 'assist.s3.go.home',
@@ -44,7 +44,6 @@ const TYPE_SPECIFICS = {
 };
 
 class AssistStorageEntry {
-
   /**
    * @param {object} options
    * @param {object} options.definition
@@ -56,7 +55,7 @@ class AssistStorageEntry {
    * @constructor
    */
   constructor(options) {
-    let self = this;
+    const self = this;
     self.type = options.type;
     self.originalType = options.originalType;
     self.definition = options.definition;
@@ -65,7 +64,7 @@ class AssistStorageEntry {
     if (self.parent !== null) {
       self.path = self.parent.path;
       if (self.parent.path !== '/') {
-        self.path += '/'
+        self.path += '/';
       }
     }
     self.path += self.definition.name;
@@ -106,27 +105,31 @@ class AssistStorageEntry {
 
   dblClick() {
     huePubSub.publish(TYPE_SPECIFICS[self.type].dblClickPubSubId, this);
-  };
+  }
 
   loadPreview() {
-    let self = this;
+    const self = this;
     self.loading(true);
-    window.apiHelper.fetchStoragePreview({
-      path: self.getHierarchy(),
-      type: self.type,
-      silenceErrors: true
-    }).done(data => {
-      self.preview(data);
-    }).fail(errorText => {
-      self.hasErrors(true);
-      self.errorText(errorText);
-    }).always(() => {
-      self.loading(false);
-    })
-  };
+    window.apiHelper
+      .fetchStoragePreview({
+        path: self.getHierarchy(),
+        type: self.type,
+        silenceErrors: true
+      })
+      .done(data => {
+        self.preview(data);
+      })
+      .fail(errorText => {
+        self.hasErrors(true);
+        self.errorText(errorText);
+      })
+      .always(() => {
+        self.loading(false);
+      });
+  }
 
   loadEntries(callback) {
-    let self = this;
+    const self = this;
     if (self.loading()) {
       return;
     }
@@ -140,15 +143,17 @@ class AssistStorageEntry {
       pathParts: self.getHierarchy(),
       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
+        const 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) {
@@ -163,27 +168,29 @@ class AssistStorageEntry {
           callback();
         }
       }
-    })
-  };
+    });
+  }
 
   goHome() {
     huePubSub.publish(TYPE_SPECIFICS[this.type].goHomePubSubId);
-  };
+  }
 
   loadDeep(folders, callback) {
-    let self = this;
+    const self = this;
 
     if (folders.length === 0) {
       callback(self);
       return;
     }
 
-    let nextName = folders.shift();
+    const nextName = folders.shift();
     let loadedPages = 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;
+    const findNextAndLoadDeep = () => {
+      const foundEntry = self.entries().filter(entry => entry.definition.name === nextName);
+      const 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);
@@ -197,16 +204,16 @@ class AssistStorageEntry {
       }
     };
 
-    if (! self.loaded) {
+    if (!self.loaded) {
       self.loadEntries(findNextAndLoadDeep);
     } else {
       findNextAndLoadDeep();
     }
-  };
+  }
 
   getHierarchy() {
-    let self = this;
-    let parts = [];
+    const self = this;
+    const parts = [];
     let entry = self;
     while (entry != null) {
       parts.push(entry.definition.name);
@@ -214,10 +221,10 @@ class AssistStorageEntry {
     }
     parts.reverse();
     return parts;
-  };
+  }
 
   toggleOpen(data, event) {
-    let self = this;
+    const self = this;
     if (self.definition.type === 'file') {
       if (IS_HUE_4) {
         if (event.ctrlKey || event.metaKey || event.which === 2) {
@@ -238,10 +245,10 @@ class AssistStorageEntry {
     } else {
       huePubSub.publish(TYPE_SPECIFICS[self.type].selectEntryPubSubId, self);
     }
-  };
+  }
 
   fetchMore(successCallback, errorCallback) {
-    let self = this;
+    const self = this;
     if (!self.hasMorePages || self.loadingMore()) {
       return;
     }
@@ -256,12 +263,19 @@ class AssistStorageEntry {
       pathParts: self.getHierarchy(),
       successCallback: data => {
         self.hasMorePages = data.page.next_page_number > self.currentPage;
-        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
-        }))));
+        const 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();
@@ -274,11 +288,11 @@ class AssistStorageEntry {
         }
       }
     });
-  };
+  }
 
   showContextPopover(entry, event, positionAdjustment) {
-    let $source = $(event.target);
-    let offset = $source.offset();
+    const $source = $(event.target);
+    const offset = $source.offset();
     entry.contextPopoverVisible(true);
 
     if (positionAdjustment) {
@@ -305,11 +319,11 @@ class AssistStorageEntry {
     huePubSub.subscribeOnce('context.popover.hidden', () => {
       entry.contextPopoverVisible(false);
     });
-  };
+  }
 
   openInImporter() {
     huePubSub.publish('open.in.importer', this.definition.path);
-  };
+  }
 
   /**
    * Helper function to create an assistStorageEntry. It will load the entries starting from the root up until the
@@ -320,13 +334,13 @@ class AssistStorageEntry {
    * @return {Promise}
    */
   static getEntry(path, type) {
-    let deferred = $.Deferred();
-    let typeMatch = path.match(/^([^:]+):\/(\/.*)\/?/i);
-    type = typeMatch ? typeMatch[1] : (type || 'hdfs');
+    const deferred = $.Deferred();
+    const typeMatch = path.match(/^([^:]+):\/(\/.*)\/?/i);
+    type = typeMatch ? typeMatch[1] : type || 'hdfs';
     type = type.replace(/s3.*/i, 's3');
     type = type.replace(/adl.*/i, 'adls');
 
-    let rootEntry = new AssistStorageEntry({
+    const rootEntry = new AssistStorageEntry({
       type: type.toLowerCase(),
       originalType: typeMatch && typeMatch[1],
       definition: {
@@ -342,7 +356,7 @@ class AssistStorageEntry {
     rootEntry.loadDeep(path, deferred.resolve);
 
     return deferred.promise();
-  };
+  }
 }
 
-export default AssistStorageEntry
+export default AssistStorageEntry;

+ 6 - 6
desktop/core/src/desktop/js/assist/assistViewModel.js

@@ -14,15 +14,15 @@
 // 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'
+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;
+window.AssistStorageEntry = AssistStorageEntry;

+ 4 - 5
desktop/core/src/desktop/js/catalog/catalogUtils.js

@@ -14,8 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import apiHelper from '../api/apiHelper'
-import CancellablePromise from '../api/cancellablePromise'
+import apiHelper from 'api/apiHelper';
 
 /**
  * Wrapper function around ApiHelper calls, it will also save the entry on success.
@@ -26,7 +25,8 @@ import CancellablePromise from '../api/cancellablePromise'
  * @param {Object} [apiOptions]
  * @param {boolean} [apiOptions.silenceErrors]
  */
-const fetchAndSave = (apiHelperFunction, attributeName, entry, apiOptions) => apiHelper[apiHelperFunction]({
+const fetchAndSave = (apiHelperFunction, attributeName, entry, apiOptions) =>
+  apiHelper[apiHelperFunction]({
     sourceType: entry.dataCatalog.sourceType,
     compute: entry.compute,
     path: entry.path, // Set for DataCatalogEntry
@@ -70,9 +70,8 @@ const applyCancellable = (promise, options) => {
   return promise;
 };
 
-
 export default {
   applyCancellable: applyCancellable,
   fetchAndSave: fetchAndSave,
   setSilencedErrors: setSilencedErrors
-}
+};

+ 73 - 55
desktop/core/src/desktop/js/catalog/contextCatalog.js

@@ -14,11 +14,11 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import $ from 'jquery'
-import localforage from 'localforage'
+import $ from 'jquery';
+import localforage from 'localforage';
 
-import apiHelper from '../api/apiHelper'
-import huePubSub from '../utils/huePubSub'
+import apiHelper from 'api/apiHelper';
+import huePubSub from 'utils/huePubSub';
 
 /**
  * @typedef {Object} ContextCompute
@@ -39,9 +39,8 @@ const NAMESPACES_CONTEXT_TYPE = 'namespaces';
 const DISABLE_CACHE = true;
 
 class ContextCatalog {
-
   constructor() {
-    let self = this;
+    const self = this;
     self.namespaces = {};
     self.namespacePromises = {};
 
@@ -51,10 +50,10 @@ class ContextCatalog {
     self.clusters = {};
     self.clusterPromises = {};
 
-    let addPubSubs = () => {
+    const addPubSubs = () => {
       if (typeof huePubSub !== 'undefined') {
         huePubSub.subscribe('context.catalog.refresh', () => {
-          let namespacesToRefresh = Object.keys(self.namespaces);
+          const namespacesToRefresh = Object.keys(self.namespaces);
           self.namespaces = {};
           self.namespacePromises = {};
 
@@ -66,8 +65,8 @@ class ContextCatalog {
           huePubSub.publish('context.catalog.refreshed');
           namespacesToRefresh.forEach(sourceType => {
             huePubSub.publish('context.catalog.namespaces.refreshed', sourceType);
-          })
-        })
+          });
+        });
       } else {
         window.setTimeout(addPubSubs, 100);
       }
@@ -83,36 +82,43 @@ class ContextCatalog {
       });
     }
     return self.store;
-  };
+  }
 
   saveLater(contextType, sourceType, entry) {
-    let self = this;
+    const self = this;
     window.setTimeout(() => {
-      self.getStore().setItem(sourceType + '_' + contextType, { version: CONTEXT_CATALOG_VERSION, entry: entry });
+      self.getStore().setItem(sourceType + '_' + contextType, {
+        version: CONTEXT_CATALOG_VERSION,
+        entry: entry
+      });
     }, 1000);
-  };
+  }
 
   getSaved(contextType, sourceType) {
-    let self = this;
-    let deferred = $.Deferred();
+    const self = this;
+    const deferred = $.Deferred();
 
     if (DISABLE_CACHE) {
       return deferred.reject().promise();
     }
 
-    self.getStore().getItem(sourceType + '_' + contextType).then(saved => {
-      if (saved && saved.version === CONTEXT_CATALOG_VERSION) {
-        deferred.resolve(saved.entry);
-      } else {
+    self
+      .getStore()
+      .getItem(sourceType + '_' + contextType)
+      .then(saved => {
+        if (saved && saved.version === CONTEXT_CATALOG_VERSION) {
+          deferred.resolve(saved.entry);
+        } else {
+          deferred.reject();
+        }
+      })
+      .catch(error => {
+        console.warn(error);
         deferred.reject();
-      }
-    }).catch(error => {
-      console.warn(error);
-      deferred.reject();
-    });
+      });
 
     return deferred.promise();
-  };
+  }
 
   /**
    * @param {Object} options
@@ -122,9 +128,9 @@ class ContextCatalog {
    * @return {Promise}
    */
   getNamespaces(options) {
-    let self = this;
+    const self = this;
 
-    let notifyForRefresh = self.namespacePromises[options.sourceType] && options.clearCache;
+    const notifyForRefresh = self.namespacePromises[options.sourceType] && options.clearCache;
     if (options.clearCache) {
       self.namespacePromises[options.sourceType] = undefined;
       self.namespaces[options.sourceType] = undefined;
@@ -135,24 +141,26 @@ class ContextCatalog {
     }
 
     if (self.namespaces[options.sourceType]) {
-      self.namespacePromises[options.sourceType] = $.Deferred().resolve(self.namespaces[options.sourceType]).promise();
+      self.namespacePromises[options.sourceType] = $.Deferred()
+        .resolve(self.namespaces[options.sourceType])
+        .promise();
       return self.namespacePromises[options.sourceType];
     }
 
-    let deferred = $.Deferred();
+    const deferred = $.Deferred();
 
     self.namespacePromises[options.sourceType] = deferred.promise();
 
-    let startingNamespaces = {};
-    let pollTimeout = -1;
+    const startingNamespaces = {};
+    const pollTimeout = -1;
 
-    let pollForStarted = () => {
+    const pollForStarted = () => {
       window.clearTimeout(pollTimeout);
       window.setTimeout(() => {
         if (Object.keys(startingNamespaces).length) {
           apiHelper.fetchContextNamespaces(options).done(namespaces => {
             if (namespaces[options.sourceType]) {
-              let namespaces = namespaces[options.sourceType];
+              const namespaces = namespaces[options.sourceType];
               if (namespaces) {
                 let statusChanged = false;
                 namespaces.forEach(namespace => {
@@ -186,7 +194,7 @@ class ContextCatalog {
       }
     });
 
-    let fetchNamespaces = () => {
+    const fetchNamespaces = () => {
       apiHelper.fetchContextNamespaces(options).done(namespaces => {
         if (namespaces[options.sourceType]) {
           const dynamic = namespaces.dynamicClusters;
@@ -200,7 +208,7 @@ class ContextCatalog {
                 if (!compute.name && compute.clusterName) {
                   compute.name = compute.clusterName;
                 }
-              })
+              });
             });
             self.namespaces[options.sourceType] = {
               namespaces: namespaces.filter(namespace => namespace.name),
@@ -213,7 +221,11 @@ class ContextCatalog {
             }
 
             if (self.namespaces[options.sourceType].namespaces.length) {
-              self.saveLater(NAMESPACES_CONTEXT_TYPE, options.sourceType, self.namespaces[options.sourceType]);
+              self.saveLater(
+                NAMESPACES_CONTEXT_TYPE,
+                options.sourceType,
+                self.namespaces[options.sourceType]
+              );
             } else {
               self.getStore().removeItem(options.sourceType + '_' + NAMESPACES_CONTEXT_TYPE);
             }
@@ -227,16 +239,19 @@ class ContextCatalog {
     };
 
     if (!options.clearCache) {
-      self.getSaved(NAMESPACES_CONTEXT_TYPE, options.sourceType).done(namespaces => {
-        self.namespaces[options.sourceType] = namespaces;
-        deferred.resolve(self.namespaces[options.sourceType]);
-      }).fail(fetchNamespaces);
+      self
+        .getSaved(NAMESPACES_CONTEXT_TYPE, options.sourceType)
+        .done(namespaces => {
+          self.namespaces[options.sourceType] = namespaces;
+          deferred.resolve(self.namespaces[options.sourceType]);
+        })
+        .fail(fetchNamespaces);
     } else {
       fetchNamespaces();
     }
 
     return self.namespacePromises[options.sourceType];
-  };
+  }
 
   /**
    * @param {Object} options
@@ -246,7 +261,7 @@ class ContextCatalog {
    * @return {Promise}
    */
   getComputes(options) {
-    let self = this;
+    const self = this;
 
     if (options.clearCache) {
       self.computePromises[options.sourceType] = undefined;
@@ -258,11 +273,13 @@ class ContextCatalog {
     }
 
     if (self.computes[options.sourceType]) {
-      self.computePromises[options.sourceType] = $.Deferred().resolve(self.computes[options.sourceType]).promise();
+      self.computePromises[options.sourceType] = $.Deferred()
+        .resolve(self.computes[options.sourceType])
+        .promise();
       return self.computePromises[options.sourceType];
     }
 
-    let deferred = $.Deferred();
+    const deferred = $.Deferred();
     self.computePromises[options.sourceType] = deferred.promise();
 
     apiHelper.fetchContextComputes(options).done(computes => {
@@ -270,7 +287,7 @@ class ContextCatalog {
         computes = computes[options.sourceType];
         if (computes) {
           self.computes[options.sourceType] = computes;
-          deferred.resolve(self.computes[options.sourceType])
+          deferred.resolve(self.computes[options.sourceType]);
           // TODO: save
         } else {
           deferred.reject();
@@ -281,7 +298,7 @@ class ContextCatalog {
     });
 
     return self.computePromises[options.sourceType];
-  };
+  }
 
   /**
    * @param {Object} options
@@ -290,37 +307,38 @@ class ContextCatalog {
    * @return {Promise}
    */
   getClusters(options) {
-    let self = this;
+    const self = this;
 
     if (self.clusterPromises[options.sourceType]) {
       return self.clusterPromises[options.sourceType];
     }
 
     if (self.clusters[options.sourceType]) {
-      self.clusterPromises[options.sourceType] = $.Deferred().resolve(self.clusters[options.sourceType]).promise();
+      self.clusterPromises[options.sourceType] = $.Deferred()
+        .resolve(self.clusters[options.sourceType])
+        .promise();
       return self.clusterPromises[options.sourceType];
     }
 
-    let deferred = $.Deferred();
+    const deferred = $.Deferred();
     self.clusterPromises[options.sourceType] = deferred.promise();
 
     apiHelper.fetchContextClusters(options).done(clusters => {
       if (clusters && clusters[options.sourceType]) {
         self.clusters[options.sourceType] = clusters[options.sourceType];
-        deferred.resolve(self.clusters[options.sourceType])
+        deferred.resolve(self.clusters[options.sourceType]);
       } else {
         deferred.reject();
       }
     });
 
     return self.clusterPromises[options.sourceType];
-  };
+  }
 }
 
-let contextCatalog = new ContextCatalog();
+const contextCatalog = new ContextCatalog();
 
 export default {
-
   /**
    * @param {Object} options
    * @param {string} options.sourceType
@@ -345,4 +363,4 @@ export default {
    * @return {Promise}
    */
   getClusters: options => contextCatalog.getClusters(options)
-}
+};

+ 385 - 255
desktop/core/src/desktop/js/catalog/dataCatalog.js

@@ -14,18 +14,19 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import $ from 'jquery'
-import localforage from 'localforage'
+import $ from 'jquery';
+import localforage from 'localforage';
 
-import apiHelper from '../api/apiHelper'
-import CancellablePromise from '../api/cancellablePromise'
-import catalogUtils from './catalogUtils'
-import DataCatalogEntry from './dataCatalogEntry'
-import GeneralDataCatalog from './generalDataCatalog'
-import MultiTableEntry from './multiTableEntry'
+import apiHelper from 'api/apiHelper';
+import CancellablePromise from 'api/cancellablePromise';
+import catalogUtils from 'catalog/catalogUtils';
+import DataCatalogEntry from 'catalog/dataCatalogEntry';
+import GeneralDataCatalog from 'catalog/generalDataCatalog';
+import MultiTableEntry from 'catalog/multiTableEntry';
 
 const STORAGE_POSTFIX = LOGGED_USERNAME;
 const DATA_CATALOG_VERSION = 5;
+
 let cacheEnabled = true;
 
 /**
@@ -38,20 +39,20 @@ let cacheEnabled = true;
  * @param {string[][]} [options.paths]
  * @return {string}
  */
-const generateEntryCacheId = function (options) {
+const generateEntryCacheId = function(options) {
   let id = options.namespace.id;
   if (options.path) {
     if (typeof options.path === 'string') {
       id += '_' + options.path;
     } else if (options.path.length) {
-      id +='_' + options.path.join('.');
+      id += '_' + options.path.join('.');
     }
   } else if (options.paths && options.paths.length) {
-    let pathSet = {};
-    options.paths.forEach(function (path) {
+    const pathSet = {};
+    options.paths.forEach(path => {
       pathSet[path.join('.')] = true;
     });
-    let uniquePaths = Object.keys(pathSet);
+    const uniquePaths = Object.keys(pathSet);
     uniquePaths.sort();
     id += '_' + uniquePaths.join(',');
   }
@@ -64,12 +65,19 @@ const generateEntryCacheId = function (options) {
  * @param {DataCatalogEntry} dataCatalogEntry - The entry to fill
  * @param {Object} storeEntry - The cached version
  */
-const mergeEntry = function (dataCatalogEntry, storeEntry) {
-  let mergeAttribute = function (attributeName, ttl, promiseName) {
-    if (storeEntry.version === DATA_CATALOG_VERSION && storeEntry[attributeName] && (!storeEntry[attributeName].hueTimestamp || (Date.now() - storeEntry[attributeName].hueTimestamp) < ttl)) {
+const mergeEntry = function(dataCatalogEntry, storeEntry) {
+  const mergeAttribute = function(attributeName, ttl, promiseName) {
+    if (
+      storeEntry.version === DATA_CATALOG_VERSION &&
+      storeEntry[attributeName] &&
+      (!storeEntry[attributeName].hueTimestamp ||
+        Date.now() - storeEntry[attributeName].hueTimestamp < ttl)
+    ) {
       dataCatalogEntry[attributeName] = storeEntry[attributeName];
       if (promiseName) {
-        dataCatalogEntry[promiseName] = $.Deferred().resolve(dataCatalogEntry[attributeName]).promise();
+        dataCatalogEntry[promiseName] = $.Deferred()
+          .resolve(dataCatalogEntry[attributeName])
+          .promise();
       }
     }
   };
@@ -84,19 +92,25 @@ const mergeEntry = function (dataCatalogEntry, storeEntry) {
   mergeAttribute('navOptPopularity', CACHEABLE_TTL.optimizer);
 };
 
-
 /**
  * Helper function to fill a multi table catalog entry with cached metadata.
  *
  * @param {MultiTableEntry} multiTableCatalogEntry - The entry to fill
  * @param {Object} storeEntry - The cached version
  */
-const mergeMultiTableEntry = function (multiTableCatalogEntry, storeEntry) {
-  let mergeAttribute = function (attributeName, ttl, promiseName) {
-    if (storeEntry.version === DATA_CATALOG_VERSION && storeEntry[attributeName] && (!storeEntry[attributeName].hueTimestamp || (Date.now() - storeEntry[attributeName].hueTimestamp) < ttl)) {
+const mergeMultiTableEntry = function(multiTableCatalogEntry, storeEntry) {
+  const mergeAttribute = function(attributeName, ttl, promiseName) {
+    if (
+      storeEntry.version === DATA_CATALOG_VERSION &&
+      storeEntry[attributeName] &&
+      (!storeEntry[attributeName].hueTimestamp ||
+        Date.now() - storeEntry[attributeName].hueTimestamp < ttl)
+    ) {
       multiTableCatalogEntry[attributeName] = storeEntry[attributeName];
       if (promiseName) {
-        multiTableCatalogEntry[promiseName] = $.Deferred().resolve(multiTableCatalogEntry[attributeName]).promise();
+        multiTableCatalogEntry[promiseName] = $.Deferred()
+          .resolve(multiTableCatalogEntry[attributeName])
+          .promise();
       }
     }
   };
@@ -108,14 +122,13 @@ const mergeMultiTableEntry = function (multiTableCatalogEntry, storeEntry) {
 };
 
 class DataCatalog {
-
   /**
    * @param {string} sourceType
    *
    * @constructor
    */
   constructor(sourceType) {
-    let self = this;
+    const self = this;
     self.sourceType = sourceType;
     self.entries = {};
     self.temporaryEntries = {};
@@ -133,14 +146,14 @@ class DataCatalog {
    */
   static disableCache() {
     cacheEnabled = false;
-  };
+  }
 
   /**
    * Enables the cache for subsequent operations, mainly used for test purposes
    */
   static enableCache() {
     cacheEnabled = true;
-  };
+  }
 
   /**
    * Returns true if the catalog can have NavOpt metadata
@@ -148,9 +161,9 @@ class DataCatalog {
    * @return {boolean}
    */
   canHaveNavOptMetadata() {
-    let self = this;
+    const self = this;
     return HAS_OPTIMIZER && (self.sourceType === 'hive' || self.sourceType === 'impala');
-  };
+  }
 
   /**
    * Clears the data catalog and cache for the given path and any children thereof.
@@ -160,40 +173,49 @@ class DataCatalog {
    * @param {string[]} rootPath - The path to clear
    */
   clearStorageCascade(namespace, compute, rootPath) {
-    let self = this;
-    let deferred = $.Deferred();
+    const self = this;
+    const deferred = $.Deferred();
     if (!namespace || !compute) {
       if (rootPath.length === 0) {
         self.entries = {};
-        self.store.clear().then(deferred.resolve).catch(deferred.reject);
+        self.store
+          .clear()
+          .then(deferred.resolve)
+          .catch(deferred.reject);
         return deferred.promise();
       }
       return deferred.reject().promise();
     }
 
-    let keyPrefix = generateEntryCacheId({ namespace: namespace, path: rootPath });
-    Object.keys(self.entries).forEach(function (key) {
+    const keyPrefix = generateEntryCacheId({ namespace: namespace, path: rootPath });
+    Object.keys(self.entries).forEach(key => {
       if (key.indexOf(keyPrefix) === 0) {
         delete self.entries[key];
       }
     });
 
-    let deletePromises = [];
-    let keysDeferred = $.Deferred();
+    const deletePromises = [];
+    const keysDeferred = $.Deferred();
     deletePromises.push(keysDeferred.promise());
-    self.store.keys().then(function (keys) {
-      keys.forEach(function (key) {
-        if (key.indexOf(keyPrefix) === 0) {
-          let deleteDeferred = $.Deferred();
-          deletePromises.push(deleteDeferred.promise());
-          self.store.removeItem(key).then(deleteDeferred.resolve).catch(deleteDeferred.reject);
-        }
-      });
-      keysDeferred.resolve();
-    }).catch(keysDeferred.reject);
+    self.store
+      .keys()
+      .then(keys => {
+        keys.forEach(key => {
+          if (key.indexOf(keyPrefix) === 0) {
+            const deleteDeferred = $.Deferred();
+            deletePromises.push(deleteDeferred.promise());
+            self.store
+              .removeItem(key)
+              .then(deleteDeferred.resolve)
+              .catch(deleteDeferred.reject);
+          }
+        });
+        keysDeferred.resolve();
+      })
+      .catch(keysDeferred.reject);
 
     return $.when.apply($, deletePromises);
-  };
+  }
 
   /**
    * Updates the cache for the given entry
@@ -202,28 +224,33 @@ class DataCatalog {
    * @return {Promise}
    */
   persistCatalogEntry(dataCatalogEntry) {
-    let self = this;
+    const self = this;
     if (!cacheEnabled || CACHEABLE_TTL.default <= 0) {
-      return $.Deferred().resolve().promise();
+      return $.Deferred()
+        .resolve()
+        .promise();
     }
-    let deferred = $.Deferred();
-
-    let identifier = generateEntryCacheId(dataCatalogEntry);
-
-    self.store.setItem(identifier, {
-      version: DATA_CATALOG_VERSION,
-      definition: dataCatalogEntry.definition,
-      sourceMeta: dataCatalogEntry.sourceMeta,
-      analysis: dataCatalogEntry.analysis,
-      partitions: dataCatalogEntry.partitions,
-      sample: dataCatalogEntry.sample,
-      navigatorMeta: dataCatalogEntry.navigatorMeta,
-      navOptMeta:  dataCatalogEntry.navOptMeta,
-      navOptPopularity: dataCatalogEntry.navOptPopularity,
-    }).then(deferred.resolve).catch(deferred.reject);
+    const deferred = $.Deferred();
+
+    const identifier = generateEntryCacheId(dataCatalogEntry);
+
+    self.store
+      .setItem(identifier, {
+        version: DATA_CATALOG_VERSION,
+        definition: dataCatalogEntry.definition,
+        sourceMeta: dataCatalogEntry.sourceMeta,
+        analysis: dataCatalogEntry.analysis,
+        partitions: dataCatalogEntry.partitions,
+        sample: dataCatalogEntry.sample,
+        navigatorMeta: dataCatalogEntry.navigatorMeta,
+        navOptMeta: dataCatalogEntry.navOptMeta,
+        navOptPopularity: dataCatalogEntry.navOptPopularity
+      })
+      .then(deferred.resolve)
+      .catch(deferred.reject);
 
     return deferred.promise();
-  };
+  }
 
   /**
    * Loads Navigator Optimizer popularity for multiple tables in one go.
@@ -238,102 +265,134 @@ class DataCatalog {
    * @return {CancellablePromise}
    */
   loadNavOptPopularityForTables(options) {
-    let self = this;
-    let deferred = $.Deferred();
-    let cancellablePromises = [];
+    const self = this;
+    const deferred = $.Deferred();
+    const cancellablePromises = [];
     let popularEntries = [];
-    let pathsToLoad = [];
+    const pathsToLoad = [];
 
     options = catalogUtils.setSilencedErrors(options);
 
-    let existingPromises = [];
-    options.paths.forEach(function (path) {
-      let existingDeferred = $.Deferred();
-      self.getEntry({ namespace: options.namespace, compute: options.compute, path: path }).done(function (tableEntry) {
-        if (tableEntry.navOptPopularityForChildrenPromise) {
-          tableEntry.navOptPopularityForChildrenPromise.done(function (existingPopularEntries) {
-            popularEntries = popularEntries.concat(existingPopularEntries);
-            existingDeferred.resolve();
-          }).fail(existingDeferred.reject);
-        } else if (tableEntry.definition && tableEntry.definition.navOptLoaded) {
-          cancellablePromises.push(tableEntry.getChildren(options).done(function (childEntries) {
-            childEntries.forEach(function (childEntry) {
-              if (childEntry.navOptPopularity) {
-                popularEntries.push(childEntry);
-              }
-            });
+    const existingPromises = [];
+    options.paths.forEach(path => {
+      const existingDeferred = $.Deferred();
+      self
+        .getEntry({ namespace: options.namespace, compute: options.compute, path: path })
+        .done(tableEntry => {
+          if (tableEntry.navOptPopularityForChildrenPromise) {
+            tableEntry.navOptPopularityForChildrenPromise
+              .done(existingPopularEntries => {
+                popularEntries = popularEntries.concat(existingPopularEntries);
+                existingDeferred.resolve();
+              })
+              .fail(existingDeferred.reject);
+          } else if (tableEntry.definition && tableEntry.definition.navOptLoaded) {
+            cancellablePromises.push(
+              tableEntry
+                .getChildren(options)
+                .done(childEntries => {
+                  childEntries.forEach(childEntry => {
+                    if (childEntry.navOptPopularity) {
+                      popularEntries.push(childEntry);
+                    }
+                  });
+                  existingDeferred.resolve();
+                })
+                .fail(existingDeferred.reject)
+            );
+          } else {
+            pathsToLoad.push(path);
             existingDeferred.resolve();
-          }).fail(existingDeferred.reject));
-        } else {
-          pathsToLoad.push(path);
-          existingDeferred.resolve();
-        }
-      }).fail(existingDeferred.reject);
+          }
+        })
+        .fail(existingDeferred.reject);
       existingPromises.push(existingDeferred.promise());
     });
 
-    $.when.apply($, existingPromises).always(function () {
-      let loadDeferred = $.Deferred();
+    $.when.apply($, existingPromises).always(() => {
+      const loadDeferred = $.Deferred();
       if (pathsToLoad.length) {
-        cancellablePromises.push(apiHelper.fetchNavOptPopularity({
-          silenceErrors: options.silenceErrors,
-          paths: pathsToLoad
-        }).done(function (data) {
-          let perTable = {};
-
-          let splitNavOptValuesPerTable = function (listName) {
-            if (data.values[listName]) {
-              data.values[listName].forEach(function (column) {
-                let tableMeta = perTable[column.dbName + '.' + column.tableName];
-                if (!tableMeta) {
-                  tableMeta = { values: [] };
-                  perTable[column.dbName + '.' + column.tableName] = tableMeta;
+        cancellablePromises.push(
+          apiHelper
+            .fetchNavOptPopularity({
+              silenceErrors: options.silenceErrors,
+              paths: pathsToLoad
+            })
+            .done(data => {
+              const perTable = {};
+
+              const splitNavOptValuesPerTable = function(listName) {
+                if (data.values[listName]) {
+                  data.values[listName].forEach(column => {
+                    let tableMeta = perTable[column.dbName + '.' + column.tableName];
+                    if (!tableMeta) {
+                      tableMeta = { values: [] };
+                      perTable[column.dbName + '.' + column.tableName] = tableMeta;
+                    }
+                    if (!tableMeta.values[listName]) {
+                      tableMeta.values[listName] = [];
+                    }
+                    tableMeta.values[listName].push(column);
+                  });
                 }
-                if (!tableMeta.values[listName]) {
-                  tableMeta.values[listName] = [];
-                }
-                tableMeta.values[listName].push(column);
-              });
-            }
-          };
-
-          if (data.values) {
-            splitNavOptValuesPerTable('filterColumns');
-            splitNavOptValuesPerTable('groupbyColumns');
-            splitNavOptValuesPerTable('joinColumns');
-            splitNavOptValuesPerTable('orderbyColumns');
-            splitNavOptValuesPerTable('selectColumns');
-          }
+              };
 
-          let tablePromises = [];
-
-          Object.keys(perTable).forEach(function (path) {
-            let tableDeferred = $.Deferred();
-            self.getEntry({ namespace: options.namespace, compute: options.compute, path: path }).done(function (entry) {
-              cancellablePromises.push(entry.trackedPromise('navOptPopularityForChildrenPromise', entry.applyNavOptResponseToChildren(perTable[path], options).done(function (entries) {
-                popularEntries = popularEntries.concat(entries);
-                tableDeferred.resolve();
-              }).fail(tableDeferred.resolve)));
-            }).fail(tableDeferred.reject);
-            tablePromises.push(tableDeferred.promise());
-          });
+              if (data.values) {
+                splitNavOptValuesPerTable('filterColumns');
+                splitNavOptValuesPerTable('groupbyColumns');
+                splitNavOptValuesPerTable('joinColumns');
+                splitNavOptValuesPerTable('orderbyColumns');
+                splitNavOptValuesPerTable('selectColumns');
+              }
 
-          $.when.apply($, tablePromises).always(function () {
-            loadDeferred.resolve();
-          });
-        }).fail(loadDeferred.reject));
+              const tablePromises = [];
+
+              Object.keys(perTable).forEach(path => {
+                const tableDeferred = $.Deferred();
+                self
+                  .getEntry({ namespace: options.namespace, compute: options.compute, path: path })
+                  .done(entry => {
+                    cancellablePromises.push(
+                      entry.trackedPromise(
+                        'navOptPopularityForChildrenPromise',
+                        entry
+                          .applyNavOptResponseToChildren(perTable[path], options)
+                          .done(entries => {
+                            popularEntries = popularEntries.concat(entries);
+                            tableDeferred.resolve();
+                          })
+                          .fail(tableDeferred.resolve)
+                      )
+                    );
+                  })
+                  .fail(tableDeferred.reject);
+                tablePromises.push(tableDeferred.promise());
+              });
+
+              $.when.apply($, tablePromises).always(() => {
+                loadDeferred.resolve();
+              });
+            })
+            .fail(loadDeferred.reject)
+        );
       } else {
         loadDeferred.resolve();
       }
-      loadDeferred.always(function () {
-        $.when.apply($, cancellablePromises).done(function () {
-          deferred.resolve(popularEntries);
-        }).fail(deferred.reject);
+      loadDeferred.always(() => {
+        $.when
+          .apply($, cancellablePromises)
+          .done(() => {
+            deferred.resolve(popularEntries);
+          })
+          .fail(deferred.reject);
       });
     });
 
-    return catalogUtils.applyCancellable(new CancellablePromise(deferred, cancellablePromises), options);
-  };
+    return catalogUtils.applyCancellable(
+      new CancellablePromise(deferred, cancellablePromises),
+      options
+    );
+  }
 
   /**
    * @param {Object} options
@@ -343,9 +402,9 @@ class DataCatalog {
    * @return {DataCatalogEntry}
    */
   getKnownEntry(options) {
-    let self = this;
+    const self = this;
     return self.entries[generateEntryCacheId(options)];
-  };
+  }
 
   /**
    * Adds a temporary table to the data catalog. This would allow autocomplete etc. of tables that haven't
@@ -366,32 +425,38 @@ class DataCatalog {
    * @return {Object}
    */
   addTemporaryTable(options) {
-    let self = this;
-    let tableDeferred = $.Deferred();
-    let path = ['default', options.name];
+    const self = this;
+    const tableDeferred = $.Deferred();
+    const path = ['default', options.name];
 
-    let identifiersToClean = [];
+    const identifiersToClean = [];
 
-    let addEntryMeta = function (entry, sourceMeta) {
+    const addEntryMeta = function(entry, sourceMeta) {
       entry.sourceMeta = sourceMeta || entry.definition;
-      entry.sourceMetaPromise = $.Deferred().resolve(entry.sourceMeta).promise();
+      entry.sourceMetaPromise = $.Deferred()
+        .resolve(entry.sourceMeta)
+        .promise();
       entry.navigatorMeta = { comment: '' };
-      entry.navigatorMetaPromise = $.Deferred().resolve(entry.navigatorMeta).promise();
+      entry.navigatorMetaPromise = $.Deferred()
+        .resolve(entry.navigatorMeta)
+        .promise();
       entry.analysis = { is_view: false };
-      entry.analysisPromise = $.Deferred().resolve(entry.analysis).promise();
+      entry.analysisPromise = $.Deferred()
+        .resolve(entry.analysis)
+        .promise();
     };
 
-    let removeTable = function () {}; // noop until actually added
+    let removeTable = function() {}; // noop until actually added
 
-    let sourceIdentifier = generateEntryCacheId({
+    const sourceIdentifier = generateEntryCacheId({
       namespace: options.namespace,
       path: []
     });
 
     if (!self.temporaryEntries[sourceIdentifier]) {
-      let sourceDeferred = $.Deferred();
+      const sourceDeferred = $.Deferred();
       self.temporaryEntries[sourceIdentifier] = sourceDeferred.promise();
-      let sourceEntry = new DataCatalogEntry({
+      const sourceEntry = new DataCatalogEntry({
         isTemporary: true,
         dataCatalog: self,
         namespace: options.namespace,
@@ -405,21 +470,23 @@ class DataCatalog {
       });
       addEntryMeta(sourceEntry);
       identifiersToClean.push(sourceIdentifier);
-      sourceEntry.childrenPromise = $.Deferred().resolve([]).promise();
+      sourceEntry.childrenPromise = $.Deferred()
+        .resolve([])
+        .promise();
       sourceDeferred.resolve(sourceEntry);
     }
 
-    self.temporaryEntries[sourceIdentifier].done(function (sourceEntry) {
-      sourceEntry.getChildren().done(function (existingTemporaryDatabases) {
-        let databaseIdentifier = generateEntryCacheId({
+    self.temporaryEntries[sourceIdentifier].done(sourceEntry => {
+      sourceEntry.getChildren().done(existingTemporaryDatabases => {
+        const databaseIdentifier = generateEntryCacheId({
           namespace: options.namespace,
           path: ['default']
         });
 
         if (!self.temporaryEntries[databaseIdentifier]) {
-          let databaseDeferred = $.Deferred();
+          const databaseDeferred = $.Deferred();
           self.temporaryEntries[databaseIdentifier] = databaseDeferred.promise();
-          let databaseEntry = new DataCatalogEntry({
+          const databaseEntry = new DataCatalogEntry({
             isTemporary: true,
             dataCatalog: self,
             namespace: options.namespace,
@@ -433,21 +500,23 @@ class DataCatalog {
           });
           addEntryMeta(databaseEntry);
           identifiersToClean.push(databaseIdentifier);
-          databaseEntry.childrenPromise = $.Deferred().resolve([]).promise();
+          databaseEntry.childrenPromise = $.Deferred()
+            .resolve([])
+            .promise();
           databaseDeferred.resolve(databaseEntry);
           existingTemporaryDatabases.push(databaseEntry);
         }
 
-        self.temporaryEntries[databaseIdentifier].done(function (databaseEntry) {
-          databaseEntry.getChildren().done(function (existingTemporaryTables) {
-            let tableIdentifier = generateEntryCacheId({
+        self.temporaryEntries[databaseIdentifier].done(databaseEntry => {
+          databaseEntry.getChildren().done(existingTemporaryTables => {
+            const tableIdentifier = generateEntryCacheId({
               namespace: options.namespace,
               path: path
             });
             self.temporaryEntries[tableIdentifier] = tableDeferred.promise();
             identifiersToClean.push(tableIdentifier);
 
-            let tableEntry = new DataCatalogEntry({
+            const tableEntry = new DataCatalogEntry({
               isTemporary: true,
               dataCatalog: self,
               namespace: options.namespace,
@@ -462,14 +531,16 @@ class DataCatalog {
               }
             });
             existingTemporaryTables.push(tableEntry);
-            let indexToDelete = existingTemporaryTables.length - 1;
-            removeTable = function () { existingTemporaryTables.splice(indexToDelete, 1); };
+            const indexToDelete = existingTemporaryTables.length - 1;
+            removeTable = function() {
+              existingTemporaryTables.splice(indexToDelete, 1);
+            };
 
-            let childrenDeferred = $.Deferred();
+            const childrenDeferred = $.Deferred();
             tableEntry.childrenPromise = childrenDeferred.promise();
 
             if (options.columns) {
-              let childEntries = [];
+              const childEntries = [];
 
               addEntryMeta(tableEntry, {
                 columns: [],
@@ -483,21 +554,23 @@ class DataCatalog {
                 data: options.sample || [],
                 meta: tableEntry.sourceMeta.extended_columns
               };
-              tableEntry.samplePromise = $.Deferred().resolve(tableEntry.sample).promise();
+              tableEntry.samplePromise = $.Deferred()
+                .resolve(tableEntry.sample)
+                .promise();
 
               let index = 0;
-              options.columns.forEach(function (column) {
-                let columnPath = path.concat(column.name);
-                let columnIdentifier = generateEntryCacheId({
+              options.columns.forEach(column => {
+                const columnPath = path.concat(column.name);
+                const columnIdentifier = generateEntryCacheId({
                   namespace: options.namespace,
                   path: columnPath
                 });
 
-                let columnDeferred = $.Deferred();
+                const columnDeferred = $.Deferred();
                 self.temporaryEntries[columnIdentifier] = columnDeferred.promise();
                 identifiersToClean.push(columnIdentifier);
 
-                let columnEntry = new DataCatalogEntry({
+                const columnEntry = new DataCatalogEntry({
                   isTemporary: true,
                   dataCatalog: self,
                   namespace: options.namespace,
@@ -517,11 +590,13 @@ class DataCatalog {
                   meta: column
                 };
                 if (options.sample) {
-                  options.sample.forEach(function (sampleRow) {
+                  options.sample.forEach(sampleRow => {
                     columnEntry.sample.data.push([sampleRow[index - 1]]);
-                  })
+                  });
                 }
-                columnEntry.samplePromise = $.Deferred().resolve(columnEntry.sample).promise();
+                columnEntry.samplePromise = $.Deferred()
+                  .resolve(columnEntry.sample)
+                  .promise();
 
                 tableEntry.sourceMeta.columns.push(column.name);
                 tableEntry.sourceMeta.extended_columns.push(columnEntry.definition);
@@ -534,7 +609,7 @@ class DataCatalog {
                   type: column.type
                 });
 
-                childEntries.push(columnEntry)
+                childEntries.push(columnEntry);
               });
               childrenDeferred.resolve(childEntries);
             } else {
@@ -544,20 +619,18 @@ class DataCatalog {
             tableDeferred.resolve(tableEntry);
           });
         });
-      })
-
-
+      });
     });
 
     return {
-      delete: function () {
+      delete: function() {
         removeTable();
         while (identifiersToClean.length) {
           delete self.entries[identifiersToClean.pop()];
         }
       }
-    }
-  };
+    };
+  }
 
   /**
    * @param {Object} options
@@ -570,42 +643,72 @@ class DataCatalog {
    * @return {Promise}
    */
   getEntry(options) {
-    let self = this;
-    let identifier = generateEntryCacheId(options);
+    const self = this;
+    const identifier = generateEntryCacheId(options);
     if (options.temporaryOnly) {
-      return self.temporaryEntries[identifier] || $.Deferred().reject().promise();
+      return (
+        self.temporaryEntries[identifier] ||
+        $.Deferred()
+          .reject()
+          .promise()
+      );
     }
     if (self.entries[identifier]) {
       return self.entries[identifier];
     }
 
-    let deferred = $.Deferred();
+    const deferred = $.Deferred();
     self.entries[identifier] = deferred.promise();
 
     if (!cacheEnabled) {
-      deferred.resolve(new DataCatalogEntry({ dataCatalog: self, namespace: options.namespace, compute: options.compute, path: options.path, definition: options.definition })).promise();
+      deferred
+        .resolve(
+          new DataCatalogEntry({
+            dataCatalog: self,
+            namespace: options.namespace,
+            compute: options.compute,
+            path: options.path,
+            definition: options.definition
+          })
+        )
+        .promise();
     } else {
-      self.store.getItem(identifier).then(function (storeEntry) {
-        let definition = storeEntry ? storeEntry.definition : options.definition;
-        let entry = new DataCatalogEntry({ dataCatalog: self, namespace: options.namespace, compute: options.compute, path: options.path, definition: definition });
-        if (storeEntry) {
-          mergeEntry(entry, storeEntry);
-        } else if (!options.cachedOnly && options.definition) {
-          entry.saveLater();
-        }
-        deferred.resolve(entry);
-      }).catch(function (error) {
-        console.warn(error);
-        let entry = new DataCatalogEntry({ dataCatalog: self, namespace: options.namespace, compute: options.compute, path: options.path, definition: options.definition });
-        if (!options.cachedOnly && options.definition) {
-          entry.saveLater();
-        }
-        deferred.resolve(entry);
-      })
+      self.store
+        .getItem(identifier)
+        .then(storeEntry => {
+          const definition = storeEntry ? storeEntry.definition : options.definition;
+          const entry = new DataCatalogEntry({
+            dataCatalog: self,
+            namespace: options.namespace,
+            compute: options.compute,
+            path: options.path,
+            definition: definition
+          });
+          if (storeEntry) {
+            mergeEntry(entry, storeEntry);
+          } else if (!options.cachedOnly && options.definition) {
+            entry.saveLater();
+          }
+          deferred.resolve(entry);
+        })
+        .catch(error => {
+          console.warn(error);
+          const entry = new DataCatalogEntry({
+            dataCatalog: self,
+            namespace: options.namespace,
+            compute: options.compute,
+            path: options.path,
+            definition: options.definition
+          });
+          if (!options.cachedOnly && options.definition) {
+            entry.saveLater();
+          }
+          deferred.resolve(entry);
+        });
     }
 
     return self.entries[identifier];
-  };
+  }
 
   /**
    *
@@ -617,32 +720,45 @@ class DataCatalog {
    * @return {Promise}
    */
   getMultiTableEntry(options) {
-    let self = this;
-    let identifier = generateEntryCacheId(options);
+    const self = this;
+    const identifier = generateEntryCacheId(options);
     if (self.multiTableEntries[identifier]) {
       return self.multiTableEntries[identifier];
     }
 
-    let deferred = $.Deferred();
+    const deferred = $.Deferred();
     self.multiTableEntries[identifier] = deferred.promise();
 
     if (!cacheEnabled) {
-      deferred.resolve(new MultiTableEntry({ identifier: identifier, dataCatalog: self, paths: options.paths })).promise();
+      deferred
+        .resolve(
+          new MultiTableEntry({ identifier: identifier, dataCatalog: self, paths: options.paths })
+        )
+        .promise();
     } else {
-      self.multiTableStore.getItem(identifier).then(function (storeEntry) {
-        let entry = new MultiTableEntry({ identifier: identifier, dataCatalog: self, paths: options.paths });
-        if (storeEntry) {
-          mergeMultiTableEntry(entry, storeEntry);
-        }
-        deferred.resolve(entry);
-      }).catch(function (error) {
-        console.warn(error);
-        deferred.resolve(new MultiTableEntry({ identifier: identifier, dataCatalog: self, paths: options.paths }));
-      })
+      self.multiTableStore
+        .getItem(identifier)
+        .then(storeEntry => {
+          const entry = new MultiTableEntry({
+            identifier: identifier,
+            dataCatalog: self,
+            paths: options.paths
+          });
+          if (storeEntry) {
+            mergeMultiTableEntry(entry, storeEntry);
+          }
+          deferred.resolve(entry);
+        })
+        .catch(error => {
+          console.warn(error);
+          deferred.resolve(
+            new MultiTableEntry({ identifier: identifier, dataCatalog: self, paths: options.paths })
+          );
+        });
     }
 
     return self.multiTableEntries[identifier];
-  };
+  }
 
   /**
    * Updates the cache for the given multi tableentry
@@ -651,24 +767,29 @@ class DataCatalog {
    * @return {Promise}
    */
   persistMultiTableEntry(multiTableEntry) {
-    let self = this;
+    const self = this;
     if (!cacheEnabled || CACHEABLE_TTL.default <= 0 || CACHEABLE_TTL.optimizer <= 0) {
-      return $.Deferred().resolve().promise();
+      return $.Deferred()
+        .resolve()
+        .promise();
     }
-    let deferred = $.Deferred();
-    self.multiTableStore.setItem(multiTableEntry.identifier, {
-      version: DATA_CATALOG_VERSION,
-      topAggs: multiTableEntry.topAggs,
-      topColumns: multiTableEntry.topColumns,
-      topFilters: multiTableEntry.topFilters,
-      topJoins: multiTableEntry.topJoins,
-    }).then(deferred.resolve).catch(deferred.reject);
+    const deferred = $.Deferred();
+    self.multiTableStore
+      .setItem(multiTableEntry.identifier, {
+        version: DATA_CATALOG_VERSION,
+        topAggs: multiTableEntry.topAggs,
+        topColumns: multiTableEntry.topColumns,
+        topFilters: multiTableEntry.topFilters,
+        topJoins: multiTableEntry.topJoins
+      })
+      .then(deferred.resolve)
+      .catch(deferred.reject);
     return deferred.promise();
-  };
+  }
 }
 
-let generalDataCatalog = new GeneralDataCatalog();
-let sourceBoundCatalogs = {};
+const generalDataCatalog = new GeneralDataCatalog();
+const sourceBoundCatalogs = {};
 
 /**
  * Helper function to get the DataCatalog instance for a given data source.
@@ -676,15 +797,17 @@ let sourceBoundCatalogs = {};
  * @param {string} sourceType
  * @return {DataCatalog}
  */
-const getCatalog = function (sourceType) {
+const getCatalog = function(sourceType) {
   if (!sourceType) {
     throw new Error('getCatalog called without sourceType');
   }
-  return sourceBoundCatalogs[sourceType] || (sourceBoundCatalogs[sourceType] = new DataCatalog(sourceType));
+  return (
+    sourceBoundCatalogs[sourceType] ||
+    (sourceBoundCatalogs[sourceType] = new DataCatalog(sourceType))
+  );
 };
 
 export default {
-
   /**
    * Adds a detached (temporary) entry to the data catalog. This would allow autocomplete etc. of tables that haven't
    * been created yet.
@@ -704,7 +827,7 @@ export default {
    *
    * @return {Object}
    */
-  addTemporaryTable: function (options) {
+  addTemporaryTable: function(options) {
     return getCatalog(options.sourceType).addTemporaryTable(options);
   },
 
@@ -719,7 +842,7 @@ export default {
    *
    * @return {Promise}
    */
-  getEntry: function (options) {
+  getEntry: function(options) {
     return getCatalog(options.sourceType).getEntry(options);
   },
 
@@ -732,7 +855,7 @@ export default {
    *
    * @return {Promise}
    */
-  getMultiTableEntry: function (options) {
+  getMultiTableEntry: function(options) {
     return getCatalog(options.sourceType).getMultiTableEntry(options);
   },
 
@@ -753,12 +876,20 @@ export default {
    *
    * @return {CancellablePromise}
    */
-  getChildren:  function(options) {
-    let deferred = $.Deferred();
-    let cancellablePromises = [];
-    getCatalog(options.sourceType).getEntry(options).done(function (entry) {
-      cancellablePromises.push(entry.getChildren(options).done(deferred.resolve).fail(deferred.reject));
-    }).fail(deferred.reject);
+  getChildren: function(options) {
+    const deferred = $.Deferred();
+    const cancellablePromises = [];
+    getCatalog(options.sourceType)
+      .getEntry(options)
+      .done(entry => {
+        cancellablePromises.push(
+          entry
+            .getChildren(options)
+            .done(deferred.resolve)
+            .fail(deferred.reject)
+        );
+      })
+      .fail(deferred.reject);
     return new CancellablePromise(deferred, undefined, cancellablePromises);
   },
 
@@ -767,7 +898,7 @@ export default {
    *
    * @return {DataCatalog}
    */
-  getCatalog : getCatalog,
+  getCatalog: getCatalog,
 
   /**
    * @param {Object} [options]
@@ -784,14 +915,13 @@ export default {
    */
   updateAllNavigatorTags: generalDataCatalog.updateAllNavigatorTags.bind(generalDataCatalog),
 
-  enableCache: function () {
-    cacheEnabled = true
+  enableCache: function() {
+    cacheEnabled = true;
   },
 
-  disableCache: function () {
+  disableCache: function() {
     cacheEnabled = false;
   },
 
   applyCancellable: catalogUtils.applyCancellable
 };
-

File diff suppressed because it is too large
+ 463 - 290
desktop/core/src/desktop/js/catalog/dataCatalogEntry.js


+ 47 - 29
desktop/core/src/desktop/js/catalog/generalDataCatalog.js

@@ -14,18 +14,17 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import $ from 'jquery'
-import localforage from 'localforage'
+import $ from 'jquery';
+import localforage from 'localforage';
 
-import apiHelper from '../api/apiHelper'
+import apiHelper from 'api/apiHelper';
 
 const STORAGE_POSTFIX = LOGGED_USERNAME;
 const DATA_CATALOG_VERSION = 5;
 
 class GeneralDataCatalog {
-
   constructor() {
-    let self = this;
+    const self = this;
     self.store = localforage.createInstance({
       name: 'HueDataCatalog_' + STORAGE_POSTFIX
     });
@@ -41,12 +40,12 @@ class GeneralDataCatalog {
    * @return {Promise}
    */
   getAllNavigatorTags(options) {
-    let self = this;
+    const self = this;
     if (self.allNavigatorTagsPromise && (!options || !options.refreshCache)) {
       return self.allNavigatorTagsPromise;
     }
 
-    let deferred = $.Deferred();
+    const deferred = $.Deferred();
 
     if (!window.HAS_NAVIGATOR) {
       return deferred.reject().promise();
@@ -54,48 +53,63 @@ class GeneralDataCatalog {
 
     self.allNavigatorTagsPromise = deferred.promise();
 
-    let reloadAllTags = () => {
-      apiHelper.fetchAllNavigatorTags({
-        silenceErrors: options && options.silenceErrors,
-      }).done(deferred.resolve).fail(deferred.reject);
+    const reloadAllTags = () => {
+      apiHelper
+        .fetchAllNavigatorTags({
+          silenceErrors: options && options.silenceErrors
+        })
+        .done(deferred.resolve)
+        .fail(deferred.reject);
 
       if (window.CACHEABLE_TTL.default > 0) {
-        deferred.done(function (allTags) {
-          self.store.setItem('hue.dataCatalog.allNavTags', { allTags: allTags, hueTimestamp: Date.now(), version: DATA_CATALOG_VERSION });
-        })
+        deferred.done(allTags => {
+          self.store.setItem('hue.dataCatalog.allNavTags', {
+            allTags: allTags,
+            hueTimestamp: Date.now(),
+            version: DATA_CATALOG_VERSION
+          });
+        });
       }
     };
 
     if (window.CACHEABLE_TTL.default > 0 && (!options || !options.refreshCache)) {
-      self.store.getItem('hue.dataCatalog.allNavTags').then(function (storeEntry) {
-        if (storeEntry && storeEntry.version === DATA_CATALOG_VERSION && (!storeEntry.hueTimestamp || (Date.now() - storeEntry.hueTimestamp) < CACHEABLE_TTL.default)) {
-          deferred.resolve(storeEntry.allTags);
-        } else {
-          reloadAllTags();
-        }
-      }).catch(reloadAllTags);
+      self.store
+        .getItem('hue.dataCatalog.allNavTags')
+        .then(storeEntry => {
+          if (
+            storeEntry &&
+            storeEntry.version === DATA_CATALOG_VERSION &&
+            (!storeEntry.hueTimestamp ||
+              Date.now() - storeEntry.hueTimestamp < CACHEABLE_TTL.default)
+          ) {
+            deferred.resolve(storeEntry.allTags);
+          } else {
+            reloadAllTags();
+          }
+        })
+        .catch(reloadAllTags);
     } else {
       reloadAllTags();
     }
 
     return self.allNavigatorTagsPromise;
-  };
+  }
 
   /**
    * @param {string[]} tagsToAdd
    * @param {string[]} tagsToRemove
    */
   updateAllNavigatorTags(tagsToAdd, tagsToRemove) {
-    let self = this;
+    const self = this;
     if (self.allNavigatorTagsPromise) {
-      self.allNavigatorTagsPromise.done(function (allTags) {
-        tagsToAdd.forEach(function (newTag) {
+      self.allNavigatorTagsPromise.done(allTags => {
+        tagsToAdd.forEach(newTag => {
           if (!allTags[newTag]) {
             allTags[newTag] = 0;
           }
           allTags[newTag]++;
         });
-        tagsToRemove.forEach(function (removedTag) {
+        tagsToRemove.forEach(removedTag => {
           if (!allTags[removedTag]) {
             allTags[removedTag]--;
             if (allTags[removedTag] === 0) {
@@ -103,10 +117,14 @@ class GeneralDataCatalog {
             }
           }
         });
-        self.store.setItem('hue.dataCatalog.allNavTags', { allTags: allTags, hueTimestamp: Date.now(), version: DATA_CATALOG_VERSION });
+        self.store.setItem('hue.dataCatalog.allNavTags', {
+          allTags: allTags,
+          hueTimestamp: Date.now(),
+          version: DATA_CATALOG_VERSION
+        });
       });
     }
-  };
+  }
 }
 
-export default GeneralDataCatalog
+export default GeneralDataCatalog;

+ 80 - 30
desktop/core/src/desktop/js/catalog/multiTableEntry.js

@@ -14,7 +14,7 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import catalogUtils from './catalogUtils'
+import catalogUtils from 'catalog/catalogUtils';
 
 /**
  * Helper function to reload a NavOpt multi table attribute, like topAggs or topFilters
@@ -27,9 +27,18 @@ import catalogUtils from './catalogUtils'
  * @param {string} apiHelperFunction
  * @return {CancellablePromise}
  */
-const genericNavOptReload = function (multiTableEntry, options, promiseAttribute, dataAttribute, apiHelperFunction) {
+const genericNavOptReload = function(
+  multiTableEntry,
+  options,
+  promiseAttribute,
+  dataAttribute,
+  apiHelperFunction
+) {
   if (multiTableEntry.dataCatalog.canHaveNavOptMetadata()) {
-    return multiTableEntry.trackedPromise(promiseAttribute, catalogUtils.fetchAndSave(apiHelperFunction, dataAttribute, multiTableEntry, options));
+    return multiTableEntry.trackedPromise(
+      promiseAttribute,
+      catalogUtils.fetchAndSave(apiHelperFunction, dataAttribute, multiTableEntry, options)
+    );
   }
   multiTableEntry[promiseAttribute] = $.Deferred().reject();
   return multiTableEntry[promiseAttribute];
@@ -49,18 +58,47 @@ const genericNavOptReload = function (multiTableEntry, options, promiseAttribute
  * @param {string} apiHelperFunction
  * @return {CancellablePromise}
  */
-const genericNavOptGet = function (multiTableEntry, options, promiseAttribute, dataAttribute, apiHelperFunction) {
+const genericNavOptGet = function(
+  multiTableEntry,
+  options,
+  promiseAttribute,
+  dataAttribute,
+  apiHelperFunction
+) {
   if (options && options.cachedOnly) {
-    return catalogUtils.applyCancellable(multiTableEntry[promiseAttribute], options) || $.Deferred().reject(false).promise();
+    return (
+      catalogUtils.applyCancellable(multiTableEntry[promiseAttribute], options) ||
+      $.Deferred()
+        .reject(false)
+        .promise()
+    );
   }
   if (options && options.refreshCache) {
-    return catalogUtils.applyCancellable(genericNavOptReload(multiTableEntry, options, promiseAttribute, dataAttribute, apiHelperFunction), options);
+    return catalogUtils.applyCancellable(
+      genericNavOptReload(
+        multiTableEntry,
+        options,
+        promiseAttribute,
+        dataAttribute,
+        apiHelperFunction
+      ),
+      options
+    );
   }
-  return catalogUtils.applyCancellable(multiTableEntry[promiseAttribute] || genericNavOptReload(multiTableEntry, options, promiseAttribute, dataAttribute, apiHelperFunction), options);
+  return catalogUtils.applyCancellable(
+    multiTableEntry[promiseAttribute] ||
+      genericNavOptReload(
+        multiTableEntry,
+        options,
+        promiseAttribute,
+        dataAttribute,
+        apiHelperFunction
+      ),
+    options
+  );
 };
 
 class MultiTableEntry {
-
   /**
    *
    * @param {Object} options
@@ -70,7 +108,7 @@ class MultiTableEntry {
    * @constructor
    */
   constructor(options) {
-    let self = this;
+    const self = this;
     self.identifier = options.identifier;
     self.dataCatalog = options.dataCatalog;
     self.paths = options.paths;
@@ -86,7 +124,7 @@ class MultiTableEntry {
 
     self.topJoins = undefined;
     self.topJoinsPromise = undefined;
-  };
+  }
 
   /**
    * Save the multi table entry to cache
@@ -94,23 +132,23 @@ class MultiTableEntry {
    * @return {Promise}
    */
   save() {
-    let self = this;
+    const self = this;
     window.clearTimeout(self.saveTimeout);
     return self.dataCatalog.persistMultiTableEntry(self);
-  };
+  }
 
   /**
    * Save the multi table entry at a later point of time
    */
   saveLater() {
-    let self = this;
+    const self = this;
     if (CACHEABLE_TTL.default > 0) {
       window.clearTimeout(self.saveTimeout);
-      self.saveTimeout = window.setTimeout(function () {
+      self.saveTimeout = window.setTimeout(() => {
         self.save();
       }, 1000);
     }
-  };
+  }
   /**
    * Helper function that ensure that cancellable promises are not tracked anymore when cancelled
    *
@@ -118,14 +156,14 @@ class MultiTableEntry {
    * @param {CancellablePromise} cancellablePromise
    */
   trackedPromise(promiseName, cancellablePromise) {
-    let self = this;
+    const self = this;
     self[promiseName] = cancellablePromise;
-    return cancellablePromise.fail(function () {
+    return cancellablePromise.fail(() => {
       if (cancellablePromise.cancelled) {
         delete self[promiseName];
       }
-    })
-  };
+    });
+  }
 
   /**
    * Gets the top aggregate UDFs for the entry. It will fetch it if not cached or if the refresh option is set.
@@ -139,9 +177,9 @@ class MultiTableEntry {
    * @return {CancellablePromise}
    */
   getTopAggs(options) {
-    let self = this;
+    const self = this;
     return genericNavOptGet(self, options, 'topAggsPromise', 'topAggs', 'fetchNavOptTopAggs');
-  };
+  }
 
   /**
    * Gets the top columns for the entry. It will fetch it if not cached or if the refresh option is set.
@@ -155,9 +193,15 @@ class MultiTableEntry {
    * @return {CancellablePromise}
    */
   getTopColumns(options) {
-    let self = this;
-    return genericNavOptGet(self, options, 'topColumnsPromise', 'topColumns', 'fetchNavOptTopColumns');
-  };
+    const self = this;
+    return genericNavOptGet(
+      self,
+      options,
+      'topColumnsPromise',
+      'topColumns',
+      'fetchNavOptTopColumns'
+    );
+  }
 
   /**
    * Gets the top filters for the entry. It will fetch it if not cached or if the refresh option is set.
@@ -171,9 +215,15 @@ class MultiTableEntry {
    * @return {CancellablePromise}
    */
   getTopFilters(options) {
-    let self = this;
-    return genericNavOptGet(self, options, 'topFiltersPromise', 'topFilters', 'fetchNavOptTopFilters');
-  };
+    const self = this;
+    return genericNavOptGet(
+      self,
+      options,
+      'topFiltersPromise',
+      'topFilters',
+      'fetchNavOptTopFilters'
+    );
+  }
 
   /**
    * Gets the top joins for the entry. It will fetch it if not cached or if the refresh option is set.
@@ -187,9 +237,9 @@ class MultiTableEntry {
    * @return {CancellablePromise}
    */
   getTopJoins(options) {
-    let self = this;
+    const self = this;
     return genericNavOptGet(self, options, 'topJoinsPromise', 'topJoins', 'fetchNavOptTopJoins');
-  };
+  }
 }
 
-export default MultiTableEntry
+export default MultiTableEntry;

+ 26 - 26
desktop/core/src/desktop/js/hue.js

@@ -14,35 +14,35 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import './ext/jquery/hue.jquery.lib'
-import './ext/bootstrap.2.3.2.min'
-import _ from 'lodash'
-import filesize from 'filesize'
-import qq from './ext/fileuploader.custom'
-import page from 'page'
-import localforage from 'localforage'
+import 'ext/jquery/hue.jquery.lib';
+import 'ext/bootstrap.2.3.2.min';
+import _ from 'lodash';
+import filesize from 'filesize';
+import qq from 'ext/fileuploader.custom';
+import page from 'page';
+import localforage from 'localforage';
 
-import ko from 'knockout'
-import komapping from 'knockout.mapping'
-import 'ext/ko.editable.custom'
-import 'ext/ko.selectize.custom'
-import 'knockout-switch-case'
-import 'knockout-sortable'
-import 'knockout.validation'
+import ko from 'knockout';
+import komapping from 'knockout.mapping';
+import 'ext/ko.editable.custom';
+import 'ext/ko.selectize.custom';
+import 'knockout-switch-case';
+import 'knockout-sortable';
+import 'knockout.validation';
 
-import apiHelper from 'api/apiHelper'
-import CancellablePromise from 'api/cancellablePromise'
-import contextCatalog from 'catalog/contextCatalog'
-import dataCatalog from 'catalog/dataCatalog'
-import hueAnalytics from 'utils/hueAnalytics'
-import hueDebug from 'utils/hueDebug'
-import hueDrop from 'utils/hueDrop'
-import huePubSub from 'utils/huePubSub'
-import hueUtils from 'utils/hueUtils'
+import apiHelper from 'api/apiHelper';
+import CancellablePromise from 'api/cancellablePromise';
+import contextCatalog from 'catalog/contextCatalog';
+import dataCatalog from 'catalog/dataCatalog';
+import hueAnalytics from 'utils/hueAnalytics';
+import hueDebug from 'utils/hueDebug';
+import hueDrop from 'utils/hueDrop';
+import huePubSub from 'utils/huePubSub';
+import hueUtils from 'utils/hueUtils';
 
-import sqlUtils from 'sql/sqlUtils'
+import sqlUtils from 'sql/sqlUtils';
 
-import 'assist/assistViewModel'
+import 'assist/assistViewModel';
 
 // TODO: Migrate away
 window._ = _;
@@ -61,4 +61,4 @@ window.ko.mapping = komapping;
 window.localforage = localforage;
 window.page = page;
 window.sqlUtils = sqlUtils;
-window.qq = qq;
+window.qq = qq;

+ 673 - 139
desktop/core/src/desktop/js/sql/sqlUtils.js

@@ -14,85 +14,588 @@
 // 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'
+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
+  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
+  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
+  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();
+const 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) {
+const autocompleteFilter = (filter, entries) => {
+  const lowerCaseFilter = filter.toLowerCase();
+  return entries.filter(suggestion => {
     // TODO: Extend with fuzzy matches
-    var foundIndex = suggestion.value.toLowerCase().indexOf(lowerCaseFilter);
+    let foundIndex = suggestion.value.toLowerCase().indexOf(lowerCaseFilter);
     if (foundIndex !== -1) {
-      if (foundIndex === 0 || (suggestion.filterValue && suggestion.filterValue.toLowerCase().indexOf(lowerCaseFilter) === 0)) {
+      if (
+        foundIndex === 0 ||
+        (suggestion.filterValue &&
+          suggestion.filterValue.toLowerCase().indexOf(lowerCaseFilter) === 0)
+      ) {
         suggestion.filterWeight = 3;
-      } else  {
+      } 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;
-        }
+    } 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) {
@@ -104,10 +607,14 @@ let autocompleteFilter = (filter, entries) => {
   });
 };
 
-let sortSuggestions = (suggestions, filter, sortOverride) => {
+const sortSuggestions = (suggestions, filter, sortOverride) => {
   suggestions.sort((a, b) => {
     if (filter) {
-      if (typeof a.filterWeight !== 'undefined' && typeof b.filterWeight !== 'undefined' && b.filterWeight !== a.filterWeight) {
+      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') {
@@ -125,8 +632,8 @@ let sortSuggestions = (suggestions, filter, sortOverride) => {
         return 1;
       }
     }
-    let aWeight = a.category.weight + (a.weightAdjust || 0);
-    let bWeight = b.category.weight + (b.weightAdjust || 0);
+    const aWeight = a.category.weight + (a.weightAdjust || 0);
+    const bWeight = b.category.weight + (b.weightAdjust || 0);
     if (typeof aWeight !== 'undefined' && typeof bWeight !== 'undefined' && bWeight !== aWeight) {
       return bWeight - aWeight;
     }
@@ -140,7 +647,7 @@ let sortSuggestions = (suggestions, filter, sortOverride) => {
   });
 };
 
-let identifierChainToPath = (identifierChain) => identifierChain.map(identifier => identifier.name);
+const identifierChainToPath = identifierChain => identifierChain.map(identifier => identifier.name);
 
 /**
  *
@@ -156,10 +663,10 @@ let identifierChainToPath = (identifierChain) => identifierChain.map(identifier
  *
  * @return {CancellablePromise}
  */
-let resolveCatalogEntry = (options) => {
-  let cancellablePromises = [];
-  let deferred = $.Deferred();
-  let promise = new CancellablePromise(deferred, undefined, cancellablePromises);
+const resolveCatalogEntry = options => {
+  const cancellablePromises = [];
+  const deferred = $.Deferred();
+  const promise = new CancellablePromise(deferred, undefined, cancellablePromises);
   dataCatalog.applyCancellable(promise, options);
 
   if (!options.identifierChain) {
@@ -167,7 +674,7 @@ let resolveCatalogEntry = (options) => {
     return promise;
   }
 
-  let findInTree = (currentEntry, fieldsToGo) => {
+  const findInTree = (currentEntry, fieldsToGo) => {
     if (fieldsToGo.length === 0) {
       deferred.reject();
       return;
@@ -182,102 +689,123 @@ let resolveCatalogEntry = (options) => {
       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))
+    cancellablePromises.push(
+      currentEntry
+        .getChildren({
+          cancellable: options.cancellable,
+          cachedOnly: options.cachedOnly,
+          silenceErrors: true
+        })
+        .done(childEntries => {
+          let 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 => {
+  const findTable = tablesToGo => {
     if (tablesToGo.length === 0) {
       deferred.reject();
       return;
     }
 
-    let nextTable = tablesToGo.pop();
+    const 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;
-        }
-      });
+    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 => {
+          let 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 (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())
+    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))
-    })
+    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(entry => {
+        findInTree(entry, identifierChainToPath(options.identifierChain));
+      });
   }
 
   return promise;
 };
 
 export default {
-  autocompleteFilter : autocompleteFilter,
+  autocompleteFilter: autocompleteFilter,
   backTickIfNeeded: (sourceType, identifier) => {
     if (identifier.indexOf('`') === 0) {
       return identifier;
     }
-    var upperIdentifier = identifier.toUpperCase();
-    if (sourceType === 'hive' && (hiveReservedKeywords[upperIdentifier] || extraHiveReservedKeywords[upperIdentifier])) {
+    const 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])) {
+    if (
+      sourceType !== 'impala' &&
+      sourceType !== 'hive' &&
+      (impalaReservedKeywords[upperIdentifier] ||
+        hiveReservedKeywords[upperIdentifier] ||
+        extraHiveReservedKeywords[upperIdentifier])
+    ) {
       return '`' + identifier + '`';
     }
     if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(identifier)) {
@@ -285,9 +813,15 @@ export default {
     }
     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,
+  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
-}
+};

+ 5 - 5
desktop/core/src/desktop/js/utils/hueAnalytics.js

@@ -16,17 +16,17 @@
 
 import $ from 'jquery';
 
-let hueAnalytics = {
-  log: function (app, page) {
+const hueAnalytics = {
+  log: function(app, page) {
     if (typeof trackOnGA == 'function') {
       trackOnGA(app + '/' + page);
     }
   },
-  convert: function (app, page) {
+  convert: function(app, page) {
     $.post('/desktop/log_analytics', {
-      page: app + '/' + page,
+      page: app + '/' + page
     });
   }
 };
 
-export default hueAnalytics;
+export default hueAnalytics;

+ 10 - 8
desktop/core/src/desktop/js/utils/hueDebug.js

@@ -14,11 +14,13 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-let hueDebug = {
-  clearCaches: function () {
-    var promises = [];
-    var clearInstance = function (prefix) {
-      promises.push(localforage.createInstance({name: prefix + LOGGED_USERNAME}).clear());
+import localforage from 'localforage';
+
+const hueDebug = {
+  clearCaches: function() {
+    const promises = [];
+    const clearInstance = function(prefix) {
+      promises.push(localforage.createInstance({ name: prefix + LOGGED_USERNAME }).clear());
     };
     clearInstance('HueContextCatalog_');
     clearInstance('HueDataCatalog_');
@@ -26,10 +28,10 @@ let hueDebug = {
     clearInstance('HueDataCatalog_hive_multiTable_');
     clearInstance('HueDataCatalog_impala_');
     clearInstance('HueDataCatalog_impala_multiTable_');
-    Promise.all(promises).then(function () {
+    Promise.all(promises).then(() => {
       console.log('Done! Refresh the browser.');
-    })
+    });
   }
 };
 
-export default hueDebug;
+export default hueDebug;

+ 22 - 23
desktop/core/src/desktop/js/utils/hueDrop.js

@@ -14,21 +14,20 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import huePubSub from './huePubSub';
 import $ from 'jquery';
 
-let jQuery = $;
+import huePubSub from 'utils/huePubSub';
 
 const hueDrop = () => {
-  var draggableMeta = {};
+  let draggableMeta = {};
 
-  huePubSub.subscribe('draggable.text.meta', function (meta) {
+  huePubSub.subscribe('draggable.text.meta', meta => {
     draggableMeta = meta;
   });
 
   return {
-    fromAssist: function (element, callback) {
-      if (typeof element === 'function' && !(element instanceof jQuery)) {
+    fromAssist: function(element, callback) {
+      if (typeof element === 'function' && !(element instanceof $)) {
         callback = element;
       }
       if (typeof element === 'string') {
@@ -37,8 +36,7 @@ const hueDrop = () => {
       if (element.length > 0) {
         element.droppable({
           accept: '.draggableText',
-          drop: function (e, ui) {
-            var droppedText = ui.helper.text();
+          drop: function(e, ui) {
             if (callback) {
               callback({
                 text: ui.helper.text(),
@@ -47,29 +45,28 @@ const hueDrop = () => {
             }
           }
         });
-      }
-      else {
+      } else {
         console.warn('hueDrop.fromAssist could not be attached to the element');
       }
     },
-    fromDesktop: function (element, callback, method) {
+    fromDesktop: function(element, callback, method) {
       if (window.FileReader) {
-        if (typeof element === 'function' && !(element instanceof jQuery)) {
+        if (typeof element === 'function' && !(element instanceof $)) {
           callback = element;
         }
         if (typeof element === 'string') {
           element = $(element);
         }
 
-        let handleFileSelect = (e) => {
+        const handleFileSelect = e => {
           e.stopPropagation();
           e.preventDefault();
-          var dt = e.dataTransfer;
-          var files = dt.files;
-          for (var i = 0, f; f = files[i]; i++) {
-            var reader = new FileReader();
-            reader.onload = (function (file) {
-              return function (e) {
+          const dt = e.dataTransfer;
+          const files = dt.files;
+          for (let i = 0, f; (f = files[i]); i++) {
+            const reader = new FileReader();
+            reader.onload = (function(file) {
+              return function(e) {
                 callback(e.target.result);
               };
             })(f);
@@ -89,7 +86,7 @@ const hueDrop = () => {
           }
         };
 
-        let handleDragOver = (e) => {
+        const handleDragOver = e => {
           e.stopPropagation();
           e.preventDefault();
           e.dataTransfer.dropEffect = 'copy';
@@ -101,11 +98,13 @@ const hueDrop = () => {
         } else {
           console.warn('hueDrop.fromDesktop could not be attached to the element');
         }
-      }  else {
-        console.warn('FileReader is not supported by your browser. Please consider upgrading to fully experience Hue!')
+      } else {
+        console.warn(
+          'FileReader is not supported by your browser. Please consider upgrading to fully experience Hue!'
+        );
       }
     }
   };
 };
 
-export default hueDrop;
+export default hueDrop;

+ 45 - 31
desktop/core/src/desktop/js/utils/huePubSub.js

@@ -16,80 +16,94 @@
 
 // Based on original pub/sub implementation from http://davidwalsh.name/pubsub-javascript
 
-let topics = {};
-let hOP = topics.hasOwnProperty;
+const topics = {};
+const hOP = topics.hasOwnProperty;
 
 const huePubSub = {
-  subscribe: function (topic, listener, app) {
+  subscribe: function(topic, listener, app) {
     if (!hOP.call(topics, topic)) {
       topics[topic] = [];
     }
 
-    let index = topics[topic].push({
-      listener: listener,
-      app: app,
-      status: 'running'
-    }) - 1;
+    const index =
+      topics[topic].push({
+        listener: listener,
+        app: app,
+        status: 'running'
+      }) - 1;
 
     return {
-      remove: function () {
+      remove: function() {
         delete topics[topic][index];
       }
     };
   },
-  removeAll: function (topic) {
+  removeAll: function(topic) {
     topics[topic] = [];
   },
-  subscribeOnce: function (topic, listener, app) {
-    let ephemeral = this.subscribe(topic, function () {
-      listener.apply(listener, arguments);
-      ephemeral.remove();
-    }, app);
-
+  subscribeOnce: function(topic, listener, app) {
+    const ephemeral = this.subscribe(
+      topic,
+      function() {
+        listener.apply(listener, arguments);
+        ephemeral.remove();
+      },
+      app
+    );
   },
-  publish: function (topic, info) {
+  publish: function(topic, info) {
     if (!hOP.call(topics, topic)) {
       return;
     }
 
-    topics[topic].forEach(function (item) {
+    topics[topic].forEach(item => {
       if (item.status === 'running') {
         item.listener(info);
       }
     });
   },
-  getTopics: function () {
+  getTopics: function() {
     return topics;
   },
-  pauseAppSubscribers: function (app) {
+  pauseAppSubscribers: function(app) {
     if (app) {
-      Object.keys(topics).forEach(function (topicName) {
-        topics[topicName].forEach(function (topic) {
-          if (typeof topic.app !== 'undefined' && topic.app !== null && (topic.app === app || topic.app.split('-')[0] === app)) {
+      Object.keys(topics).forEach(topicName => {
+        topics[topicName].forEach(topic => {
+          if (
+            typeof topic.app !== 'undefined' &&
+            topic.app !== null &&
+            (topic.app === app || topic.app.split('-')[0] === app)
+          ) {
             topic.status = 'paused';
           }
         });
       });
     }
   },
-  resumeAppSubscribers: function (app) {
+  resumeAppSubscribers: function(app) {
     if (app) {
-      Object.keys(topics).forEach(function (topicName) {
-        topics[topicName].forEach(function (topic) {
-          if (typeof topic.app !== 'undefined' && topic.app !== null && (topic.app === app || topic.app.split('-')[0] === app)) {
+      Object.keys(topics).forEach(topicName => {
+        topics[topicName].forEach(topic => {
+          if (
+            typeof topic.app !== 'undefined' &&
+            topic.app !== null &&
+            (topic.app === app || topic.app.split('-')[0] === app)
+          ) {
             topic.status = 'running';
           }
         });
       });
     }
   },
-  clearAppSubscribers: function (app) {
+  clearAppSubscribers: function(app) {
     if (app) {
-      Object.keys(topics).forEach(function (topicName) {
-        topics[topicName] = topics[topicName].filter(function(obj){ return obj.app !== app });
+      Object.keys(topics).forEach(topicName => {
+        topics[topicName] = topics[topicName].filter(obj => {
+          return obj.app !== app;
+        });
       });
     }
   }
 };
 
-export default huePubSub
+export default huePubSub;

+ 121 - 92
desktop/core/src/desktop/js/utils/hueUtils.js

@@ -14,21 +14,21 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
-import $ from 'jquery'
+import $ from 'jquery';
 
 const bootstrapRatios = {
   span3() {
-    var windowWidth = $(window).width();
+    const windowWidth = $(window).width();
     if (windowWidth >= 1200) {
-    return 23.07692308;
-  } else if (windowWidth >= 768 && windowWidth <= 979) {
-    return 22.9281768;
-  } else {
-    return 23.17073171;
-  }
+      return 23.07692308;
+    } else if (windowWidth >= 768 && windowWidth <= 979) {
+      return 22.9281768;
+    } else {
+      return 23.17073171;
+    }
   },
   span9() {
-    var windowWidth = $(window).width();
+    const windowWidth = $(window).width();
     if (windowWidth >= 1200) {
       return 74.35897436;
     } else if (windowWidth >= 768 && windowWidth <= 979) {
@@ -49,22 +49,22 @@ const bootstrapRatios = {
  * @param selectors
  * @return {default}
  */
-const text2Url = (selectors) => {
-  var i = 0,
-    len = selectors.length;
+const text2Url = selectors => {
+  let i = 0;
+  const len = selectors.length;
 
   for (i; i < len; i++) {
-    var arr = [],
+    const arr = [],
       selector = selectors[i],
       val = selector.innerHTML.replace(/&nbsp;/g, ' ').split(' ');
 
-    val.forEach(function(word) {
-      var matched = null,
-        re = /(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?/gi;
+    val.forEach(word => {
+      let matched = null;
+      const re = /(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{2,6})([\/\w \.-]*)*\/?/gi;
 
       if (re.test(word)) {
         matched = word.match(re);
-        word = word.replace(matched, '<a href="' + matched + '">' + matched + '</a>')
+        word = word.replace(matched, '<a href="' + matched + '">' + matched + '</a>');
         arr.push(word);
       } else {
         arr.push(word);
@@ -83,16 +83,26 @@ const text2Url = (selectors) => {
  * @param value
  * @return {*|jQuery}
  */
-const htmlEncode = (value) => {
-  return $('<div/>').text(value).html();
+const htmlEncode = value => {
+  return $('<div/>')
+    .text(value)
+    .html();
 };
 
-const html2text = (value) => {
-  return $('<div/>').html(value).text().replace(/\u00A0/g, ' ');
+const html2text = value => {
+  return $('<div/>')
+    .html(value)
+    .text()
+    .replace(/\u00A0/g, ' ');
 };
 
 const goFullScreen = () => {
-  if (!document.fullscreenElement && !document.mozFullScreenElement && !document.webkitFullscreenElement && !document.msFullscreenElement) {
+  if (
+    !document.fullscreenElement &&
+    !document.mozFullScreenElement &&
+    !document.webkitFullscreenElement &&
+    !document.msFullscreenElement
+  ) {
     if (document.documentElement.requestFullscreen) {
       document.documentElement.requestFullscreen();
     } else if (document.documentElement.msRequestFullscreen) {
@@ -106,8 +116,12 @@ const goFullScreen = () => {
 };
 
 const exitFullScreen = () => {
-  if (document.fullscreenElement ||
-    document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement) {
+  if (
+    document.fullscreenElement ||
+    document.mozFullScreenElement ||
+    document.webkitFullscreenElement ||
+    document.msFullscreenElement
+  ) {
     if (document.exitFullscreen) {
       document.exitFullscreen();
     } else if (document.msExitFullscreen) {
@@ -121,12 +135,12 @@ const exitFullScreen = () => {
 };
 
 const changeURL = (newURL, params) => {
-  var extraSearch = '';
+  let extraSearch = '';
   if (params) {
-    var newSearchKeys = Object.keys(params);
+    const newSearchKeys = Object.keys(params);
     if (newSearchKeys.length) {
       while (newSearchKeys.length) {
-        var newKey = newSearchKeys.pop();
+        const newKey = newSearchKeys.pop();
         extraSearch += newKey + '=' + params[newKey];
         if (newSearchKeys.length) {
           extraSearch += '&';
@@ -136,17 +150,17 @@ const changeURL = (newURL, params) => {
   }
 
   if (typeof IS_EMBEDDED !== 'undefined' && IS_EMBEDDED) {
-    var search = window.location.search;
+    let search = window.location.search;
     if (extraSearch) {
-      search += (search ? '&' : '?') + extraSearch
+      search += (search ? '&' : '?') + extraSearch;
     }
     newURL = window.location.pathname + search + '#!' + newURL.replace('/hue', '');
     window.history.pushState(null, null, newURL);
     return;
   }
 
-  var hashSplit = newURL.split('#');
-  var url = hashSplit[0];
+  const hashSplit = newURL.split('#');
+  let url = hashSplit[0];
   if (extraSearch) {
     url += (url.indexOf('?') === -1 ? '?' : '&') + extraSearch;
   }
@@ -158,16 +172,16 @@ const changeURL = (newURL, params) => {
   window.history.pushState(null, null, url);
 };
 
-const replaceURL = (newURL) => {
+const replaceURL = newURL => {
   window.history.replaceState(null, null, newURL);
 };
 
 const changeURLParameter = (param, value) => {
   if (typeof IS_EMBEDDED !== 'undefined' && IS_EMBEDDED) {
-    var currentUrl = window.location.hash.replace('#!', '');
-    var parts = currentUrl.split('?');
-    var path = parts[0];
-    var search = parts.length > 1 ? parts[1] : '';
+    const currentUrl = window.location.hash.replace('#!', '');
+    const parts = currentUrl.split('?');
+    const path = parts[0];
+    let search = parts.length > 1 ? parts[1] : '';
     if (~search.indexOf(param + '=' + value)) {
       return;
     }
@@ -188,19 +202,24 @@ const changeURLParameter = (param, value) => {
 
     changeURL(search ? path + '?' + search : path);
   } else {
-    var newSearch = '';
+    let newSearch = '';
     if (window.location.getParameter(param, true) !== null) {
       newSearch += '?';
-      window.location.search.replace(/\?/gi, '').split('&').forEach(function (p) {
-        if (p.split('=')[0] !== param) {
-          newSearch += p;
-        }
-      });
-      if (value){
+      window.location.search
+        .replace(/\?/gi, '')
+        .split('&')
+        .forEach(p => {
+          if (p.split('=')[0] !== param) {
+            newSearch += p;
+          }
+        });
+      if (value) {
         newSearch += (newSearch !== '?' ? '&' : '') + param + '=' + value;
       }
     } else {
-      newSearch = window.location.search + (value ? (window.location.search.indexOf('?') > -1 ? '&' : '?') + param + '=' + value : '' );
+      newSearch =
+        window.location.search +
+        (value ? (window.location.search.indexOf('?') > -1 ? '&' : '?') + param + '=' + value : '');
     }
 
     if (newSearch === '?') {
@@ -211,18 +230,18 @@ const changeURLParameter = (param, value) => {
   }
 };
 
-const removeURLParameter = (param) => {
+const removeURLParameter = param => {
   changeURLParameter(param, null);
 };
 
-const parseHivePseudoJson = (pseudoJson) => {
+const parseHivePseudoJson = pseudoJson => {
   // Hive returns a pseudo-json with parameters, like
   // "{Lead Developer=John Foo, Lead Developer Email=jfoo@somewhere.com, date=2013-07-11 }"
-  var parsedParams = {};
-  if (pseudoJson && pseudoJson.length > 2){
-    var splits = pseudoJson.substring(1, pseudoJson.length-1).split(', ');
-    splits.forEach(function(part){
-      if (part.indexOf('=') > -1){
+  const parsedParams = {};
+  if (pseudoJson && pseudoJson.length > 2) {
+    const splits = pseudoJson.substring(1, pseudoJson.length - 1).split(', ');
+    splits.forEach(part => {
+      if (part.indexOf('=') > -1) {
         parsedParams[part.split('=')[0]] = part.split('=')[1];
       }
     });
@@ -230,7 +249,7 @@ const parseHivePseudoJson = (pseudoJson) => {
   return parsedParams;
 };
 
-const isOverflowing = (element) => {
+const isOverflowing = element => {
   if (element instanceof jQuery) {
     element = element[0];
   }
@@ -238,12 +257,12 @@ const isOverflowing = (element) => {
 };
 
 const waitForRendered = (selector, condition, callback, timeout) => {
-  var $el = selector instanceof jQuery ? selector: $(selector);
+  const $el = selector instanceof jQuery ? selector : $(selector);
   if (condition($el)) {
     callback($el);
   } else {
     window.clearTimeout($el.data('waitForRenderTimeout'));
-    var waitForRenderTimeout = window.setTimeout(function () {
+    const waitForRenderTimeout = window.setTimeout(() => {
       waitForRendered(selector, condition, callback);
     }, timeout || 100);
     $el.data('waitForRenderTimeout', waitForRenderTimeout);
@@ -253,9 +272,8 @@ const waitForRendered = (selector, condition, callback, timeout) => {
 const waitForObservable = (observable, callback) => {
   if (observable()) {
     callback(observable);
-  }
-  else {
-    var subscription = observable.subscribe(function(newValue) {
+  } else {
+    const subscription = observable.subscribe(newValue => {
       if (newValue) {
         subscription.dispose();
         callback(observable);
@@ -267,34 +285,34 @@ const waitForObservable = (observable, callback) => {
 const waitForVariable = (variable, callback, timeout) => {
   if (variable) {
     callback(variable);
-  }
-  else {
-    window.setTimeout(function () {
+  } else {
+    window.setTimeout(() => {
       waitForVariable(variable, callback);
-    }, timeout || 100)
+    }, timeout || 100);
   }
 };
 
 const scrollbarWidth = () => {
-  var $parent, $children, width;
-  $parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo(HUE_CONTAINER);
-  $children = $parent.children();
-  width = $children.innerWidth() - $children.height(99).innerWidth();
+  const $parent = $('<div style="width:50px;height:50px;overflow:auto"><div/></div>').appendTo(
+    HUE_CONTAINER
+  );
+  const $children = $parent.children();
+  const width = $children.innerWidth() - $children.height(99).innerWidth();
   $parent.remove();
   return width;
 };
 
 const getSearchParameter = (search, name, returnNull) => {
-  name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
-  var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"),
+  name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
+  const regex = new RegExp('[\\?&]' + name + '=([^&#]*)'),
     results = regex.exec(search);
-  if (returnNull && results === null){
+  if (returnNull && results === null) {
     return null;
   }
-  return results === null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
+  return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
 };
 
-const logError = (error) => {
+const logError = error => {
   if (typeof window.console !== 'undefined' && typeof window.console.error !== 'undefined') {
     if (typeof error !== 'undefined') {
       console.error(error);
@@ -305,19 +323,19 @@ const logError = (error) => {
 
 const equalIgnoreCase = (a, b) => a && b && a.toLowerCase() === b.toLowerCase();
 
-const deXSS = (str) => {
+const deXSS = str => {
   if (typeof str !== 'undefined' && str !== null && typeof str === 'string') {
     return str.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '');
   }
   return str;
 };
 
-const getStyleFromCSSClass = (cssClass) => {
-  for (var i = 0; i < document.styleSheets.length; i++) {
-    var cssClasses = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
-    for (var x = 0; x < cssClasses.length; x++) {
+const getStyleFromCSSClass = cssClass => {
+  for (let i = 0; i < document.styleSheets.length; i++) {
+    const cssClasses = document.styleSheets[i].rules || document.styleSheets[i].cssRules;
+    for (let x = 0; x < cssClasses.length; x++) {
       if (cssClasses[x].selectorText == cssClass) {
-        return (cssClasses[x].style) ? cssClasses[x].style : cssClasses[x];
+        return cssClasses[x].style ? cssClasses[x].style : cssClasses[x];
       }
     }
   }
@@ -328,15 +346,20 @@ const highlight = (text, searchTerm) => {
     return text;
   }
 
-  var remText = text;
-  var highLightedText = '';
+  let remText = text;
+  let highLightedText = '';
   searchTerm = searchTerm.toLowerCase();
 
+  let startIndex;
   do {
-    var remLowerText = remText.toLowerCase();
-    var startIndex = remLowerText.indexOf(searchTerm);
-    if(startIndex >= 0) {
-      highLightedText += remText.substring(0, startIndex) + '<strong>' + remText.substring(startIndex, startIndex + searchTerm.length) + '</strong>';
+    const remLowerText = remText.toLowerCase();
+    startIndex = remLowerText.indexOf(searchTerm);
+    if (startIndex >= 0) {
+      highLightedText +=
+        remText.substring(0, startIndex) +
+        '<strong>' +
+        remText.substring(startIndex, startIndex + searchTerm.length) +
+        '</strong>';
       remText = remText.substring(startIndex + searchTerm.length);
     } else {
       highLightedText += remText;
@@ -347,18 +370,18 @@ const highlight = (text, searchTerm) => {
 };
 
 const dfs = (node, callback) => {
-  if (!node || typeof(node) !== 'object') {
+  if (!node || typeof node !== 'object') {
     return;
   }
-  Object.keys(node).forEach(function(key) {
+  Object.keys(node).forEach(key => {
     callback(node, key);
     dfs(node[key], callback);
   });
 };
 
-const deleteAllEmptyStringKey = (node) => {
-  var fDeleteEmptyStringKey = function (node, key) {
-    if (node[key] || typeof(node[key]) !== 'string') {
+const deleteAllEmptyStringKey = node => {
+  const fDeleteEmptyStringKey = function(node, key) {
+    if (node[key] || typeof node[key] !== 'string') {
       return;
     }
     delete node[key];
@@ -366,12 +389,18 @@ const deleteAllEmptyStringKey = (node) => {
   dfs(node, fDeleteEmptyStringKey);
 };
 
+const s4 = () =>
+  Math.floor((1 + Math.random()) * 0x10000)
+    .toString(16)
+    .substring(1);
 
-const s4 = () => Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
-
-const UUID = () =>  s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
+const UUID = () => s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
 
-const escapeOutput = (str) =>  $('<span>').text(str).html().trim();
+const escapeOutput = str =>
+  $('<span>')
+    .text(str)
+    .html()
+    .trim();
 
 export default {
   bootstrapRatios: bootstrapRatios,
@@ -400,4 +429,4 @@ export default {
   deleteAllEmptyStringKey: deleteAllEmptyStringKey,
   UUID: UUID,
   escapeOutput: escapeOutput
-}
+};

File diff suppressed because it is too large
+ 138 - 138
desktop/core/src/desktop/static/desktop/js/hue-bundle-72c7c47509e0d902668b.js


File diff suppressed because it is too large
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/hue-bundle-72c7c47509e0d902668b.js.map


File diff suppressed because it is too large
+ 0 - 0
desktop/core/src/desktop/static/desktop/js/hue-bundle-8e5037d20b056ab61e91.js.map


+ 669 - 0
package-lock.json

@@ -1192,6 +1192,12 @@
       "integrity": "sha512-d3OEjQV4ROpoflsnUA8HozoIR504TFxNivYEUi6uwz0IYhBkTDXGuWlNdMtybRt3nqVx/L6XqMt0FxkXuWKZhw==",
       "dev": true
     },
+    "acorn-jsx": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz",
+      "integrity": "sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg==",
+      "dev": true
+    },
     "acorn-to-esprima": {
       "version": "2.0.8",
       "resolved": "https://registry.npmjs.org/acorn-to-esprima/-/acorn-to-esprima-2.0.8.tgz",
@@ -1221,6 +1227,12 @@
       "integrity": "sha1-6GuBnGAs+IIa1jdBNpjx3sAhhHo=",
       "dev": true
     },
+    "ansi-escapes": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz",
+      "integrity": "sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==",
+      "dev": true
+    },
     "ansi-regex": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
@@ -1391,6 +1403,12 @@
       "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=",
       "dev": true
     },
+    "astral-regex": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz",
+      "integrity": "sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==",
+      "dev": true
+    },
     "async": {
       "version": "1.5.2",
       "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
@@ -1653,6 +1671,32 @@
         }
       }
     },
+    "babel-eslint": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/babel-eslint/-/babel-eslint-10.0.1.tgz",
+      "integrity": "sha512-z7OT1iNV+TjOwHNLLyJk+HN+YVWX+CLE6fPD2SymJZOZQBs+QIexFjhm4keGTm8MW9xr4EC9Q0PbaLB24V5GoQ==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "@babel/parser": "^7.0.0",
+        "@babel/traverse": "^7.0.0",
+        "@babel/types": "^7.0.0",
+        "eslint-scope": "3.7.1",
+        "eslint-visitor-keys": "^1.0.0"
+      },
+      "dependencies": {
+        "eslint-scope": {
+          "version": "3.7.1",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-3.7.1.tgz",
+          "integrity": "sha1-PWPD7f2gLgbgGkUq2IyqzHzctug=",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
+          }
+        }
+      }
+    },
     "babel-generator": {
       "version": "6.26.1",
       "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz",
@@ -2161,6 +2205,12 @@
         "unset-value": "^1.0.0"
       }
     },
+    "callsites": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz",
+      "integrity": "sha512-tWnkwu9YEq2uzlBDI4RcLn8jrFvF9AOi8PxDNU3hZZjJcjkcRAq3vCI+vZcg1SuxISDYe86k9VZFwAxDiJGoAw==",
+      "dev": true
+    },
     "camelcase": {
       "version": "2.1.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
@@ -2211,6 +2261,12 @@
         "supports-color": "^2.0.0"
       }
     },
+    "chardet": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
+      "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
+      "dev": true
+    },
     "chokidar": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.0.4.tgz",
@@ -2257,6 +2313,12 @@
         "safe-buffer": "^5.0.1"
       }
     },
+    "circular-json": {
+      "version": "0.3.3",
+      "resolved": "https://registry.npmjs.org/circular-json/-/circular-json-0.3.3.tgz",
+      "integrity": "sha512-UZK3NBx2Mca+b5LsG7bY183pHWt5Y1xts4P3Pz7ENTwGVnJOUWbRb3ocjvX7hx9tq/yTAdclXm9sZ38gNuem4A==",
+      "dev": true
+    },
     "class-utils": {
       "version": "0.3.6",
       "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
@@ -2280,6 +2342,15 @@
         }
       }
     },
+    "cli-cursor": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
+      "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=",
+      "dev": true,
+      "requires": {
+        "restore-cursor": "^2.0.0"
+      }
+    },
     "cli-table3": {
       "version": "0.5.1",
       "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz",
@@ -2291,6 +2362,12 @@
         "string-width": "^2.1.1"
       }
     },
+    "cli-width": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz",
+      "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=",
+      "dev": true
+    },
     "cliui": {
       "version": "4.1.0",
       "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz",
@@ -2608,6 +2685,12 @@
       "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==",
       "dev": true
     },
+    "deep-is": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
+      "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
+      "dev": true
+    },
     "define-properties": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@@ -2699,6 +2782,15 @@
         "randombytes": "^2.0.0"
       }
     },
+    "doctrine": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+      "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
     "domain-browser": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/domain-browser/-/domain-browser-1.2.0.tgz",
@@ -2838,6 +2930,174 @@
       "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=",
       "dev": true
     },
+    "eslint": {
+      "version": "5.12.1",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.12.1.tgz",
+      "integrity": "sha512-54NV+JkTpTu0d8+UYSA8mMKAG4XAsaOrozA9rCW7tgneg1mevcL7wIotPC+fZ0SkWwdhNqoXoxnQCTBp7UvTsg==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "ajv": "^6.5.3",
+        "chalk": "^2.1.0",
+        "cross-spawn": "^6.0.5",
+        "debug": "^4.0.1",
+        "doctrine": "^2.1.0",
+        "eslint-scope": "^4.0.0",
+        "eslint-utils": "^1.3.1",
+        "eslint-visitor-keys": "^1.0.0",
+        "espree": "^5.0.0",
+        "esquery": "^1.0.1",
+        "esutils": "^2.0.2",
+        "file-entry-cache": "^2.0.0",
+        "functional-red-black-tree": "^1.0.1",
+        "glob": "^7.1.2",
+        "globals": "^11.7.0",
+        "ignore": "^4.0.6",
+        "import-fresh": "^3.0.0",
+        "imurmurhash": "^0.1.4",
+        "inquirer": "^6.1.0",
+        "js-yaml": "^3.12.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.3.0",
+        "lodash": "^4.17.5",
+        "minimatch": "^3.0.4",
+        "mkdirp": "^0.5.1",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.8.2",
+        "path-is-inside": "^1.0.2",
+        "pluralize": "^7.0.0",
+        "progress": "^2.0.0",
+        "regexpp": "^2.0.1",
+        "semver": "^5.5.1",
+        "strip-ansi": "^4.0.0",
+        "strip-json-comments": "^2.0.1",
+        "table": "^5.0.2",
+        "text-table": "^0.2.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz",
+          "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "debug": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
+          "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
+          "dev": true,
+          "requires": {
+            "ms": "^2.1.1"
+          }
+        },
+        "esprima": {
+          "version": "4.0.1",
+          "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
+          "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
+          "dev": true
+        },
+        "glob": {
+          "version": "7.1.3",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz",
+          "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.0.4",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "globals": {
+          "version": "11.10.0",
+          "resolved": "https://registry.npmjs.org/globals/-/globals-11.10.0.tgz",
+          "integrity": "sha512-0GZF1RiPKU97IHUO5TORo9w1PwrH/NBPl+fS7oMLdaTRiYmYbwK4NWoZWrAdd0/abG9R2BU+OiwyQpTpE6pdfQ==",
+          "dev": true
+        },
+        "js-yaml": {
+          "version": "3.12.1",
+          "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.1.tgz",
+          "integrity": "sha512-um46hB9wNOKlwkHgiuyEVAybXBjwFUV0Z/RaHJblRd9DXltue9FTYvzCr9ErQrK9Adz5MU4gHWVaNUfdmrC8qA==",
+          "dev": true,
+          "requires": {
+            "argparse": "^1.0.7",
+            "esprima": "^4.0.0"
+          }
+        },
+        "ms": {
+          "version": "2.1.1",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz",
+          "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==",
+          "dev": true
+        },
+        "strip-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz",
+          "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^3.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "eslint-config-prettier": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-4.0.0.tgz",
+      "integrity": "sha512-kWuiJxzV5NwOwZcpyozTzDT5KJhBw292bbYro9Is7BWnbNMg15Gmpluc1CTetiCatF8DRkNvgPAOaSyg+bYr3g==",
+      "dev": true,
+      "requires": {
+        "get-stdin": "^6.0.0"
+      },
+      "dependencies": {
+        "get-stdin": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
+          "integrity": "sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==",
+          "dev": true
+        }
+      }
+    },
+    "eslint-plugin-prettier": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.0.1.tgz",
+      "integrity": "sha512-/PMttrarPAY78PLvV3xfWibMOdMDl57hmlQ2XqFeA37wd+CJ7WSxV7txqjVPHi/AAFKd2lX0ZqfsOc/i5yFCSQ==",
+      "dev": true,
+      "requires": {
+        "prettier-linter-helpers": "^1.0.0"
+      }
+    },
     "eslint-scope": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.0.tgz",
@@ -2848,12 +3108,44 @@
         "estraverse": "^4.1.1"
       }
     },
+    "eslint-utils": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz",
+      "integrity": "sha512-Z7YjnIldX+2XMcjr7ZkgEsOj/bREONV60qYeB/bjMAqqqZ4zxKyWX+BOUkdmRmA9riiIPVvo5x86m5elviOk0Q==",
+      "dev": true
+    },
+    "eslint-visitor-keys": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz",
+      "integrity": "sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ==",
+      "dev": true
+    },
+    "espree": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.0.tgz",
+      "integrity": "sha512-1MpUfwsdS9MMoN7ZXqAr9e9UKdVHDcvrJpyx7mm1WuQlx/ygErEQBzgi5Nh5qBHIoYweprhtMkTCb9GhcAIcsA==",
+      "dev": true,
+      "requires": {
+        "acorn": "^6.0.2",
+        "acorn-jsx": "^5.0.0",
+        "eslint-visitor-keys": "^1.0.0"
+      }
+    },
     "esprima": {
       "version": "2.7.3",
       "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz",
       "integrity": "sha1-luO3DVd59q1JzQMmc9HDEnZ7pYE=",
       "dev": true
     },
+    "esquery": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
+      "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^4.0.0"
+      }
+    },
     "esrecurse": {
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
@@ -3058,6 +3350,17 @@
         }
       }
     },
+    "external-editor": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz",
+      "integrity": "sha512-bn71H9+qWoOQKyZDo25mOMVpSmXROAsTJVVVYzrrtol3d4y+AsKjf4Iwl2Q+IuT0kFSQ1qo166UuIwqYq7mGnA==",
+      "dev": true,
+      "requires": {
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
+      }
+    },
     "extglob": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
@@ -3133,11 +3436,23 @@
       "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
       "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
     },
+    "fast-diff": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+      "dev": true
+    },
     "fast-json-stable-stringify": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
       "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
     },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
     "faye-websocket": {
       "version": "0.10.0",
       "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
@@ -3163,6 +3478,16 @@
         "object-assign": "^4.1.0"
       }
     },
+    "file-entry-cache": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-2.0.0.tgz",
+      "integrity": "sha1-w5KZDD5oR4PYOLjISkXYoEhFg2E=",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^1.2.1",
+        "object-assign": "^4.0.1"
+      }
+    },
     "filename-regex": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/filename-regex/-/filename-regex-2.0.1.tgz",
@@ -3242,6 +3567,18 @@
         }
       }
     },
+    "flat-cache": {
+      "version": "1.3.4",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-1.3.4.tgz",
+      "integrity": "sha512-VwyB3Lkgacfik2vhqR4uv2rvebqmDvFu4jlN/C1RzWoJEo8I7z4Q404oiqYCkq41mni8EzQnm95emU9seckwtg==",
+      "dev": true,
+      "requires": {
+        "circular-json": "^0.3.1",
+        "graceful-fs": "^4.1.2",
+        "rimraf": "~2.6.2",
+        "write": "^0.2.1"
+      }
+    },
     "flush-write-stream": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/flush-write-stream/-/flush-write-stream-1.0.3.tgz",
@@ -3863,6 +4200,12 @@
       "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
       "dev": true
     },
+    "functional-red-black-tree": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
+      "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
+      "dev": true
+    },
     "gaze": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/gaze/-/gaze-1.1.3.tgz",
@@ -4495,6 +4838,12 @@
       "integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=",
       "dev": true
     },
+    "ignore": {
+      "version": "4.0.6",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz",
+      "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==",
+      "dev": true
+    },
     "image-size": {
       "version": "0.5.5",
       "resolved": "https://registry.npmjs.org/image-size/-/image-size-0.5.5.tgz",
@@ -4506,6 +4855,24 @@
       "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
       "integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps="
     },
+    "import-fresh": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz",
+      "integrity": "sha512-pOnA9tfM3Uwics+SaBLCNyZZZbK+4PTu0OPZtLlMIrv17EdBoC15S9Kn8ckJ9TZTyKb3ywNE5y1yeDxxGA7nTQ==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      },
+      "dependencies": {
+        "resolve-from": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+          "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+          "dev": true
+        }
+      }
+    },
     "import-local": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/import-local/-/import-local-2.0.0.tgz",
@@ -4559,6 +4926,82 @@
       "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==",
       "dev": true
     },
+    "inquirer": {
+      "version": "6.2.2",
+      "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz",
+      "integrity": "sha512-Z2rREiXA6cHRR9KBOarR3WuLlFzlIfAEIiB45ll5SSadMg7WqOh1MKEjjndfuH5ewXdixWCxqnVfGOQzPeiztA==",
+      "dev": true,
+      "requires": {
+        "ansi-escapes": "^3.2.0",
+        "chalk": "^2.4.2",
+        "cli-cursor": "^2.1.0",
+        "cli-width": "^2.0.0",
+        "external-editor": "^3.0.3",
+        "figures": "^2.0.0",
+        "lodash": "^4.17.11",
+        "mute-stream": "0.0.7",
+        "run-async": "^2.2.0",
+        "rxjs": "^6.4.0",
+        "string-width": "^2.1.0",
+        "strip-ansi": "^5.0.0",
+        "through": "^2.3.6"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.0.0.tgz",
+          "integrity": "sha512-iB5Dda8t/UqpPI/IjsejXu5jOGDrzn41wJyljwPH65VCIbk6+1BzFIMJGFwTNrYXT1CrD+B4l19U7awiQ8rk7w==",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        },
+        "chalk": {
+          "version": "2.4.2",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
+          "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^3.2.1",
+            "escape-string-regexp": "^1.0.5",
+            "supports-color": "^5.3.0"
+          }
+        },
+        "figures": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+          "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+          "dev": true,
+          "requires": {
+            "escape-string-regexp": "^1.0.5"
+          }
+        },
+        "strip-ansi": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.0.0.tgz",
+          "integrity": "sha512-Uu7gQyZI7J7gn5qLn1Np3G9vcYGTVqB+lFTytnDJv83dd8T22aGH451P3jueT2/QemInJDfxHB5Tde5OzgG1Ow==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^4.0.0"
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
     "interpret": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.2.0.tgz",
@@ -4777,6 +5220,12 @@
       "dev": true,
       "optional": true
     },
+    "is-promise": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
+      "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=",
+      "dev": true
+    },
     "is-regex": {
       "version": "1.0.4",
       "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz",
@@ -4915,6 +5364,12 @@
       "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
       "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
     },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
+      "dev": true
+    },
     "json-stringify-safe": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
@@ -5005,6 +5460,16 @@
         }
       }
     },
+    "levn": {
+      "version": "0.3.0",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz",
+      "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2"
+      }
+    },
     "lie": {
       "version": "3.1.1",
       "resolved": "https://registry.npmjs.org/lie/-/lie-3.1.1.tgz",
@@ -5509,6 +5974,12 @@
         "minimatch": "^3.0.0"
       }
     },
+    "mute-stream": {
+      "version": "0.0.7",
+      "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
+      "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=",
+      "dev": true
+    },
     "nan": {
       "version": "2.12.1",
       "resolved": "https://registry.npmjs.org/nan/-/nan-2.12.1.tgz",
@@ -5535,6 +6006,12 @@
         "to-regex": "^3.0.1"
       }
     },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=",
+      "dev": true
+    },
     "neo-async": {
       "version": "2.6.0",
       "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.0.tgz",
@@ -5746,6 +6223,15 @@
         "wrappy": "1"
       }
     },
+    "onetime": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz",
+      "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=",
+      "dev": true,
+      "requires": {
+        "mimic-fn": "^1.0.0"
+      }
+    },
     "optimist": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz",
@@ -5762,6 +6248,28 @@
         }
       }
     },
+    "optionator": {
+      "version": "0.8.2",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz",
+      "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=",
+      "dev": true,
+      "requires": {
+        "deep-is": "~0.1.3",
+        "fast-levenshtein": "~2.0.4",
+        "levn": "~0.3.0",
+        "prelude-ls": "~1.1.2",
+        "type-check": "~0.3.2",
+        "wordwrap": "~1.0.0"
+      },
+      "dependencies": {
+        "wordwrap": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz",
+          "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=",
+          "dev": true
+        }
+      }
+    },
     "os-browserify": {
       "version": "0.3.0",
       "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
@@ -5869,6 +6377,15 @@
         "readable-stream": "^2.1.5"
       }
     },
+    "parent-module": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz",
+      "integrity": "sha512-8Mf5juOMmiE4FcmzYc4IaiS9L3+9paz2KOiXzkRviCP6aDmN49Hz6EMWz0lGNp9pX80GvvAuLADtyGfW/Em3TA==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
     "parse-asn1": {
       "version": "5.1.3",
       "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.3.tgz",
@@ -5959,6 +6476,12 @@
       "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
       "dev": true
     },
+    "path-is-inside": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
+      "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=",
+      "dev": true
+    },
     "path-key": {
       "version": "2.0.1",
       "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz",
@@ -6101,12 +6624,24 @@
         }
       }
     },
+    "pluralize": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
+      "integrity": "sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow==",
+      "dev": true
+    },
     "posix-character-classes": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
       "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=",
       "dev": true
     },
+    "prelude-ls": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz",
+      "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=",
+      "dev": true
+    },
     "preserve": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/preserve/-/preserve-0.2.0.tgz",
@@ -6114,6 +6649,21 @@
       "dev": true,
       "optional": true
     },
+    "prettier": {
+      "version": "1.16.1",
+      "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.16.1.tgz",
+      "integrity": "sha512-XXUITwIkGb3CPJ2hforHah/zTINRyie5006Jd2HKy2qz7snEJXl0KLfsJZW/wst9g6R2rFvqba3VpNYdu1hDcA==",
+      "dev": true
+    },
+    "prettier-linter-helpers": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
+      "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
+      "dev": true,
+      "requires": {
+        "fast-diff": "^1.1.2"
+      }
+    },
     "pretty-bytes": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-3.0.1.tgz",
@@ -6141,6 +6691,12 @@
       "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==",
       "dev": true
     },
+    "progress": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+      "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+      "dev": true
+    },
     "promise": {
       "version": "7.3.1",
       "resolved": "https://registry.npmjs.org/promise/-/promise-7.3.1.tgz",
@@ -6623,6 +7179,12 @@
         }
       }
     },
+    "regexpp": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz",
+      "integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
+      "dev": true
+    },
     "remove-trailing-separator": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz",
@@ -6744,6 +7306,16 @@
       "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=",
       "dev": true
     },
+    "restore-cursor": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz",
+      "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=",
+      "dev": true,
+      "requires": {
+        "onetime": "^2.0.0",
+        "signal-exit": "^3.0.2"
+      }
+    },
     "ret": {
       "version": "0.1.15",
       "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz",
@@ -6785,6 +7357,15 @@
         "inherits": "^2.0.1"
       }
     },
+    "run-async": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz",
+      "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=",
+      "dev": true,
+      "requires": {
+        "is-promise": "^2.1.0"
+      }
+    },
     "run-queue": {
       "version": "1.0.3",
       "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
@@ -6794,6 +7375,15 @@
         "aproba": "^1.1.1"
       }
     },
+    "rxjs": {
+      "version": "6.4.0",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz",
+      "integrity": "sha512-Z9Yfa11F6B9Sg/BK9MnqnQ+aQYicPLtilXBp2yUtDt2JRCE0h26d33EnfO3ZxoNxG0T92OUucP3Ct7cpfkdFfw==",
+      "dev": true,
+      "requires": {
+        "tslib": "^1.9.0"
+      }
+    },
     "safe-buffer": {
       "version": "5.1.2",
       "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -6944,6 +7534,28 @@
       "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=",
       "dev": true
     },
+    "slice-ansi": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz",
+      "integrity": "sha512-Qu+VC3EwYLldKa1fCxuuvULvSJOKEgk9pi8dZeCVK7TqBfUNTH4sFkk4joj8afVSfAYgJoSOetjx9QWOJ5mYoQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^3.2.0",
+        "astral-regex": "^1.0.0",
+        "is-fullwidth-code-point": "^2.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "3.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz",
+          "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==",
+          "dev": true,
+          "requires": {
+            "color-convert": "^1.9.0"
+          }
+        }
+      }
+    },
     "snapdragon": {
       "version": "0.8.2",
       "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz",
@@ -7309,12 +7921,30 @@
         "get-stdin": "^4.0.1"
       }
     },
+    "strip-json-comments": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz",
+      "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=",
+      "dev": true
+    },
     "supports-color": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz",
       "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=",
       "dev": true
     },
+    "table": {
+      "version": "5.2.2",
+      "resolved": "https://registry.npmjs.org/table/-/table-5.2.2.tgz",
+      "integrity": "sha512-f8mJmuu9beQEDkKHLzOv4VxVYlU68NpdzjbGPl69i4Hx0sTopJuNxuzJd17iV2h24dAfa93u794OnDA5jqXvfQ==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.6.1",
+        "lodash": "^4.17.11",
+        "slice-ansi": "^2.0.0",
+        "string-width": "^2.1.1"
+      }
+    },
     "tapable": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/tapable/-/tapable-1.1.1.tgz",
@@ -7379,6 +8009,18 @@
         }
       }
     },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
+      "dev": true
+    },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
+      "dev": true
+    },
     "through2": {
       "version": "2.0.5",
       "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
@@ -7429,6 +8071,15 @@
         }
       }
     },
+    "tmp": {
+      "version": "0.0.33",
+      "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
+      "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+      "dev": true,
+      "requires": {
+        "os-tmpdir": "~1.0.2"
+      }
+    },
     "to-arraybuffer": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
@@ -7539,6 +8190,15 @@
       "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
       "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
     },
+    "type-check": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz",
+      "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "~1.1.2"
+      }
+    },
     "typedarray": {
       "version": "0.0.6",
       "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
@@ -8075,6 +8735,15 @@
       "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
       "dev": true
     },
+    "write": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/write/-/write-0.2.1.tgz",
+      "integrity": "sha1-X8A4KOJkzqP+kUVUdvejxWbLB1c=",
+      "dev": true,
+      "requires": {
+        "mkdirp": "^0.5.1"
+      }
+    },
     "xtend": {
       "version": "4.0.1",
       "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz",

+ 1 - 1
package.json

@@ -64,7 +64,7 @@
     "watch": "./node_modules/.bin/grunt watch",
     "webpack": "webpack --config webpack.config.js",
     "dev": "webpack --watch -d",
-    "lint": "cd desktop/core/src/desktop/static/desktop/js/; eslint assist autocomplete document",
+    "lint": "cd desktop/core/src/desktop/js/; eslint api/** assist/** catalog/** sql/** utils/**",
     "lint-fix": "npm run lint -- --fix"
   },
   "files": []

+ 1 - 1
webpack-stats.json

@@ -1 +1 @@
-{"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"}]}}
+{"status":"done","chunks":{"hue":[{"name":"hue-bundle-72c7c47509e0d902668b.js","path":"/Users/jahlen/dev/hue/desktop/core/src/desktop/static/desktop/js/hue-bundle-72c7c47509e0d902668b.js"},{"name":"hue-bundle-72c7c47509e0d902668b.js.map","path":"/Users/jahlen/dev/hue/desktop/core/src/desktop/static/desktop/js/hue-bundle-72c7c47509e0d902668b.js.map"}]}}

Some files were not shown because too many files changed in this diff