浏览代码

HUE-7820 [core] Introduce a generic sqlMetadata object

Johan Ahlen 8 年之前
父节点
当前提交
5ed9719

+ 114 - 74
desktop/core/src/desktop/static/desktop/js/apiHelper.js

@@ -1427,50 +1427,6 @@ var ApiHelper = (function () {
     });
   };
 
-  /**
-   * @param {Object} options
-   * @param {string} options.sourceType
-   * @param {Function} options.successCallback
-   * @param {Function} [options.errorCallback]
-   * @param {boolean} [options.silenceErrors]
-   * @param {Number} [options.timeout]
-   * @param {Object} [options.editor] - Ace editor
-   *
-   * @param {string} options.databaseName
-   */
-  ApiHelper.prototype.fetchTables = function (options) {
-    var self = this;
-    return fetchAssistData.bind(self)($.extend({}, options, {
-      url: AUTOCOMPLETE_API_PREFIX + options.databaseName,
-      errorCallback: self.assistErrorCallback(options),
-      cacheCondition: genericCacheCondition
-    }));
-  };
-
-  /**
-   * @param {Object} options
-   * @param {string} options.sourceType
-   * @param {Function} options.successCallback
-   * @param {Function} [options.errorCallback]
-   * @param {boolean} [options.silenceErrors]
-   * @param {Number} [options.timeout]
-   * @param {boolean} [options.cachedOnly] - Default false
-   * @param {Object} [options.editor] - Ace editor
-   *
-   * @param {string} options.databaseName
-   * @param {string} options.tableName
-   * @param {string[]} options.fields
-   */
-  ApiHelper.prototype.fetchFields = function (options) {
-    var self = this;
-    var fieldPart = options.fields.length > 0 ? "/" + options.fields.join("/") : "";
-    return fetchAssistData.bind(self)($.extend({}, options, {
-      url: AUTOCOMPLETE_API_PREFIX + options.databaseName + "/" + options.tableName + fieldPart,
-      errorCallback: self.assistErrorCallback(options),
-      cacheCondition: genericCacheCondition
-    }));
-  };
-
   /**
    * Returns a promise that will always be resolved with:
    *
@@ -1592,13 +1548,14 @@ var ApiHelper = (function () {
    * @param {string} options.defaultDatabase
    * @param {boolean} [options.cachedOnly] - Default false
    *
-   * @param {function} successCallback
+   * @returns {Object} promise
    */
