Procházet zdrojové kódy

HUE-5189 [core] Add Hbase to Assist

Enrico Berti před 9 roky
rodič
revize
cb296dc

+ 53 - 3
desktop/core/src/desktop/static/desktop/js/apiHelper.js

@@ -32,6 +32,7 @@ var ApiHelper = (function () {
   var NAV_LIST_TAGS_API = '/metadata/api/navigator/list_tags';
   var NAV_FIND_ENTITY_API = '/metadata/api/navigator/find_entity';
   var SOLR_COLLECTIONS_API = '/indexer/api/collections/';
+  var HBASE_API_PREFIX = '/hbase/api/';
 
   /**
    * @param {Object} i18n
@@ -64,6 +65,10 @@ var ApiHelper = (function () {
     huePubSub.subscribe('assist.clear.collections.cache', function () {
       $.totalStorage("hue.assist." + self.getTotalStorageUserPrefix('collections'), {});
     });
+
+    huePubSub.subscribe('assist.clear.hbase.cache', function () {
+      $.totalStorage("hue.assist." + self.getTotalStorageUserPrefix('hbase'), {});
+    });
   }
 
   ApiHelper.prototype.hasExpired = function (timestamp) {
@@ -346,9 +351,6 @@ var ApiHelper = (function () {
    * @param {Number} [options.timeout]
    * @param {Object} [options.editor] - Ace editor
    *
-   * @param {string[]} options.pathParts
-   * @param {number} [options.pageSize] - Default 500
-   * @param {number} [options.page] - Default 1
    */
   ApiHelper.prototype.fetchSolrCollections = function (options) {
     var self = this;
@@ -386,6 +388,54 @@ var ApiHelper = (function () {
     }));
   };
 
+  /**
+   * @param {Object} options
+   * @param {Function} options.successCallback
+   * @param {Function} [options.errorCallback]
+   * @param {boolean} [options.silenceErrors]
+   * @param {Number} [options.timeout]
+   * @param {Object} [options.editor] - Ace editor
+   */
+  ApiHelper.prototype.fetchHBase = function (options) {
+    var self = this;
+    var suffix = 'getClusters';
+    if (options.parent.name !== '') {
+      suffix = 'getTableList/' + options.parent.name;
+    }
+    var url = HBASE_API_PREFIX + suffix;
+    var fetchFunction = function (storeInCache) {
+      if (options.timeout === 0) {
+        self.assistErrorCallback(options)({ status: -1 });
+        return;
+      }
+      $.ajax({
+        dataType: "json",
+        url: url,
+        timeout: options.timeout,
+        success: function (data) {
+          if (!data.error && !self.successResponseIsError(data)) {
+            storeInCache(data);
+            options.successCallback(data);
+          } else {
+            self.assistErrorCallback(options)(data);
+          }
+        }
+      })
+      .fail(self.assistErrorCallback(options))
+      .always(function () {
+        if (typeof options.editor !== 'undefined' && options.editor !== null) {
+          options.editor.hideSpinner();
+        }
+      });
+    };
+
+    fetchCached.bind(self)($.extend({}, options, {
+      sourceType: 'hbase',
+      url: url,
+      fetchFunction: fetchFunction
+    }));
+  };
+
   ApiHelper.prototype.getFetchQueue = function (fetchFunction, identifier) {
     var self = this;
     if (! self.fetchQueue[fetchFunction]) {

+ 90 - 0
desktop/core/src/desktop/static/desktop/js/assist/assistHbaseEntry.js

@@ -0,0 +1,90 @@
+// 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 AssistHBaseEntry = (function () {
+
+  /**
+   * @param {object} options
+   * @param {object} options.definition
+   * @param {string} options.definition.name
+   * @param {ApiHelper} options.apiHelper
+   * @constructor
+   */
+  function AssistHBaseEntry (options) {
+    var self = this;
+
+    self.definition = options.definition;
+    self.apiHelper = options.apiHelper;
+    self.path = self.definition.name;
+
+    self.entries = ko.observableArray([]);
+
+    self.loaded = false;
+    self.loading = ko.observable(false);
+    self.hasErrors = ko.observable(false);
+
+    self.hasEntries = ko.computed(function() {
+      return self.entries().length > 0;
+    });
+  }
+
+  AssistHBaseEntry.prototype.loadEntries = function(callback) {
+    var self = this;
+    if (self.loading()) {
+      return;
+    }
+    self.loading(true);
+    self.hasErrors(false);
+
+    var successCallback = function(data) {
+      self.entries($.map(data.data, function (obj) {
+        return new AssistHBaseEntry({
+          definition: obj,
+          apiHelper: self.apiHelper
+        })
+      }));
+      self.loaded = true;
+      self.loading(false);
+      if (callback) {
+        callback();
+      }
+    };
+
+    var errorCallback = function () {
+      self.hasErrors(true);
+      self.loading(false);
+      if (callback) {
+        callback();
+      }
+    };
+
+    self.apiHelper.fetchHBase({
+      parent: self.definition,
+      successCallback: successCallback,
+      errorCallback: errorCallback
+    })
+  };
+
+  AssistHBaseEntry.prototype.click = function () {
+    huePubSub.publish('assist.clickHBaseItem', this);
+  };
+
+  AssistHBaseEntry.prototype.dblClick = function () {
+    huePubSub.publish('assist.dblClickHBaseItem', this);
+  };
+
+  return AssistHBaseEntry;
+})();

+ 141 - 16
desktop/core/src/desktop/templates/assist.mako

@@ -29,6 +29,7 @@ from metadata.conf import has_navigator
 <script src="${ static('desktop/js/assist/assistHdfsEntry.js') }"></script>
 <script src="${ static('desktop/js/assist/assistS3Entry.js') }"></script>
 <script src="${ static('desktop/js/assist/assistCollectionEntry.js') }"></script>
+<script src="${ static('desktop/js/assist/assistHBaseEntry.js') }"></script>
 <script src="${ static('desktop/js/document/hueDocument.js') }"></script>
 <script src="${ static('desktop/js/document/hueFileEntry.js') }"></script>
 </%def>
@@ -903,6 +904,71 @@ from metadata.conf import has_navigator
     </div>
   </script>
 
+  <script type="text/html" id="assist-hbase-header-actions">
+    <div class="assist-db-header-actions" style="margin-top: -1px;">
+      <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.hbase.refresh'); }"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }" title="${_('Manual refresh')}"></i></a>
+    </div>
+  </script>
+
+  <script type="text/html" id="assist-hbase-inner-panel">
+    <div class="assist-inner-panel">
+      <div class="assist-flex-panel">
+        <!-- ko with: selectedHBaseEntry -->
+        <div class="assist-inner-header assist-breadcrumb" >
+          <!-- ko if: definition.host !== '' -->
+          <a href="javascript: void(0);" data-bind="click: function () { huePubSub.publish('assist.clickHBaseRootItem'); }">
+            <i class="fa fa-chevron-left" style="font-size: 15px;margin-right:8px;"></i>
+            <i class="fa fa-th-large" style="font-size: 14px; line-height: 16px; vertical-align: top; margin-right:4px;"></i>
+            <span style="font-size: 14px;line-height: 16px;vertical-align: top;" data-bind="text: definition.name"></span>
+          </a>
+          <!-- /ko -->
+          <!-- ko if: definition.host === '' -->
+          ${_('Clusters')}
+          <!-- /ko -->
+
+          <!-- ko template: 'assist-hbase-header-actions' --><!-- /ko -->
+        </div>
+        <div class="assist-flex-fill assist-hbase-scrollable">
+          <div data-bind="visible: ! loading() && ! hasErrors()" style="position: relative;">
+            <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 20, container: '.assist-hbase-scrollable' }">
+              <li class="assist-entry assist-table-link" style="position: relative;" data-bind="visibleOnHover: { 'selector': '.assist-actions' }">
+                <a href="javascript:void(0)" class="assist-entry assist-table-link" data-bind="multiClick: { click: click, dblClick: dblClick }, attr: {'title': definition.name }">
+                  <!-- ko if: definition.host -->
+                  <i class="fa fa-fw fa-th-large muted valign-middle"></i>
+                  <!-- /ko -->
+                  <!-- ko ifnot: definition.host -->
+                  <i class="fa fa-fw fa-th muted valign-middle"></i>
+                  <!-- /ko -->
+                  <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\'' + definition.name + '\'', meta: {'type': 'collection', 'definition': definition} }"></span>
+                </a>
+              </li>
+            </ul>
+            <!-- ko if: !loading() && entries().length === 0 -->
+            <ul class="assist-tables">
+              <li class="assist-entry" style="font-style: italic;">
+                <!-- ko if: definition.host === '' -->
+                ${_('No clusters available.')}
+                <!-- /ko -->
+                <!-- ko if: definition.host !== '' -->
+                ${_('No tables available.')}
+                <!-- /ko -->
+              </li>
+            </ul>
+            <!-- /ko -->
+          </div>
+          <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
+          <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
+            <span>${ _('Error loading contents.') }</span>
+          </div>
+        </div>
+        <!-- /ko -->
+      </div>
+      <!-- ko with: $parents[1] -->
+      <!-- ko template: { if: searchActive() && searchInput() !== '' && navigatorEnabled(), name: 'nav-search-result' } --><!-- /ko -->
+      <!-- /ko -->
+    </div>
+  </script>
+
   <script type="text/html" id="assist-sources-template">
     <div class="assist-flex-header">
       <div class="assist-inner-header">
