Ver código fonte

HUE-7820 [assist] Show comment tooltips for entries in the left and right assist

Johan Ahlen 7 anos atrás
pai
commit
1aaebb7

+ 1 - 1
desktop/core/src/desktop/static/desktop/js/assist/assistDbSource.js

@@ -218,7 +218,7 @@ var AssistDbSource = (function () {
     var updateDatabases = function (names, lastSelectedDb) {
       dbIndex = {};
       var dbs = $.map(names, function(name) {
-        var catalogEntry = DataCatalog.getEntry({ sourceType: self.sourceType, path: name });
+        var catalogEntry = DataCatalog.getEntry({ sourceType: self.sourceType, path: name, definition: { type: 'database' }});
 
         var database = new AssistDbEntry(catalogEntry, null, self, nestedFilter, self.i18n, self.navigationSettings);
         dbIndex[name] = database;

+ 46 - 42
desktop/core/src/desktop/static/desktop/js/dataCatalog.js

@@ -28,13 +28,15 @@ var DataCatalog = (function () {
   }
 
   /**
-   * @param {string|string[]} path
+   * @param {Object} options
+   * @param {string|string[]} options.path
+   * @param {Object} [options.definition] - The initial definition if not already set on the entry
    * @return {DataCatalogEntry}
    */
-  DataCatalog.prototype.getEntry = function (path) {
+  DataCatalog.prototype.getEntry = function (options) {
     var self = this;
-    var identifier = typeof path === 'string' ? path : (typeof path === 'string' ? path : path.join('.'));
-    return self.entries[identifier] || (self.entries[identifier] = new DataCatalogEntry(self, path));
+    var identifier = typeof options.path === 'string' ? options.path : (typeof options.path === 'string' ? options.path : options.path.join('.'));
+    return self.entries[identifier] || (self.entries[identifier] = new DataCatalogEntry(self, options.path, options.definition));
   };
 
   /**
@@ -78,8 +80,8 @@ var DataCatalog = (function () {
         cachedOnly: apiOptions && apiOptions.cachedOnly,
         refreshCache: apiOptions && apiOptions.refreshCache
       }).done(function (data) {
-        dataCatalogEntry.navigatorMeta = data;
-        deferred.resolve(data);
+        dataCatalogEntry.navigatorMeta = data.entity || data;
+        deferred.resolve(dataCatalogEntry.navigatorMeta);
       }).fail(deferred.reject);
     } else {
       deferred.reject();
@@ -91,16 +93,17 @@ var DataCatalog = (function () {
   /**
    * @param {DataCatalog} dataCatalog
    * @param {string|string[]} path
+   * @param {Object} definition
    * @constructor
    */
-  function DataCatalogEntry(dataCatalog, path) {
+  function DataCatalogEntry(dataCatalog, path, definition) {
     var self = this;
 
     self.dataCatalog = dataCatalog;
     self.path = typeof path === 'string' && path ? path.split('.') : path || [];
     self.name = self.path.length ? self.path[self.path.length - 1] : dataCatalog.sourceType;
 
-    self.definition = undefined;
+    self.definition = definition;
     self.lastSourceMetaPromise = undefined;
     self.sourceMeta = undefined;
     self.lastNavigatorMetaPromise = undefined;
@@ -131,22 +134,18 @@ var DataCatalog = (function () {
           var entities = sourceMeta.databases || sourceMeta.tables_meta || sourceMeta.extended_columns || sourceMeta.fields;
           if (entities) {
             entities.forEach(function (entity) {
-              var catalogEntry = self.dataCatalog.getEntry(self.path.concat(entity.name || entity));
-              if (!catalogEntry.definition && typeof entity === 'object') {
-                catalogEntry.definition = entity;
-                catalogEntry.definition.index = index++;
-              }
+              var definition = typeof entity === 'object' ? entity : {};
+              definition.index = index++;
+              var catalogEntry = self.dataCatalog.getEntry({ path: self.path.concat(entity.name || entity), definition: definition });
               self.children.push(catalogEntry);
             });
           } else {
             (sourceMeta.type === 'map' ? ['key', 'value'] : ['item']).forEach(function (path) {
               if (sourceMeta[path]) {
-                var catalogEntry = self.dataCatalog.getEntry(self.path.concat(path));
-                if (!catalogEntry.definition) {
-                  catalogEntry.definition = sourceMeta[path];
-                  catalogEntry.definition.index = index++;
-                  catalogEntry.definition.isMapValue = path === 'value'
-                }
+                var definition = sourceMeta[path];
+                definition.index = index++;
+                definition.isMapValue = path === 'value'
+                var catalogEntry = self.dataCatalog.getEntry({ path: self.path.concat(path), definition: definition.isMapValue = path === 'value' });
                 self.children.push(catalogEntry);
               }
             })
@@ -201,8 +200,16 @@ var DataCatalog = (function () {
     return deferred.promise();
   };
 
+  DataCatalogEntry.prototype.getKnownComment = function () {
+    var self = this;
+    if (self.navigatorMeta) {
+      return self.navigatorMeta.description || self.navigatorMeta.originalDescription || ''
+    }
+    return self.sourceMeta && self.sourceMeta.comment || '';
+  };
+
   /**
-   * @param {Object} [apiOptions]
+   * @param {Object|boolean} [apiOptions] -
    * @param {boolean} [apiOptions.silenceErrors]
    * @param {boolean} [apiOptions.cachedOnly]
    * @param {boolean} [apiOptions.refreshCache]
@@ -223,12 +230,12 @@ var DataCatalog = (function () {
     };
 
     if (HAS_NAVIGATOR) {
-      if (self.navigatorMeta && self.navigatorMeta.entity) {
-        deferred.resolve(self.navigatorMeta.entity.description || self.navigatorMeta.entity.originalDescription || '');
+      if (self.navigatorMeta) {
+        deferred.resolve(self.navigatorMeta.description || self.navigatorMeta.originalDescription || '');
       } else {
         self.getNavigatorMeta(apiOptions).done(function (navigatorMeta) {
-          if (navigatorMeta && navigatorMeta.entity) {
-            deferred.resolve(navigatorMeta.entity.description || navigatorMeta.entity.originalDescription || '');
+          if (navigatorMeta) {
+            deferred.resolve(navigatorMeta.description || navigatorMeta.originalDescription || '');
           } else {
             resolveWithSourceMeta();
           }
@@ -255,9 +262,9 @@ var DataCatalog = (function () {
 
     if (HAS_NAVIGATOR) {
       self.getNavigatorMeta(apiOptions).done(function (navigatorMeta) {
-        if (navigatorMeta && navigatorMeta.entity) {
+        if (navigatorMeta) {
           ApiHelper.getInstance().updateNavigatorMetadata({
-            identity: navigatorMeta.entity.identity,
+            identity: navigatorMeta.identity,
             properties: {
               description: comment
             }
@@ -294,8 +301,8 @@ var DataCatalog = (function () {
   DataCatalogEntry.prototype.hasPossibleChildren = function () {
     var self = this;
     return (!self.definition && !self.sourceMeta) ||
-      (self.sourceMeta && /^(?:table|view|struct|array|map)/i.test(self.sourceMeta.type)) ||
-      (self.definition && /^(?:table|view|struct|array|map)/i.test(self.definition.type));
+      (self.sourceMeta && /^(?:database|table|view|struct|array|map)/i.test(self.sourceMeta.type)) ||
+      (self.definition && /^(?:database|table|view|struct|array|map)/i.test(self.definition.type));
   };
 
   DataCatalogEntry.prototype.getIndex = function () {
@@ -313,6 +320,10 @@ var DataCatalog = (function () {
     return self.path.length === 2;
   };
 
+  DataCatalogEntry.prototype.getTooltip = function () {
+    var self = this;
+    return self.getKnownComment() || self.getTitle();
+  };
 
   DataCatalogEntry.prototype.getTitle = function () {
     var self = this;
@@ -320,24 +331,18 @@ var DataCatalog = (function () {
     if (self.isField()) {
       var type = self.getType();
       if (type) {
-        if (~type.indexOf('<')) {
-          type = type.substring(0, type.indexOf('<'));
-        }
         title += ' (' + type + ')';
       }
     }
     return title;
   };
 
-  DataCatalogEntry.prototype.getDisplayName = function () {
+  DataCatalogEntry.prototype.getDisplayName = function (qualified) {
     var self = this;
-    var displayName = self.name;
+    var displayName = qualified ? self.path.join('.') : self.name;
     if (self.isField()) {
       var type = self.getType();
       if (type) {
-        if (~type.indexOf('<')) {
-          type = type.substring(0, type.indexOf('<'));
-        }
         displayName += ' (' + type + ')';
       }
     }
@@ -400,12 +405,11 @@ var DataCatalog = (function () {
 
   DataCatalogEntry.prototype.getType = function () {
     var self = this;
-    if (self.sourceMeta) {
-      return self.sourceMeta.type;
-    }
-    if (self.definition) {
-      return self.definition.type;
+    var type = self.sourceMeta && self.sourceMeta.type || self.definition.type || '';
+    if (~type.indexOf('<')) {
+      type = type.substring(0, type.indexOf('<'));
     }
+    return type;
   };
 
   /**
@@ -445,7 +449,7 @@ var DataCatalog = (function () {
 
   return {
     getEntry: function (options) {
-      return getCatalog(options.sourceType).getEntry(options.path);
+      return getCatalog(options.sourceType).getEntry(options);
     }
   };
 })();

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

@@ -2744,7 +2744,9 @@
 
       if (options.title) {
         var title = ko.unwrap(options.title); // Not always an observable
-        options.title = escapeOutput(title);
+        if (typeof title === 'string') {
+          options.title = escapeOutput(title);
+        }
       }
 
       $(element).tooltip(options);

+ 55 - 37
desktop/core/src/desktop/templates/assist.mako

@@ -203,7 +203,11 @@ from desktop.views import _ko
   <script type="text/html" id="assist-database-entry">
     <li class="assist-table" data-bind="appAwareTemplateContextMenu: { template: 'sql-context-items', scrollContainer: '.assist-db-scrollable' }, visibleOnHover: { selector: '.database-actions' }">
       <!-- ko template: { name: 'assist-database-actions' } --><!-- /ko -->
-      <a class="assist-table-link" href="javascript: void(0);" data-bind="click: function () { $parent.selectedDatabase($data); $parent.selectedDatabaseChanged(); }, attr: {'title': catalogEntry.getTitle() }, draggableText: { text: editorText,  meta: {'type': 'sql', 'database': databaseName} }"><i class="fa fa-fw fa-database muted valign-middle"></i> <span class="highlightable" data-bind="text: catalogEntry.name, css: { 'highlight': highlight() }"></span></a>
+      <a class="assist-table-link" href="javascript: void(0);" data-bind="
+        click: function () { $parent.selectedDatabase($data); $parent.selectedDatabaseChanged(); },
+        draggableText: { text: editorText,  meta: {'type': 'sql', 'database': databaseName} },
+        tooltip: { placement: navigationSettings.rightAssist ? 'left' : 'right', title: catalogEntry.getTooltip.bind(catalogEntry), delay: 500 }
+      "><i class="fa fa-fw fa-database muted valign-middle"></i> <span class="highlightable" data-bind="text: catalogEntry.name, css: { 'highlight': highlight() }"></span></a>
     </li>
   </script>
 
@@ -213,9 +217,12 @@ from desktop.views import _ko
         <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.showStats, click: showContextPopover, css: { 'blue': statsVisible }"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a>
         <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.openItem, click: openItem"><i class="fa fa-long-arrow-right" title="${_('Open')}"></i></a>
       </div>
-      <a class="assist-entry assist-table-link" href="javascript:void(0)" data-bind="click: toggleOpen, attr: {'title': catalogEntry.getTitle() }, draggableText: { text: editorText,  meta: {'type': 'sql', 'isView': catalogEntry.isView(), 'table': tableName, 'database': databaseName} }">
+      <a class="assist-entry assist-table-link" href="javascript:void(0)" data-bind="
+          click: toggleOpen,
+          draggableText: { text: editorText,  meta: {'type': 'sql', 'isView': catalogEntry.isView(), 'table': tableName, 'database': databaseName} },
+          tooltip: { placement: navigationSettings.rightAssist ? 'left' : 'right', title: catalogEntry.getTooltip.bind(catalogEntry), delay: 500 }">
         <i class="fa fa-fw fa-table muted valign-middle" data-bind="css: { 'fa-eye': catalogEntry.isView() && !navigationSettings.rightAssist, 'fa-table': catalogEntry.isTable() && sourceType !== 'solr' && !navigationSettings.rightAssist, 'fa-search': sourceType === 'solr' }"></i>
-        <span class="highlightable" data-bind="text: catalogEntry.getDisplayName(), css: { 'highlight': highlight }"></span> <!-- ko if: assistDbSource.activeSort() === 'popular' && popularity() > 0 --><i title="${ _('Popular') }" class="fa fa-star-o top-star"></i> <!-- /ko -->
+        <span class="highlightable" data-bind="text: catalogEntry.getDisplayName(navigationSettings.rightAssist), css: { 'highlight': highlight }"></span> <!-- ko if: assistDbSource.activeSort() === 'popular' && popularity() > 0 --><i title="${ _('Popular') }" class="fa fa-star-o top-star"></i> <!-- /ko -->
       </a>
       <div class="center assist-spinner" data-bind="visible: loading() && open()"><i class="fa fa-spinner fa-spin"></i></div>
       <!-- ko template: { if: open, name: 'assist-db-entries'  } --><!-- /ko -->
@@ -228,12 +235,20 @@ from desktop.views import _ko
         <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.showStats, click: showContextPopover, css: { 'blue': statsVisible }"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a>
       </div>
       <!-- ko if: expandable -->
-      <a class="assist-entry assist-field-link" href="javascript:void(0)" data-bind="click: toggleOpen, attr: {'title': catalogEntry.getTitle() }, css: { 'assist-entry-left-action': navigationSettings.rightAssist }">
+      <a class="assist-entry assist-field-link" href="javascript:void(0)" data-bind="
+          click: toggleOpen,
+          css: { 'assist-entry-left-action': navigationSettings.rightAssist },
+          tooltip: { placement: navigationSettings.rightAssist ? 'left' : 'right', title: catalogEntry.getTooltip.bind(catalogEntry), delay: 500 }
+        ">
         <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName }, text: catalogEntry.getDisplayName(), draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName } }"></span><!-- ko if: catalogEntry.isPrimaryKey() --> <i class="fa fa-key"></i><!-- /ko -->
       </a>
       <!-- /ko -->
       <!-- ko ifnot: expandable -->
-      <div class="assist-entry assist-field-link default-cursor" href="javascript:void(0)" data-bind="event: { dblclick: dblClick }, attr: {'title': catalogEntry.getTitle() }, css: { 'assist-entry-left-action': navigationSettings.rightAssist }">
+      <div class="assist-entry assist-field-link default-cursor" href="javascript:void(0)" data-bind="
+          event: { dblclick: dblClick },
+          css: { 'assist-entry-left-action': navigationSettings.rightAssist },
+          tooltip: { placement: navigationSettings.rightAssist ? 'left' : 'right', title: catalogEntry.getTooltip.bind(catalogEntry), delay: 500 }
+        ">
         <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName}, text: catalogEntry.getDisplayName(), draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName} }"></span><!-- ko if: catalogEntry.isPrimaryKey()  --> <i class="fa fa-key"></i><!-- /ko --><!-- ko if: assistDbSource.activeSort() === 'popular' && popularity() > 0 --> <i title="${ _('Popular') }" class="fa fa-star-o top-star"></i> <!-- /ko -->
       </div>
       <!-- /ko -->
@@ -248,15 +263,22 @@ from desktop.views import _ko
         <a class="inactive-action" href="javascript:void(0)" data-bind="visible: navigationSettings.showStats, click: showContextPopover, css: { 'blue': statsVisible }"><i class="fa fa-fw fa-info" title="${_('Show details')}"></i></a>
       </div>
       <!-- ko if: expandable -->
-      <a class="assist-entry assist-field-link assist-field-link-dark assist-entry-left-action assist-ellipsis" href="javascript:void(0)" data-bind="click: toggleOpen, attr: {'title': catalogEntry.getTitle() }">
+      <a class="assist-entry assist-field-link assist-field-link-dark assist-entry-left-action assist-ellipsis" href="javascript:void(0)" data-bind="
+          click: toggleOpen,
+          tooltip: { placement: navigationSettings.rightAssist ? 'left' : 'right', title: catalogEntry.getTooltip.bind(catalogEntry), delay: 500 }
+        ">
         <span data-bind="text: catalogEntry.getType()" class="muted pull-right margin-right-20"></span>
         <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName }, text: catalogEntry.name, draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName } }"></span><!-- ko if: catalogEntry.isPrimaryKey() --> <i class="fa fa-key"></i><!-- /ko -->
       </a>
       <!-- /ko -->
       <!-- ko ifnot: expandable -->
-      <div class="assist-entry assist-field-link assist-field-link-dark default-cursor assist-ellipsis" href="javascript:void(0)" data-bind="event: { dblclick: dblClick }, attr: {'title': definition.title }, css: { 'assist-entry-left-action': navigationSettings.rightAssist }">
+      <div class="assist-entry assist-field-link assist-field-link-dark default-cursor assist-ellipsis" href="javascript:void(0)" data-bind="
+          event: { dblclick: dblClick },
+          css: { 'assist-entry-left-action': navigationSettings.rightAssist },
+          tooltip: { placement: navigationSettings.rightAssist ? 'left' : 'right', title: catalogEntry.getTooltip.bind(catalogEntry), delay: 500 }
+        ">
         <span data-bind="text: catalogEntry.getType()" class="muted pull-right margin-right-20"></span>
-        <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName}, text: catalogEntry.getName(), draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName} }"></span><!-- ko if: catalogEntry.isPrimaryKey() --> <i class="fa fa-key"></i><!-- /ko --><!-- ko if: assistDbSource.activeSort() === 'popular' && popularity() > 0 --> <i title="${ _('Popular') }" class="fa fa-star-o top-star"></i> <!-- /ko -->
+        <span class="highlightable" data-bind="css: { 'highlight': highlight}, attr: {'column': columnName, 'table': tableName, 'database': databaseName}, text: catalogEntry.name, draggableText: { text: editorText, meta: {'type': 'sql', 'column': columnName, 'table': tableName, 'database': databaseName} }"></span><!-- ko if: catalogEntry.isPrimaryKey() --> <i class="fa fa-key"></i><!-- /ko --><!-- ko if: assistDbSource.activeSort() === 'popular' && popularity() > 0 --> <i title="${ _('Popular') }" class="fa fa-star-o top-star"></i> <!-- /ko -->
       </div>
       <!-- /ko -->
       <div class="center assist-spinner" data-bind="visible: loading"><i class="fa fa-spinner fa-spin"></i></div>
@@ -2580,41 +2602,37 @@ from desktop.views import _ko
               if (location.type === 'table' && (location.identifierChain.length !== 1 || !ctes[location.identifierChain[0].name.toLowerCase()])) {
                 var database = location.identifierChain.length === 2 ? location.identifierChain[0].name : activeLocations.defaultDatabase;
                 database = database.toLowerCase();
+                var catalogEntry = DataCatalog.getEntry({ sourceType: activeLocations.type, path: [database], definition: { type: 'database' }});
                 if (!databaseIndex[database]) {
                   databaseIndex[database] = new AssistDbEntry(
-                    {
-                      name: database,
-                      type: 'database',
-                      isDatabase: true
-                    },
+                    DataCatalog.getEntry({
+                      sourceType: activeLocations.type,
+                      path: [database],
+                      definition: { type: 'database' }
+                    }),
                     null,
                     assistDbSource,
                     self.filter,
                     i18n,
-                    navigationSettings,
-                    {}
+                    navigationSettings
                   );
                 }
                 var qid = createQualifiedIdentifier(location.identifierChain, activeLocations.defaultDatabase);
                 tableQidIndex[qid] = true;
                 if (!activeTableIndex[qid]) {
                   var tableName = location.identifierChain[location.identifierChain.length - 1].name;
-                  var displayName = database + '.' + tableName;
 
                   activeTableIndex[createQualifiedIdentifier(location.identifierChain, activeLocations.defaultDatabase)] = new AssistDbEntry(
-                    {
-                      name: tableName,
-                      type: 'table',
-                      isTable: true,
-                      displayName: displayName.toLowerCase(),
-                      title: displayName
-                    },
+                    DataCatalog.getEntry({
+                      sourceType: activeLocations.type,
+                      path: [database, tableName],
+                      definition: { type: 'table' }
+                    }),
                     databaseIndex[database],
                     assistDbSource,
                     self.filter,
-                    {},
-                    navigationSettings,
-                    {}
+                    i18n,
+                    navigationSettings
                   );
                   updateTables = true;
                 }
@@ -2901,12 +2919,13 @@ from desktop.views import _ko
             navigationSettings: navigationSettings
           });
 
+          var fakeParentName = collectionName.indexOf('.') > -1 ? collectionName.split('.')[0] : 'default';
           var assistFakeDb = new AssistDbEntry(
-              {
-                name: collectionName.indexOf('.') > -1 ? collectionName.split('.')[0] : 'default',
-                type: 'database',
-                isDatabase: true
-              },
+              DataCatalog.getEntry({
+                sourceType: collection.engine(),
+                path: [fakeParentName],
+                definition: { type: 'database' }
+              }),
               null,
               assistDbSource,
               self.filter,
@@ -2915,12 +2934,11 @@ from desktop.views import _ko
           );
 
           var collectionEntry = new AssistDbEntry(
-              {
-                name: collectionName.indexOf('.') > -1 ? collectionName.split('.')[1] : collectionName,
-                type: 'table',
-                isTable: true,
-                displayName: collectionName.toLowerCase()
-              },
+              DataCatalog.getEntry({
+                sourceType: collection.engine(),
+                path: [fakeParentName, collectionName.indexOf('.') > -1 ? collectionName.split('.')[1] : collectionName],
+                definition: { type: 'table' }
+              }),
               assistFakeDb,
               assistDbSource,
               self.filter,