-  ApiHelper.prototype.identifierChainToPath = function (options, successCallback) {
+  ApiHelper.prototype.identifierChainToPath = function (options) {
     var self = this;
+    var promise = $.Deferred();
     if (options.identifierChain.length === 0) {
-      successCallback([options.defaultDatabase]);
-      return;
+      promise.resolve([options.defaultDatabase]);
+      return promise;
     }
 
     var identifierChainClone = options.identifierChain.concat();
@@ -1615,12 +1572,44 @@ var ApiHelper = (function () {
 
       if (identifierChainClone.length > 1) {
         self.expandComplexIdentifierChain(options.sourceType, path[0], identifierChainClone, function (fetchedFields) {
-          successCallback(path.concat(fetchedFields))
+          promise.resolve(path.concat(fetchedFields))
         }, options.errorCallback, options.cachedOnly);
       } else {
-        successCallback(path.concat($.map(identifierChainClone, function (identifier) { return identifier.name })))
+        promise.resolve(path.concat($.map(identifierChainClone, function (identifier) { return identifier.name })))
       }
     });
+    return promise;
+  };
+
+
+  /**
+   * @param {Object} options
+   * @param {string} options.sourceType
+   * @param {boolean} [options.silenceErrors]
+   * @param {boolean} [options.cachedOnly] - Default false
+   *
+   * @param {string[]} [options.path] - The path to fetch
+   *
+   * @return {Deferred} Promise
+   */
+  ApiHelper.prototype.fetchSqlMetadata = function (options) {
+    var self = this;
+    var promise = $.Deferred();
+
+    fetchAssistData.bind(self)({
+      url: AUTOCOMPLETE_API_PREFIX + options.path.join('/'),
+      sourceType: options.sourceType,
+      silenceErrors: options.silenceErrors,
+      cachedOnly: options.cachedOnly,
+      successCallback: promise.resolve,
+      errorCallback: self.assistErrorCallback({
+        errorCallback: promise.reject,
+        silenceErrors: options.silenceErrors
+      }),
+      cacheCondition: genericCacheCondition
+    });
+
+    return promise;
   };
 
   /**
@@ -1637,9 +1626,10 @@ var ApiHelper = (function () {
    * @param {string} options.identifierChain.name
    * @param {string} options.defaultDatabase
    */
+  // TODO: Drop and use fetchSqlMetadata instead
   ApiHelper.prototype.fetchAutocomplete = function (options) {
     var self = this;
-    self.identifierChainToPath(options, function (path) {
+    self.identifierChainToPath(options).done(function (path) {
       fetchAssistData.bind(self)($.extend({}, options, {
         url: AUTOCOMPLETE_API_PREFIX + path.join('/'),
         errorCallback: self.assistErrorCallback(options),
@@ -1648,6 +1638,73 @@ var ApiHelper = (function () {
     });
   };
 
+
+  /**
+   * @param {Object} options
+   * @param {string} options.sourceType
+   * @param {Function} options.successCallback
+   * @param {Function} [options.errorCallback]
+   * @param {boolean} [options.silenceErrors]
+   *
+   * @param {string[]} options.hierarchy
+   */
+  // TODO: Drop and use fetchSqlMetadata instead
+  ApiHelper.prototype.fetchPanelData = function (options) {
+    var self = this;
+    fetchAssistData.bind(self)($.extend({}, options, {
+      url: AUTOCOMPLETE_API_PREFIX + options.hierarchy.join("/"),
+      errorCallback: self.assistErrorCallback(options),
+      cacheCondition: genericCacheCondition
+    }));
+  };
+
+
+  /**
+   * @param {Object} options
+   * @param {string} options.sourceType
+   * @param {Function} options.successCallback
+   * @param {Function} [options.errorCallback]
+   * @param {boolean} [options.silenceErrors]
+   * @param {Number} [options.timeout]
+   * @param {Object} [options.editor] - Ace editor
+   *
+   * @param {string} options.databaseName
+   */
+  // TODO: Drop and use fetchSqlMetadata instead
+  ApiHelper.prototype.fetchTables = function (options) {
+    var self = this;
+    return fetchAssistData.bind(self)($.extend({}, options, {
+      url: AUTOCOMPLETE_API_PREFIX + options.databaseName,
+      errorCallback: self.assistErrorCallback(options),
+      cacheCondition: genericCacheCondition
+    }));
+  };
+
+  /**
+   * @param {Object} options
+   * @param {string} options.sourceType
+   * @param {Function} options.successCallback
+   * @param {Function} [options.errorCallback]
+   * @param {boolean} [options.silenceErrors]
+   * @param {Number} [options.timeout]
+   * @param {boolean} [options.cachedOnly] - Default false
+   * @param {Object} [options.editor] - Ace editor
+   *
+   * @param {string} options.databaseName
+   * @param {string} options.tableName
+   * @param {string[]} options.fields
+   */
+  // TODO: Drop and use fetchSqlMetadata instead
+  ApiHelper.prototype.fetchFields = function (options) {
+    var self = this;
+    var fieldPart = options.fields.length > 0 ? "/" + options.fields.join("/") : "";
+    return fetchAssistData.bind(self)($.extend({}, options, {
+      url: AUTOCOMPLETE_API_PREFIX + options.databaseName + "/" + options.tableName + fieldPart,
+      errorCallback: self.assistErrorCallback(options),
+      cacheCondition: genericCacheCondition
+    }));
+  };
+
   /**
    * @param {Object} options
    * @param {string} options.sourceType
@@ -1663,7 +1720,7 @@ var ApiHelper = (function () {
    */
   ApiHelper.prototype.fetchSamples = function (options) {
     var self = this;
-    self.identifierChainToPath(options, function (path) {
+    self.identifierChainToPath(options).done(function (path) {
       fetchAssistData.bind(self)($.extend({}, options, {
         url: SAMPLE_API_PREFIX + path.join('/'),
         errorCallback: self.assistErrorCallback(options),
@@ -1753,24 +1810,6 @@ var ApiHelper = (function () {
     });
   };
 
-  /**
-   * @param {Object} options
-   * @param {string} options.sourceType
-   * @param {Function} options.successCallback
-   * @param {Function} [options.errorCallback]
-   * @param {boolean} [options.silenceErrors]
-   *
-   * @param {string[]} options.hierarchy
-   */
-  ApiHelper.prototype.fetchPanelData = function (options) {
-    var self = this;
-    fetchAssistData.bind(self)($.extend({}, options, {
-      url: AUTOCOMPLETE_API_PREFIX + options.hierarchy.join("/"),
-      errorCallback: self.assistErrorCallback(options),
-      cacheCondition: genericCacheCondition
-    }));
-  };
-
   ApiHelper.prototype.getClusterConfig = function (data) {
     return $.post(FETCH_CONFIG, data);
   };
@@ -2215,10 +2254,10 @@ var ApiHelper = (function () {
       })
       .fail(self.assistErrorCallback(options))
       .always(function () {
-      if (typeof options.editor !== 'undefined' && options.editor !== null) {
-        options.editor.hideSpinner();
-      }
-    });
+        if (typeof options.editor !== 'undefined' && options.editor !== null) {
+          options.editor.hideSpinner();
+        }
+      });
 
     if (!firstInQueue) {
       return;
@@ -2241,6 +2280,7 @@ var ApiHelper = (function () {
       timeout: options.timeout
     }).success(function (data) {
       data.notFound = data.status === 0 && data.code === 500 && data.error && (data.error.indexOf('Error 10001') !== -1 || data.error.indexOf('AnalysisException') !== -1);
+
       // TODO: Display warning in autocomplete when an entity can't be found
       // Hive example: data.error: [...] SemanticException [Error 10001]: Table not found default.foo
       // Impala example: data.error: [...] AnalysisException: Could not resolve path: 'default.foo'

+ 69 - 76
desktop/core/src/desktop/static/desktop/js/assist/assistDbEntry.js

@@ -38,22 +38,30 @@ var AssistDbEntry = (function () {
    */
   function AssistDbEntry (definition, parent, assistDbSource, filter, i18n, navigationSettings) {
     var self = this;
-    self.i18n = i18n;
     self.definition = definition;
-    self.assistDbSource = assistDbSource;
-    self.sortFunctions = assistDbSource.sortFunctions;
     self.parent = parent;
+    self.assistDbSource = assistDbSource;
     self.filter = filter;
-    self.filterColumnNames = ko.observable(false);
+    self.i18n = i18n;
+    self.navigationSettings = navigationSettings;
+
+    self.sourceType = assistDbSource.sourceType;
+    self.invalidateOnRefresh =  assistDbSource.invalidateOnRefresh;
+    self.sortFunctions = assistDbSource.sortFunctions;
     self.isSearchVisible = assistDbSource.isSearchVisible;
-    self.sourceType = self.assistDbSource.sourceType;
-    self.invalidateOnRefresh =  self.assistDbSource.invalidateOnRefresh;
-    self.highlight = ko.observable(false);
-    self.activeSort = self.assistDbSource.activeSort;
-    self.popularity = ko.observable(0);
+    self.activeSort = assistDbSource.activeSort;
 
     self.expandable = typeof definition.type === "undefined" || /table|view|struct|array|map/i.test(definition.type);
 
+    self.metadata = new SqlMetadata({
+      sourceType: self.sourceType,
+      path: self.getHierarchy()
+    });
+
+    self.filterColumnNames = ko.observable(false);
+    self.highlight = ko.observable(false);
+    self.popularity = ko.observable(0);
+
     self.loaded = false;
     self.loading = ko.observable(false);
     self.open = ko.observable(false);
@@ -62,8 +70,6 @@ var AssistDbEntry = (function () {
 
     self.hasErrors = ko.observable(false);
 
-    self.navigationSettings = navigationSettings;
-
     self.open.subscribe(function(newValue) {
       if (newValue && self.entries().length == 0) {
         self.loadEntries();
@@ -311,21 +317,16 @@ var AssistDbEntry = (function () {
 
     var loadEntriesDeferred = $.Deferred();
 
-    var successCallback = function(data) {
+    var successCallback = function(sqlMeta) {
       self.entries([]);
       self.hasErrors(false);
       self.loading(false);
       self.loaded = true;
 
-      if (data.status === 0 && data.code === 500 && !data.tables_meta) {
-        self.hasErrors(true);
-        return;
-      }
-
       var newEntries = [];
       var index = 0;
-      if (typeof data.tables_meta !== "undefined") {
-        newEntries = $.map(data.tables_meta, function(table) {
+      if (typeof sqlMeta.meta.tables_meta !== "undefined") {
+        newEntries = $.map(sqlMeta.meta.tables_meta, function(table) {
           table.index = index++;
           table.title = table.name + (table.comment ? ' - ' + table.comment : '');
           table.displayName = table.name;
@@ -333,8 +334,8 @@ var AssistDbEntry = (function () {
           table.isView = /view/i.test(table.type);
           return self.createEntry(table);
         });
-      } else if (typeof data.extended_columns !== "undefined" && data.extended_columns !== null) {
-        newEntries = $.map(data.extended_columns, function (columnDef) {
+      } else if (typeof sqlMeta.meta.extended_columns !== "undefined" && sqlMeta.meta.extended_columns !== null) {
+        newEntries = $.map(sqlMeta.meta.extended_columns, function (columnDef) {
           var displayName = columnDef.name;
           if (typeof columnDef.type !== "undefined" && columnDef.type !== null) {
             displayName += ' (' + columnDef.type + ')'
@@ -354,8 +355,8 @@ var AssistDbEntry = (function () {
           columnDef.type = shortType;
           return self.createEntry(columnDef);
         });
-      } else if (typeof data.columns !== "undefined" && data.columns !== null) {
-        newEntries = $.map(data.columns, function(columnName) {
+      } else if (typeof sqlMeta.meta.columns !== "undefined" && sqlMeta.meta.columns !== null) {
+        newEntries = $.map(sqlMeta.meta.columns, function(columnName) {
           return self.createEntry({
             name: columnName,
             index: index++,
@@ -364,61 +365,59 @@ var AssistDbEntry = (function () {
             isColumn: true
           });
         });
-      } else if (typeof data.type !== "undefined" && data.type !== null) {
-        if (data.type === "map") {
-          newEntries = [
-            self.createEntry({
-              name: "key",
-              index: index++,
-              displayName: "key (" + data.key.type + ")",
-              title: "key (" + data.key.type + ")",
-              type: data.key.type,
-              isComplex: true
-            }),
-            self.createEntry({
-              name: "value",
-              index: index++,
-              displayName: "value (" + data.value.type + ")",
-              title: "value (" + data.value.type + ")",
-              isMapValue: true,
-              type: data.value.type,
-              isComplex: true
-            })
-          ];
-        } else if (data.type == "struct") {
-          newEntries = $.map(data.fields, function(field) {
-            return self.createEntry({
-              name: field.name,
-              index: index++,
-              displayName: field.name + " (" + field.type + ")",
-              title: field.name + " (" + field.type + ")",
-              type: field.type,
-              isComplex: true
-            });
+      } else if (sqlMeta.isMap()) {
+        newEntries = [
+          self.createEntry({
+            name: "key",
+            index: index++,
+            displayName: "key (" + sqlMeta.meta.key.type + ")",
+            title: "key (" + sqlMeta.meta.key.type + ")",
+            type: sqlMeta.meta.key.type,
+            isComplex: true
+          }),
+          self.createEntry({
+            name: "value",
+            index: index++,
+            displayName: "value (" + sqlMeta.meta.value.type + ")",
+            title: "value (" + sqlMeta.meta.value.type + ")",
+            isMapValue: true,
+            type: sqlMeta.meta.value.type,
+            isComplex: true
+          })
+        ];
+      } else if (sqlMeta.isStruct()) {
+        newEntries = $.map(sqlMeta.meta.fields, function(field) {
+          return self.createEntry({
+            name: field.name,
+            index: index++,
+            displayName: field.name + " (" + field.type + ")",
+            title: field.name + " (" + field.type + ")",
+            type: field.type,
+            isComplex: true
           });
-        } else if (data.type == "array") {
-          newEntries = [
-            self.createEntry({
-              name: "item",
-              index: index++,
-              displayName: "item (" + data.item.type + ")",
-              title: "item (" + data.item.type + ")",
-              isArray: true,
-              type: data.item.type,
-              isComplex: true
-            })
-          ];
-        }
+        });
+      } else if (sqlMeta.isArray()) {
+        newEntries = [
+          self.createEntry({
+            name: "item",
+            index: index++,
+            displayName: "item (" + sqlMeta.meta.item.type + ")",
+            title: "item (" + sqlMeta.meta.item.type + ")",
+            isArray: true,
+            type: sqlMeta.meta.item.type,
+            isComplex: true
+          })
+        ];
       }
 
-
-      if (data.type === 'array' || data.type === 'map') {
+      if (sqlMeta.isArray() || sqlMeta.isMap()) {
         self.entries(newEntries);
         self.entries()[0].open(true);
       } else {
         newEntries.sort(self.sortFunctions[self.assistDbSource.activeSort()]);
         self.entries(newEntries);
       }
+
       loadEntriesDeferred.resolve(newEntries);
       if (typeof callback === 'function') {
         callback();
@@ -462,13 +461,7 @@ var AssistDbEntry = (function () {
       })
     }
 
-    self.assistDbSource.apiHelper.fetchPanelData({
-      sourceType: self.assistDbSource.sourceType,
-      hierarchy: self.getHierarchy(),
-      successCallback: successCallback,
-      errorCallback: errorCallback,
-      silenceErrors: self.navigationSettings.rightAssist || !!silenceErrors
-    });
+    self.metadata.load(self.navigationSettings.rightAssist || !!silenceErrors, false).done(successCallback).fail(errorCallback);
   };
 
   /**

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

@@ -4082,7 +4082,7 @@
                 identifierChain: token.parseLocation.identifierChain,
                 sourceType: self.snippet.type(),
                 defaultDatabase: self.snippet.database()
-              }, function (path) {
+              }).done(function (path) {
                 token.qualifiedIdentifier = path.join('.');
               })
             }

+ 67 - 0
desktop/core/src/desktop/static/desktop/js/sqlMetadata.js

@@ -0,0 +1,67 @@
+// Licensed to Cloudera, Inc. under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  Cloudera, Inc. licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+var SqlMetadata = (function () {
+
+  function SqlMetadata (options) {
+    var self = this;
+    self.loaded = false;
+    self.hasErrors = false;
+
+    self.sourceType = options.sourceType;
+    self.path = options.path;
+
+    self.meta;
+  }
+
+  SqlMetadata.prototype.isMap = function () {
+    var self = this;
+    return self.meta && self.meta.type === 'map';
+  };
+
+  SqlMetadata.prototype.isStruct = function () {
+    var self = this;
+    return self.meta && self.meta.type === 'struct';
+  };
+
+  SqlMetadata.prototype.isArray = function () {
+    var self = this;
+    return self.meta && self.meta.type === 'array';
+  };
+
+  SqlMetadata.prototype.load = function (silenceErrors, cachedOnly) {
+    var self = this;
+    var promise = $.Deferred();
+    ApiHelper.getInstance().fetchSqlMetadata({
+      sourceType: self.sourceType,
+      path: self.path,
+      silenceErrors: silenceErrors,
+      cachedOnly: cachedOnly
+    })
+    .done(function (data) {
+      self.meta = data;
+      self.loaded = true;
+      promise.resolve(self);
+    }).fail(function (message) {
+      self.hasErrors = true;
+      promise.reject(message);
+    });
+
+    return promise;
+  };
+
+  return SqlMetadata;
+})();

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

@@ -211,6 +211,7 @@ if USE_NEW_EDITOR.get():
 
   ${ commonHeaderFooterComponents.header_pollers(user, is_s3_enabled, apps) }
 
+  <script src="${ static('desktop/js/sqlMetadata.js') }"></script>
   <script src="${ static('desktop/js/apiHelper.js') }"></script>
   <script src="${ static('desktop/js/clusterConfig.js') }"></script>
 

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

@@ -532,6 +532,7 @@ ${ commonshare() | n,unicode }
 <script src="${ static('desktop/js/jquery.tableextender2.js') }"></script>
 % endif
 <script src="${ static('desktop/js/hue.colors.js') }"></script>
+<script src="${ static('desktop/js/sqlMetadata.js') }"></script>
 <script src="${ static('desktop/js/apiHelper.js') }"></script>
 <script src="${ static('desktop/ext/js/knockout-sortable.min.js') }"></script>
 <script src="${ static('desktop/ext/js/knockout.validation.min.js') }"></script>

+ 1 - 0
desktop/core/src/desktop/templates/jasmineRunner.html

@@ -66,6 +66,7 @@
   <script type="text/javascript" src="../static/desktop/js/ace.extended.js"></script>
   <script type="text/javascript" src="../static/desktop/ext/js/knockout.min.js"></script>
   <script type="text/javascript" src="../static/desktop/ext/js/knockout-mapping.min.js"></script>
+  <script type="text/javascript" src="../static/desktop/js/sqlMetadata.js"></script>
   <script type="text/javascript" src="../static/desktop/js/apiHelper.js"></script>
   <script type="text/javascript" src="../static/desktop/spec/autocompleterTestUtils.js"></script>
 

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

@@ -803,7 +803,7 @@ from metadata.conf import has_navigator
           sourceType: sourceType,
           defaultDatabase: defaultDatabase,
           identifierChain: data.identifierChain
-        }, function (path) {
+        }).done(function (path) {
           var showInAssistPubSub = huePubSub.subscribe('context.popover.show.in.assist', function () {
             huePubSub.publish('assist.db.highlight', {
               sourceType: sourceType,
@@ -1239,7 +1239,7 @@ from metadata.conf import has_navigator
           sourceType: 'solr',
           identifierChain: data.identifierChain,
           defaultDatabase: 'default'
-        }, function (path) {
+        }).done(function (path) {
           var showInAssistPubSub = huePubSub.subscribe('context.popover.show.in.assist', function () {
             huePubSub.publish('assist.db.highlight', {
               sourceType: 'solr',
@@ -1648,7 +1648,7 @@ from metadata.conf import has_navigator
             sourceType: self.sourceType,
             identifierChain: self.data.identifierChain,
             defaultDatabase: self.defaultDatabase
-          }, function (path) {
+          }).done(function (path) {
 
             var showInMetastorePubSub = huePubSub.subscribe('context.popover.open.in.metastore', function (type) {
               if (IS_HUE_4) {