@@ -1519,6 +1585,53 @@ from metadata.conf import has_navigator
         });
       }
 
+      /**
+       * @param {Object} options
+       * @param {ApiHelper} options.apiHelper
+       * @constructor
+       **/
+      function AssistHBasePanel(options) {
+        var self = this;
+        self.apiHelper = options.apiHelper;
+
+        var root = new AssistHBaseEntry({
+          definition: {
+            host: '',
+            name: '',
+            port: 0
+          },
+          apiHelper: self.apiHelper
+        });
+
+        self.selectedHBaseEntry = ko.observable();
+        var reload = function () {
+          self.selectedHBaseEntry(root);
+          root.loadEntries();
+        };
+
+        reload();
+
+        huePubSub.subscribe('assist.clickHBaseItem', function (entry) {
+          if (entry.definition.host) {
+            entry.loadEntries();
+            self.selectedHBaseEntry(entry);
+          }
+        });
+
+        huePubSub.subscribe('assist.clickHBaseRootItem', function (entry) {
+          reload();
+        });
+
+        huePubSub.subscribe('assist.dblClickHBaseItem', function (entry) {
+          window.open('/hbase/#' + self.selectedHBaseEntry().definition.name + '/' + entry.definition.name);
+        });
+
+        huePubSub.subscribe('assist.hbase.refresh', function () {
+          huePubSub.publish('assist.clear.hbase.cache');
+          reload();
+        });
+      }
+
       var NAV_FACET_ICON = 'fa-tags';
       var NAV_TYPE_ICONS = {
         'DATABASE': 'fa-database',
@@ -1532,7 +1645,8 @@ from metadata.conf import has_navigator
         'DIRECTORY': 'fa-folder-o',
         'FILE': 'fa-file-o',
         'SUB_OPERATION': 'fa-code-fork',
-        'COLLECTION': 'fa-search'
+        'COLLECTION': 'fa-search',
+        'HBASE': 'fa-th-large'
       };
 
       /**
@@ -1556,9 +1670,9 @@ from metadata.conf import has_navigator
           errorLoadingDatabases: "${ _('There was a problem loading the databases') }",
           errorLoadingTablePreview: "${ _('There was a problem loading the table preview.') }",
           documentTypes: {
-            'query-hive' : "${ _('Hive Query') }",
-            'query' : "${ _('Query') }",
-            'notebook' : "${ _('Notebook') }"
+            'query-hive': "${ _('Hive Query') }",
+            'query': "${ _('Query') }",
+            'notebook': "${ _('Notebook') }"
           }
         };
         self.apiHelper = ApiHelper.getInstance({
@@ -1569,7 +1683,7 @@ from metadata.conf import has_navigator
         self.navigatorEnabled = ko.observable('${ has_navigator(user) }' === 'True');
         self.tabsEnabled = '${ USE_NEW_SIDE_PANELS.get() }' === 'True';
 
-        self.searchInput = ko.observable('').extend({ rateLimit: 500 });
+        self.searchInput = ko.observable('').extend({rateLimit: 500});
         self.searchResult = ko.observableArray();
 
         self.searchHasFocus = ko.observable(false);
@@ -1619,6 +1733,28 @@ from metadata.conf import has_navigator
             }));
           }
 
+          self.availablePanels.push(new AssistInnerPanel({
+            panelData: new AssistCollectionsPanel({
+              apiHelper: self.apiHelper
+            }),
+            apiHelper: self.apiHelper,
+            name: '${ _("Collections") }',
+            type: 'collections',
+            icon: 'fa-search-plus',
+            minHeight: 50
+          }));
+
+          self.availablePanels.push(new AssistInnerPanel({
+            panelData: new AssistHBasePanel({
+              apiHelper: self.apiHelper
+            }),
+            apiHelper: self.apiHelper,
+            name: '${ _("HBase") }',
+            type: 'hbase',
+            icon: 'fa-th-large',
+            minHeight: 50
+          }));
+
           self.availablePanels.push(new AssistInnerPanel({
             panelData: new AssistDocumentsPanel({
               user: params.user,
@@ -1633,17 +1769,6 @@ from metadata.conf import has_navigator
             showNavSearch: false,
             visible: params.visibleAssistPanels && params.visibleAssistPanels.indexOf('documents') !== -1
           }));
-
-          self.availablePanels.push(new AssistInnerPanel({
-            panelData: new AssistCollectionsPanel({
-              apiHelper: self.apiHelper
-            }),
-            apiHelper: self.apiHelper,
-            name: '${ _("Collections") }',
-            type: 'collections',
-            icon: 'fa-search-plus',
-            minHeight: 50
-          }));
         }
 
         self.performSearch = function () {