Эх сурвалжийг харах

HUE-8638 [frontend] Add the ability to create temporary tables in the data catalog

This will be used to support autocompletion of tables that haven't been created yet.
Johan Ahlen 7 жил өмнө
parent
commit
3917cc89dc

+ 159 - 1
desktop/core/src/desktop/static/desktop/js/dataCatalog.js

@@ -117,6 +117,7 @@ var DataCatalog = (function () {
       var self = this;
       self.sourceType = sourceType;
       self.entries = {};
+      self.temporaryEntries = {};
       self.multiTableEntries = {};
       self.store = localforage.createInstance({
         name: 'HueDataCatalog_' + self.sourceType + '_' + STORAGE_POSTFIX
@@ -371,6 +372,135 @@ var DataCatalog = (function () {
       return self.entries[generateEntryCacheId(options)];
     };
 
+    /**
+     * Adds a temporary table to the data catalog. This would allow autocomplete etc. of tables that haven't
+     * been created yet.
+     *
+     * Calling this returns a handle that allows deletion of any created entries by calling delete() on the handle.
+     *
+     * @param {Object} options
+     * @param {string} options.name
+     * @param {ContextNamespace} options.namespace - The context namespace
+     * @param {ContextCompute} options.compute - The context compute
+     *
+     * @param {Object[]} options.columns
+     * @param {string} options.columns[].name
+     * @param {string} options.columns[].type
+     *
+     * @return {Object}
+     */
+    DataCatalog.prototype.addTemporaryTable = function (options) {
+      var self = this;
+      var tableDeferred = $.Deferred();
+      var path = ['default', options.name];
+
+      var identifiersToClean = [];
+
+      var databaseIdentifier = generateEntryCacheId({
+        namespace: options.namespace,
+        path: ['default']
+      });
+
+      if (!self.temporaryEntries[databaseIdentifier]) {
+        var databaseDeferred = $.Deferred();
+        self.temporaryEntries[databaseIdentifier] = databaseDeferred.promise();
+        var databaseEntry = new DataCatalogEntry({
+          dataCatalog: self,
+          namespace: options.namespace,
+          compute: options.compute,
+          path: path,
+          definition: {
+            index: 0,
+            navOptLoaded: true,
+            type: 'database'
+          }
+        });
+        identifiersToClean.push(databaseIdentifier);
+        databaseEntry.childrenPromise = $.Deferred().resolve([]).promise();
+        databaseDeferred.resolve(databaseEntry);
+      }
+
+      var removeTable = function () {}; // noop until actually added
+
+      self.temporaryEntries[databaseIdentifier].done(function (databaseEntry) {
+        databaseEntry.getChildren().done(function (existingTemporaryTables) {
+          var tableIdentifier = generateEntryCacheId({
+            namespace: options.namespace,
+            path: path
+          });
+          self.temporaryEntries[tableIdentifier] = tableDeferred.promise();
+          identifiersToClean.push(tableIdentifier);
+
+          var tableEntry = new DataCatalogEntry({
+            dataCatalog: self,
+            namespace: options.namespace,
+            compute: options.compute,
+            path: path,
+            definition: {
+              comment: '',
+              index: 0,
+              name: options.name,
+              navOptLoaded: true,
+              type: 'table'
+            }
+          });
+          existingTemporaryTables.push(tableEntry);
+          var indexToDelete = existingTemporaryTables.length - 1;
+          removeTable = function () { existingTemporaryTables.splice(indexToDelete, 1); };
+
+          var childrenDeferred = $.Deferred();
+          tableEntry.childrenPromise = childrenDeferred.promise();
+
+          if (options.columns) {
+            var childEntries = [];
+            var index = 0;
+            options.columns.forEach(function (column) {
+              var columnPath = path.concat(column.name);
+              var columnIdentifier = generateEntryCacheId({
+                namespace: options.namespace,
+                path: columnPath
+              });
+
+              var columnDeferred = $.Deferred();
+              self.temporaryEntries[columnIdentifier] = columnDeferred.promise();
+              identifiersToClean.push(columnIdentifier);
+
+              var columnEntry = new DataCatalogEntry({
+                dataCatalog: self,
+                namespace: options.namespace,
+                compute: options.compute,
+                path: columnPath,
+                definition: {
+                  comment: '',
+                  index: index++,
+                  name: column.name,
+                  partitionKey: false,
+                  type: column.type
+                }
+              });
+              columnDeferred.resolve(columnEntry);
+
+              childEntries.push(columnEntry)
+            });
+            childrenDeferred.resolve(childEntries);
+          } else {
+            childrenDeferred.resolve([]);
+          }
+
+          tableDeferred.resolve(tableEntry);
+        });
+      });
+
+      return {
+        delete: function () {
+          removeTable();
+          while (identifiersToClean.length) {
+            delete self.entries[identifiersToClean.pop()];
+          }
+        }
+      }
+    };
+
     /**
      * @param {Object} options
      * @param {string|string[]} options.path
@@ -378,11 +508,15 @@ var DataCatalog = (function () {
      * @param {ContextCompute} options.compute - The context compute
      * @param {Object} [options.definition] - The initial definition if not already set on the entry
      * @param {boolean} [options.cachedOnly] - Default: false
+     * @param {boolean} [options.temporaryOnly] - Default: false
      * @return {Promise}
      */
     DataCatalog.prototype.getEntry = function (options) {
       var self = this;
       var identifier = generateEntryCacheId(options);
+      if (options.temporaryOnly) {
+        return self.temporaryEntries[identifier] || $.Deferred().reject().promise();
+      }
       if (self.entries[identifier]) {
         return self.entries[identifier];
       }
@@ -696,7 +830,7 @@ var DataCatalog = (function () {
      * @param {Object} options
      * @param {string} [options.invalidate] - 'cache', 'invalidate' or 'invalidateAndFlush', default 'cache', only used for Impala
      * @param {boolean} [options.cascade] - Default false, only used when the entry is for the source
-     * @param {boolean [options.silenceErrors] - Default false
+     * @param {boolean} [options.silenceErrors] - Default false
      * @return {CancellablePromise}
      */
     DataCatalogEntry.prototype.clearCache = function (options) {
@@ -2177,6 +2311,29 @@ var DataCatalog = (function () {
 
     return {
 
+      /**
+       * Adds a detached (temporary) entry to the data catalog. This would allow autocomplete etc. of tables that haven't
+       * been created yet.
+       *
+       * Calling this returns a handle that allows deletion of any created entries by calling delete() on the handle.
+       *
+       * @param {Object} options
+       * @param {string} options.sourceType
+       * @param {ContextNamespace} options.namespace - The context namespace
+       * @param {ContextCompute} options.compute - The context compute
+       * @param {string} options.name
+       *
+       * @param {Object[]} options.columns
+       * @param {string} options.columns[].name
+       * @param {string} options.columns[].type
+       * @param options
+       *
+       * @return {Object}
+       */
+      addTemporaryTable: function (options) {
+        return getCatalog(options.sourceType).addTemporaryTable(options);
+      },
+
       /**
        * @param {Object} options
        * @param {string} options.sourceType
@@ -2184,6 +2341,7 @@ var DataCatalog = (function () {
        * @param {ContextCompute} options.compute - The context compute
        * @param {string|string[]} options.path
        * @param {Object} [options.definition] - Optional initial definition
+       * @param {boolean} [options.temporaryOnly] - Default: false
        *
        * @return {Promise}
        */