Sfoglia il codice sorgente

HUE-8913 [assist] Combine ADLS, HDFS and S3 panels into one

Johan Ahlen 6 anni fa
parent
commit
f77abaebf3

+ 4 - 12
desktop/core/src/desktop/js/api/apiHelper.js

@@ -169,22 +169,10 @@ class ApiHelper {
     const self = this;
     self.queueManager = apiQueueManager;
 
-    huePubSub.subscribe('assist.clear.hdfs.cache', () => {
-      $.totalStorage(self.getAssistCacheIdentifier({ sourceType: 'hdfs' }), {});
-    });
-
-    huePubSub.subscribe('assist.clear.adls.cache', () => {
-      $.totalStorage(self.getAssistCacheIdentifier({ sourceType: 'adls' }), {});
-    });
-
     huePubSub.subscribe('assist.clear.git.cache', () => {
       $.totalStorage(self.getAssistCacheIdentifier({ sourceType: 'git' }), {});
     });
 
-    huePubSub.subscribe('assist.clear.s3.cache', () => {
-      $.totalStorage(self.getAssistCacheIdentifier({ sourceType: 's3' }), {});
-    });
-
     huePubSub.subscribe('assist.clear.collections.cache', () => {
       $.totalStorage(self.getAssistCacheIdentifier({ sourceType: 'collections' }), {});
     });
@@ -225,6 +213,10 @@ class ApiHelper {
     }
   }
 
+  clearStorageCache(sourceType) {
+    $.totalStorage(this.getAssistCacheIdentifier({ sourceType: sourceType }), {});
+  }
+
   hasExpired(timestamp, cacheType) {
     if (typeof hueDebug !== 'undefined' && typeof hueDebug.cacheTimeout !== 'undefined') {
       return new Date().getTime() - timestamp > hueDebug.cacheTimeout;

+ 6 - 12
desktop/core/src/desktop/js/ko/components/assist/assistStorageEntry.js

@@ -25,21 +25,15 @@ const PAGE_SIZE = 100;
 const TYPE_SPECIFICS = {
   adls: {
     apiHelperFetchFunction: 'fetchAdlsPath',
-    dblClickPubSubId: 'assist.dblClickAdlsItem',
-    goHomePubSubId: 'assist.adls.go.home',
-    selectEntryPubSubId: 'assist.selectAdlsEntry'
+    dblClickPubSubId: 'assist.dblClickAdlsItem'
   },
   hdfs: {
     apiHelperFetchFunction: 'fetchHdfsPath',
-    dblClickPubSubId: 'assist.dblClickHdfsItem',
-    goHomePubSubId: 'assist.hdfs.go.home',
-    selectEntryPubSubId: 'assist.selectHdfsEntry'
+    dblClickPubSubId: 'assist.dblClickHdfsItem'
   },
   s3: {
     apiHelperFetchFunction: 'fetchS3Path',
-    dblClickPubSubId: 'assist.dblClickS3Item',
-    goHomePubSubId: 'assist.s3.go.home',
-    selectEntryPubSubId: 'assist.selectS3Entry'
+    dblClickPubSubId: 'assist.dblClickS3Item'
   }
 };
 
@@ -171,7 +165,7 @@ class AssistStorageEntry {
   }
 
   goHome() {
-    huePubSub.publish(TYPE_SPECIFICS[this.type].goHomePubSubId);
+    huePubSub.publish('assist.storage.go.home');
   }
 
   loadDeep(folders, callback) {
@@ -235,10 +229,10 @@ class AssistStorageEntry {
     self.open(!self.open());
     if (self.definition.name === '..') {
       if (self.parent.parent) {
-        huePubSub.publish(TYPE_SPECIFICS[self.type].selectEntryPubSubId, self.parent.parent);
+        huePubSub.publish('assist.selectStorageEntry', self.parent.parent);
       }
     } else {
-      huePubSub.publish(TYPE_SPECIFICS[self.type].selectEntryPubSubId, self);
+      huePubSub.publish('assist.selectStorageEntry', self);
     }
   }
 

+ 0 - 173
desktop/core/src/desktop/js/ko/components/assist/ko.assistAdlsPanel.js

@@ -1,173 +0,0 @@
-// 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.
-
-import ko from 'knockout';
-
-import apiHelper from 'api/apiHelper';
-import AssistStorageEntry from 'ko/components/assist/assistStorageEntry';
-import componentUtils from 'ko/components/componentUtils';
-import huePubSub from 'utils/huePubSub';
-import HDFS_CONTEXT_ITEMS_TEMPLATE from 'ko/components/assist/ko.assistHdfsPanel';
-import I18n from 'utils/i18n';
-
-const TEMPLATE =
-  HDFS_CONTEXT_ITEMS_TEMPLATE +
-  `
-  <script type="text/html" id="assist-adls-header-actions">
-    <div class="assist-db-header-actions">
-      <a class="inactive-action" href="javascript:void(0)" data-bind="click: goHome, attr: { title: I18n('Go to ' + window.USER_HOME_DIR) }"><i class="pointer fa fa-home"></i></a>
-      <!-- ko if: window.SHOW_UPLOAD_BUTTON -->
-      <a class="inactive-action" data-bind="dropzone: {
-            url: '/filebrowser/upload/file?dest=adl:' + path,
-            params: { dest: path },
-            paramName: 'hdfs_file',
-            onError: function(x, e){ $(document).trigger('error', e); },
-            onComplete: function () { huePubSub.publish('assist.adls.refresh'); } }" title="${I18n(
-              'Upload file'
-            )}" href="javascript:void(0)">
-        <div class="dz-message inline" data-dz-message><i class="pointer fa fa-plus" title="${I18n(
-          'Upload file'
-        )}"></i></div>
-      </a>
-      <!-- /ko -->
-      <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.adls.refresh'); }" title="${I18n(
-        'Manual refresh'
-      )}"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }"></i></a>
-    </div>
-  </script>
-  
-  <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
-  <!-- ko with: selectedAdlsEntry -->
-  <div class="assist-flex-header assist-breadcrumb" >
-    <!-- ko if: parent !== null -->
-    <a href="javascript: void(0);" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-adls-scrollable' }, click: function () { huePubSub.publish('assist.selectAdlsEntry', parent); }">
-      <i class="fa fa-fw fa-chevron-left"></i>
-      <i class="fa fa-fw fa-folder-o"></i>
-      <span data-bind="text: definition.name, tooltip: {'title': path, 'placement': 'top' }"></span>
-    </a>
-    <!-- /ko -->
-    <!-- ko if: parent === null -->
-    <div>
-      <i class="fa fa-fw fa-folder-o"></i>
-      <span data-bind="text: path"></span>
-    </div>
-    <!-- /ko -->
-    <!-- ko template: 'assist-adls-header-actions' --><!-- /ko -->
-  </div>
-  <div class="assist-flex-search">
-    <div class="assist-filter"><input class="clearable" type="text" placeholder="${I18n(
-      'Filter...'
-    )}" data-bind="clearable: filter, value: filter, valueUpdate: 'afterkeydown'"/></div>
-  </div>
-  <div class="assist-flex-fill assist-adls-scrollable" data-bind="delayedOverflow">
-    <div data-bind="visible: ! loading() && ! hasErrors()" style="position: relative;">
-      <!-- ko hueSpinner: { spin: loadingMore, overlay: true } --><!-- /ko -->
-      <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 22, container: '.assist-adls-scrollable', fetchMore: $data.fetchMore.bind($data) }">
-        <li class="assist-entry assist-table-link" style="position: relative;" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-adls-scrollable' }, visibleOnHover: { override: contextPopoverVisible, 'selector': '.assist-actions' }">
-          <div class="assist-actions table-actions" style="opacity: 0;" >
-            <a style="padding: 0 3px;" class="inactive-action" href="javascript:void(0);" data-bind="click: showContextPopover, css: { 'blue': contextPopoverVisible }">
-              <i class='fa fa-info' title="${I18n('Details')}"></i>
-            </a>
-          </div>
-
-          <a href="javascript:void(0)" class="assist-entry assist-table-link" data-bind="multiClick: { click: toggleOpen, dblClick: dblClick }, attr: {'title': definition.name }">
-            <!-- ko if: definition.type === 'dir' -->
-            <i class="fa fa-fw fa-folder-o muted valign-middle"></i>
-            <!-- /ko -->
-            <!-- ko if: definition.type === 'file' -->
-            <i class="fa fa-fw fa-file-o muted valign-middle"></i>
-            <!-- /ko -->
-            <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\\'' + path + '\\'', meta: {'type': 'adls', 'definition': definition} }"></span>
-          </a>
-        </li>
-      </ul>
-      <!-- ko if: !loading() && entries().length === 0 -->
-      <ul class="assist-tables">
-        <li class="assist-entry"><span class="assist-no-entries"><!-- ko if: filter() -->${I18n(
-          'No results found.'
-        )}<!-- /ko --><!-- ko ifnot: filter() -->${I18n('Empty directory')}<!-- /ko --></span></li>
-      </ul>
-      <!-- /ko -->
-    </div>
-    <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
-    <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
-      <span>${I18n('Error loading contents.')}</span>
-    </div>
-  </div>
-  <!-- /ko -->
-`;
-
-class AssistAdlsPanel {
-  constructor() {
-    const self = this;
-    self.selectedAdlsEntry = ko.observable();
-    self.loading = ko.observable();
-    self.initialized = false;
-
-    const loadPath = path => {
-      self.loading(true);
-      const parts = path.split('/');
-      parts.shift();
-
-      const currentEntry = new AssistStorageEntry({
-        type: 'adls',
-        definition: {
-          name: '/',
-          type: 'dir'
-        },
-        parent: null
-      });
-
-      currentEntry.loadDeep(parts, entry => {
-        self.selectedAdlsEntry(entry);
-        entry.open(true);
-        self.loading(false);
-      });
-    };
-
-    self.reload = () => {
-      loadPath(apiHelper.getFromTotalStorage('assist', 'currentAdlsPath', '/'));
-    };
-
-    huePubSub.subscribe('assist.adls.go.home', () => {
-      loadPath(window.USER_HOME_DIR);
-      apiHelper.setInTotalStorage('assist', 'currentAdlsPath', window.USER_HOME_DIR);
-    });
-
-    huePubSub.subscribe('assist.selectAdlsEntry', entry => {
-      self.selectedAdlsEntry(entry);
-      apiHelper.setInTotalStorage('assist', 'currentAdlsPath', entry.path);
-    });
-
-    huePubSub.subscribe('assist.adls.refresh', () => {
-      huePubSub.publish('assist.clear.adls.cache');
-      self.reload();
-    });
-
-    self.init();
-  }
-
-  init() {
-    const self = this;
-    if (self.initialized) {
-      return;
-    }
-    self.reload();
-    self.initialized = true;
-  }
-}
-
-componentUtils.registerStaticComponent('hue-assist-adls-panel', AssistAdlsPanel, TEMPLATE);

+ 73 - 113
desktop/core/src/desktop/js/ko/components/assist/ko.assistPanel.js

@@ -122,128 +122,88 @@ class AssistPanel {
         }
 
         if (self.tabsEnabled) {
-          if (
-            appConfig['browser'] &&
-            appConfig['browser']['interpreter_names'].indexOf('hdfs') !== -1
-          ) {
-            panels.push(
-              new AssistInnerPanel({
-                panelData: {
-                  name: 'hue-assist-hdfs-panel',
-                  params: {}
-                },
-                name: I18n('HDFS'),
-                type: 'hdfs',
-                icon: 'fa-files-o',
-                minHeight: 50
-              })
+          if (appConfig.browser && appConfig.browser.interpreter_names) {
+            const storageBrowsers = appConfig.browser.interpreter_names.filter(
+              interpreter =>
+                interpreter === 'adls' || interpreter === 'hdfs' || interpreter === 's3'
             );
-          }
 
-          if (
-            appConfig['browser'] &&
-            appConfig['browser']['interpreter_names'].indexOf('s3') !== -1
-          ) {
-            panels.push(
-              new AssistInnerPanel({
+            if (storageBrowsers.length) {
+              panels.push(
+                new AssistInnerPanel({
+                  panelData: {
+                    name: 'hue-assist-storage-panel',
+                    params: {
+                      sources: storageBrowsers
+                    }
+                  },
+                  name: I18n('Files'),
+                  type: 'files',
+                  icon: 'fa-files-o',
+                  minHeight: 50
+                })
+              );
+            }
+
+            if (appConfig.browser.interpreter_names.indexOf('indexes') !== -1) {
+              const solrPanel = new AssistInnerPanel({
                 panelData: {
-                  name: 'hue-assist-s3-panel',
-                  params: {}
+                  name: 'hue-assist-db-panel',
+                  params: $.extend(
+                    {
+                      i18n: i18nCollections,
+                      isSolr: true
+                    },
+                    params.sql
+                  )
                 },
-                name: I18n('S3'),
-                type: 's3',
-                icon: 'fa-cubes',
-                minHeight: 50
-              })
-            );
-          }
+                name: I18n('Indexes'),
+                type: 'solr',
+                icon: 'fa-search-plus',
+                minHeight: 75
+              });
+              panels.push(solrPanel);
+              huePubSub.subscribe('assist.show.solr', () => {
+                if (self.visiblePanel() !== solrPanel) {
+                  self.visiblePanel(solrPanel);
+                }
+              });
+            }
 
-          if (
-            appConfig['browser'] &&
-            appConfig['browser']['interpreter_names'].indexOf('adls') !== -1
-          ) {
-            panels.push(
-              new AssistInnerPanel({
+            if (appConfig.browser.interpreter_names.indexOf('kafka') !== -1) {
+              const streamsPanel = new AssistInnerPanel({
                 panelData: {
-                  name: 'hue-assist-adls-panel',
-                  params: {}
+                  name: 'hue-assist-db-panel',
+                  params: $.extend(
+                    {
+                      i18n: i18nCollections,
+                      isStreams: true
+                    },
+                    params.sql
+                  )
                 },
-                name: I18n('ADLS'),
-                type: 'adls',
-                icon: 'fa-windows',
-                iconSvg: '#hi-adls',
-                minHeight: 50
-              })
-            );
-          }
-
-          if (
-            appConfig['browser'] &&
-            appConfig['browser']['interpreter_names'].indexOf('indexes') !== -1
-          ) {
-            const solrPanel = new AssistInnerPanel({
-              panelData: {
-                name: 'hue-assist-db-panel',
-                params: $.extend(
-                  {
-                    i18n: i18nCollections,
-                    isSolr: true
-                  },
-                  params.sql
-                )
-              },
-              name: I18n('Indexes'),
-              type: 'solr',
-              icon: 'fa-search-plus',
-              minHeight: 75
-            });
-            panels.push(solrPanel);
-            huePubSub.subscribe('assist.show.solr', () => {
-              if (self.visiblePanel() !== solrPanel) {
-                self.visiblePanel(solrPanel);
-              }
-            });
-          }
+                name: I18n('Streams'),
+                type: 'kafka',
+                icon: 'fa-sitemap',
+                minHeight: 75
+              });
+              panels.push(streamsPanel);
+            }
 
-          if (
-            appConfig['browser'] &&
-            appConfig['browser']['interpreter_names'].indexOf('kafka') !== -1
-          ) {
-            const streamsPanel = new AssistInnerPanel({
-              panelData: {
-                name: 'hue-assist-db-panel',
-                params: $.extend(
-                  {
-                    i18n: i18nCollections,
-                    isStreams: true
+            if (appConfig.browser.interpreter_names.indexOf('hbase') !== -1) {
+              panels.push(
+                new AssistInnerPanel({
+                  panelData: {
+                    name: 'hue-assist-hbase-panel',
+                    params: {}
                   },
-                  params.sql
-                )
-              },
-              name: I18n('Streams'),
-              type: 'kafka',
-              icon: 'fa-sitemap',
-              minHeight: 75
-            });
-            panels.push(streamsPanel);
-          }
-
-          if (
-            appConfig['browser'] &&
-            appConfig['browser']['interpreter_names'].indexOf('hbase') !== -1
-          ) {
-            panels.push(
-              new AssistInnerPanel({
-                panelData: {
-                  name: 'hue-assist-hbase-panel',
-                  params: {}
-                },
-                name: I18n('HBase'),
-                type: 'hbase',
-                icon: 'fa-th-large',
-                minHeight: 50
-              })
-            );
+                  name: I18n('HBase'),
+                  type: 'hbase',
+                  icon: 'fa-th-large',
+                  minHeight: 50
+                })
+              );
+            }
           }
 
           if (!window.IS_EMBEDDED) {

+ 0 - 155
desktop/core/src/desktop/js/ko/components/assist/ko.assistS3Panel.js

@@ -1,155 +0,0 @@
-// 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.
-
-import ko from 'knockout';
-
-import apiHelper from 'api/apiHelper';
-import AssistStorageEntry from 'ko/components/assist/assistStorageEntry';
-import componentUtils from 'ko/components/componentUtils';
-import HDFS_CONTEXT_ITEMS_TEMPLATE from 'ko/components/assist/ko.assistHdfsPanel';
-import huePubSub from 'utils/huePubSub';
-import I18n from 'utils/i18n';
-
-const TEMPLATE =
-  HDFS_CONTEXT_ITEMS_TEMPLATE +
-  `
-  <script type="text/html" id="assist-s3-header-actions">
-    <div class="assist-db-header-actions">
-      <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.s3.refresh'); }"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }" title="${I18n(
-        'Manual refresh'
-      )}"></i></a>
-    </div>
-  </script>
-
-  <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
-  <!-- ko with: selectedS3Entry -->
-  <div class="assist-flex-header assist-breadcrumb" >
-    <!-- ko if: parent !== null -->
-    <a href="javascript: void(0);" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-s3-scrollable' }, click: function () { huePubSub.publish('assist.selectS3Entry', parent); }">
-      <i class="fa fa-fw fa-chevron-left"></i>
-      <i class="fa fa-fw fa-folder-o"></i>
-      <span data-bind="text: definition.name, tooltip: {'title': path, 'placement': 'top' }"></span>
-    </a>
-    <!-- /ko -->
-    <!-- ko if: parent === null -->
-    <div>
-      <i class="fa fa-fw fa-folder-o"></i>
-      <span data-bind="text: path"></span>
-    </div>
-    <!-- /ko -->
-    <!-- ko template: 'assist-s3-header-actions' --><!-- /ko -->
-  </div>
-  <div class="assist-flex-search">
-    <div class="assist-filter"><input class="clearable" type="text" placeholder="${I18n(
-      'Filter...'
-    )}" data-bind="clearable: filter, value: filter, valueUpdate: 'afterkeydown'"/></div>
-  </div>
-  <div class="assist-flex-fill assist-s3-scrollable" data-bind="delayedOverflow">
-    <div data-bind="visible: ! loading() && ! hasErrors()" style="position: relative;">
-      <!-- ko hueSpinner: { spin: loadingMore, overlay: true } --><!-- /ko -->
-      <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 22, container: '.assist-s3-scrollable', fetchMore: $data.fetchMore.bind($data) }">
-        <li class="assist-entry assist-table-link" style="position: relative;" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-s3-scrollable' }, visibleOnHover: { override: contextPopoverVisible, 'selector': '.assist-actions' }">
-          <div class="assist-actions table-actions" style="opacity: 0;" >
-            <a style="padding: 0 3px;" class="inactive-action" href="javascript:void(0);" data-bind="click: showContextPopover, css: { 'blue': contextPopoverVisible }">
-              <i class='fa fa-info' title="${I18n('Details')}"></i>
-            </a>
-          </div>
-
-          <a href="javascript:void(0)" class="assist-entry assist-table-link" data-bind="multiClick: { click: toggleOpen, dblClick: dblClick }, attr: {'title': definition.name }">
-            <!-- ko if: definition.type === 'dir' -->
-            <i class="fa fa-fw fa-folder-o muted valign-middle"></i>
-            <!-- /ko -->
-            <!-- ko if: definition.type === 'file' -->
-            <i class="fa fa-fw fa-file-o muted valign-middle"></i>
-            <!-- /ko -->
-            <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\\'' + path + '\\'', meta: {'type': 's3', 'definition': definition} }"></span>
-          </a>
-        </li>
-      </ul>
-      <!-- ko if: !loading() && entries().length === 0 -->
-      <ul class="assist-tables">
-        <li class="assist-entry"><span class="assist-no-entries"><!-- ko if: filter() -->${I18n(
-          'No results found'
-        )}<!-- /ko --><!-- ko ifnot: filter() -->${I18n('Empty directory')}<!-- /ko --></span></li>
-      </ul>
-      <!-- /ko -->
-    </div>
-    <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
-    <div class="assist-errors" data-bind="visible: ! loading() && hasErrors()">
-      <span>${I18n('Error loading contents.')}</span>
-    </div>
-  </div>
-  <!-- /ko -->
-`;
-
-class AssistS3Panel {
-  /**
-   * @param {Object} options
-   * @constructor
-   **/
-  constructor(options) {
-    const self = this;
-
-    self.selectedS3Entry = ko.observable();
-    self.loading = ko.observable();
-    self.initialized = false;
-
-    self.reload = () => {
-      self.loading(true);
-      const lastKnownPath = apiHelper.getFromTotalStorage('assist', 'currentS3Path', '/');
-      const parts = lastKnownPath.split('/');
-      parts.shift();
-
-      const currentEntry = new AssistStorageEntry({
-        type: 's3',
-        definition: {
-          name: '/',
-          type: 'dir'
-        },
-        parent: null
-      });
-
-      currentEntry.loadDeep(parts, entry => {
-        self.selectedS3Entry(entry);
-        entry.open(true);
-        self.loading(false);
-      });
-    };
-
-    huePubSub.subscribe('assist.selectS3Entry', entry => {
-      self.selectedS3Entry(entry);
-      apiHelper.setInTotalStorage('assist', 'currentS3Path', entry.path);
-    });
-
-    huePubSub.subscribe('assist.s3.refresh', () => {
-      huePubSub.publish('assist.clear.s3.cache');
-      self.reload();
-    });
-
-    self.init();
-  }
-
-  init() {
-    const self = this;
-    if (self.initialized) {
-      return;
-    }
-    self.reload();
-    self.initialized = true;
-  }
-}
-
-componentUtils.registerStaticComponent('hue-assist-s3-panel', AssistS3Panel, TEMPLATE);

+ 125 - 75
desktop/core/src/desktop/js/ko/components/assist/ko.assistHdfsPanel.js → desktop/core/src/desktop/js/ko/components/assist/ko.assistStoragePanel.js

@@ -16,14 +16,14 @@
 
 import ko from 'knockout';
 
-import apiHelper from 'api/apiHelper';
-import AssistStorageEntry from 'ko/components/assist/assistStorageEntry';
+import apiHelper from '../../../api/apiHelper';
+import AssistStorageEntry from './assistStorageEntry';
 import componentUtils from 'ko/components/componentUtils';
-import huePubSub from 'utils/huePubSub';
+import huePubSub from '../../../utils/huePubSub';
 import I18n from 'utils/i18n';
 
-const HDFS_CONTEXT_ITEMS_TEMPLATE = `
-  <script type="text/html" id="hdfs-context-items">
+const TEMPLATE = `
+  <script type="text/html" id="storage-context-items">
     <li><a href="javascript:void(0);" data-bind="click: function (data) { showContextPopover(data, { target: $parentContext.$contextSourceElement }, { left: -15, top: 2 }); }"><i class="fa fa-fw fa-info"></i> ${I18n(
       'Show details'
     )}</a></li>
@@ -41,21 +41,18 @@ const HDFS_CONTEXT_ITEMS_TEMPLATE = `
     )}</a></li>
     <!-- /ko -->
   </script>
-`;
 
-const TEMPLATE =
-  HDFS_CONTEXT_ITEMS_TEMPLATE +
-  `
-  <script type="text/html" id="assist-hdfs-header-actions">
+  <script type="text/html" id="assist-storage-header-actions">
     <div class="assist-db-header-actions">
+      <!-- ko if: type !== 's3' -->
       <a class="inactive-action" href="javascript:void(0)" data-bind="click: goHome, attr: { title: I18n('Go to ' + window.USER_HOME_DIR) }"><i class="pointer fa fa-home"></i></a>
       <!-- ko if: window.SHOW_UPLOAD_BUTTON -->
       <a class="inactive-action" data-bind="dropzone: {
-            url: '/filebrowser/upload/file?dest=' + path,
+            url: '/filebrowser/upload/file?dest=' + (type === 'adls' ? 'adl:' : '') + path,
             params: { dest: path },
             paramName: 'hdfs_file',
             onError: function(x, e){ $(document).trigger('error', e); },
-            onComplete: function () { huePubSub.publish('assist.hdfs.refresh'); huePubSub.publish('fb.hdfs.refresh', path); } }" title="${I18n(
+            onComplete: function () { huePubSub.publish('assist.storage.refresh'); } }" title="${I18n(
               'Upload file'
             )}" href="javascript:void(0)">
         <div class="dz-message inline" data-dz-message><i class="pointer fa fa-plus" title="${I18n(
@@ -63,40 +60,70 @@ const TEMPLATE =
         )}"></i></div>
       </a>
       <!-- /ko -->
-      <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.hdfs.refresh'); }" title="${I18n(
+      <!-- /ko -->
+      <a class="inactive-action" href="javascript:void(0)" data-bind="click: function () { huePubSub.publish('assist.storage.refresh'); }" title="${I18n(
         'Manual refresh'
       )}"><i class="pointer fa fa-refresh" data-bind="css: { 'fa-spin blue' : loading }"></i></a>
     </div>
   </script>
-  
+
   <!-- ko hueSpinner: { spin: loading, center: true, size: 'large' } --><!-- /ko -->
-  <!-- ko with: selectedHdfsEntry -->
+
+  <!-- ko ifnot: activeSource -->
+  <div class="assist-flex-header">
+    <div class="assist-inner-header">
+      ${I18n('Sources')}
+    </div>
+  </div>
+  <div class="assist-flex-fill">
+    <ul class="assist-tables" data-bind="foreach: sources">
+      <li class="assist-table">
+        <a class="assist-table-link" href="javascript: void(0);" data-bind="click: function () { $parent.activeSource($data); }"><i class="fa fa-fw fa-server muted valign-middle"></i> <span data-bind="text: $data.toUpperCase()"></span></a>
+      </li>
+    </ul>
+  </div>
+  <!-- /ko -->
+
+  <!-- ko if: activeSource -->
+  <!-- ko with: selectedStorageEntry -->
   <div class="assist-flex-header assist-breadcrumb" >
     <!-- ko if: parent !== null -->
-    <a href="javascript: void(0);" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-hdfs-scrollable' }, click: function () { huePubSub.publish('assist.selectHdfsEntry', parent); }">
+    <a href="javascript: void(0);" data-bind="appAwareTemplateContextMenu: {
+      template: 'storage-context-items',
+      scrollContainer: '.assist-files-scrollable'
+    }, click: function () {
+      huePubSub.publish('assist.selectStorageEntry', parent);
+    }">
       <i class="fa fa-fw fa-chevron-left"></i>
       <i class="fa fa-fw fa-folder-o"></i>
       <span data-bind="text: definition.name, tooltip: {'title': path, 'placement': 'top' }"></span>
     </a>
     <!-- /ko -->
     <!-- ko if: parent === null -->
-    <div>
-      <i class="fa fa-fw fa-folder-o"></i>
-      <span data-bind="text: path"></span>
-    </div>
+    <a href="javascript: void(0);" data-bind="click: function () { $parent.activeSource(undefined) }">
+      <i class="fa fa-fw fa-chevron-left"></i>
+      <i class="fa fa-fw fa-server"></i>
+      <span data-bind="text: $parent.activeSource().toUpperCase()"></span>
+    </a>
     <!-- /ko -->
-    <!-- ko template: 'assist-hdfs-header-actions' --><!-- /ko -->
+    <!-- ko template: 'assist-storage-header-actions' --><!-- /ko -->
   </div>
   <div class="assist-flex-search">
-    <div class="assist-filter"><input class="clearable" type="text" placeholder="${I18n(
-      'Filter...'
-    )}" data-bind="clearable: filter, value: filter, valueUpdate: 'afterkeydown'"/></div>
+    <div class="assist-filter">
+      <form autocomplete="off">
+        <input class="clearable" type="text" placeholder="${I18n(
+          'Filter...'
+        )}" autocorrect="off" autocomplete="do-not-autocomplete" autocapitalize="off" spellcheck="false"
+        data-bind="clearable: filter, value: filter, valueUpdate: 'afterkeydown'"/>
+      </form>
+    </div>
   </div>
-  <div class="assist-flex-fill assist-hdfs-scrollable" data-bind="delayedOverflow">
+
+  <div class="assist-flex-fill assist-storage-scrollable" data-bind="delayedOverflow">
     <div data-bind="visible: ! loading() && ! hasErrors()" style="position: relative;">
       <!-- ko hueSpinner: { spin: loadingMore, overlay: true } --><!-- /ko -->
-      <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 22, container: '.assist-hdfs-scrollable', fetchMore: $data.fetchMore.bind($data) }">
-        <li class="assist-entry assist-table-link" style="position: relative;" data-bind="appAwareTemplateContextMenu: { template: 'hdfs-context-items', scrollContainer: '.assist-hdfs-scrollable' }, visibleOnHover: { override: contextPopoverVisible, 'selector': '.assist-actions' }">
+      <ul class="assist-tables" data-bind="foreachVisible: { data: entries, minHeight: 22, container: '.assist-storage-scrollable', fetchMore: $data.fetchMore.bind($data) }">
+        <li class="assist-entry assist-table-link" style="position: relative;" data-bind="appAwareTemplateContextMenu: { template: 'storage-context-items', scrollContainer: '.assist-storage-scrollable' }, visibleOnHover: { override: contextPopoverVisible, 'selector': '.assist-actions' }">
           <div class="assist-actions table-actions" style="opacity: 0;" >
             <a style="padding: 0 3px;" class="inactive-action" href="javascript:void(0);" data-bind="click: showContextPopover, css: { 'blue': contextPopoverVisible }">
               <i class='fa fa-info' title="${I18n('Details')}"></i>
@@ -110,7 +137,7 @@ const TEMPLATE =
             <!-- ko if: definition.type === 'file' -->
             <i class="fa fa-fw fa-file-o muted valign-middle"></i>
             <!-- /ko -->
-            <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\\'' + path + '\\'', meta: {'type': 'hdfs', 'definition': definition} }"></span>
+            <span draggable="true" data-bind="text: definition.name, draggableText: { text: '\\'' + path + '\\'', meta: {'type': type, 'definition': definition} }"></span>
           </a>
         </li>
       </ul>
@@ -128,72 +155,95 @@ const TEMPLATE =
     </div>
   </div>
   <!-- /ko -->
+  <!-- /ko -->
 `;
 
-class AssistHdfsPanel {
+class AssistStoragePanel {
   /**
    * @param {Object} options
+   * @param {String[]} options.sources
    * @constructor
    **/
   constructor(options) {
-    const self = this;
-    self.selectedHdfsEntry = ko.observable();
-    self.loading = ko.observable();
-    self.initialized = false;
-
-    const loadPath = path => {
-      self.loading(true);
-      const parts = path.split('/');
-      parts.shift();
-
-      const currentEntry = new AssistStorageEntry({
-        type: 'hdfs',
-        definition: {
-          name: '/',
-          type: 'dir'
-        },
-        parent: null
-      });
-
-      currentEntry.loadDeep(parts, entry => {
-        self.selectedHdfsEntry(entry);
-        entry.open(true);
-        self.loading(false);
-      });
-    };
-
-    self.reload = () => {
-      loadPath(apiHelper.getFromTotalStorage('assist', 'currentHdfsPath', window.USER_HOME_DIR));
-    };
-
-    huePubSub.subscribe('assist.hdfs.go.home', () => {
-      loadPath(window.USER_HOME_DIR);
-      apiHelper.setInTotalStorage('assist', 'currentHdfsPath', window.USER_HOME_DIR);
+    this.sources = ko.observableArray(options.sources);
+
+    let lastSource = apiHelper.getFromTotalStorage('assist', 'lastStorageSource', 'hdfs');
+
+    if (options.sources.indexOf(lastSource) === -1) {
+      lastSource = options.sources.indexOf('hdfs') !== -1 ? 'hdfs' : options.sources[0];
+    }
+
+    this.activeSource = ko.observable(lastSource);
+    this.loading = ko.observable();
+    this.initialized = false;
+
+    this.selectedStorageEntry = ko.observable();
+
+    this.activeSource.subscribe(newValue => {
+      if (newValue) {
+        apiHelper.setInTotalStorage('assist', 'lastStorageSource', newValue);
+        this.selectedStorageEntry(undefined);
+        this.reload();
+      }
     });
 
-    huePubSub.subscribe('assist.selectHdfsEntry', entry => {
-      self.selectedHdfsEntry(entry);
-      apiHelper.setInTotalStorage('assist', 'currentHdfsPath', entry.path);
+    huePubSub.subscribe('assist.selectStorageEntry', entry => {
+      this.selectedStorageEntry(entry);
+      apiHelper.setInTotalStorage('assist', 'currentStoragePath_' + entry.type, entry.path);
     });
 
-    huePubSub.subscribe('assist.hdfs.refresh', () => {
-      huePubSub.publish('assist.clear.hdfs.cache');
+    huePubSub.subscribe('assist.storage.refresh', () => {
+      apiHelper.clearStorageCache(this.activeSource());
       self.reload();
     });
 
-    self.init();
+    huePubSub.subscribe('assist.storage.go.home', () => {
+      const path = this.activeSource() === 's3' ? '/' : window.USER_HOME_DIR;
+      this.loadPath(path);
+      apiHelper.setInTotalStorage('assist', 'currentStoragePath_' + this.activeSource(), path);
+    });
+
+    this.init();
+  }
+
+  loadPath(path) {
+    this.loading(true);
+    const parts = path.split('/');
+    parts.shift();
+
+    const currentEntry = new AssistStorageEntry({
+      type: this.activeSource(),
+      definition: {
+        name: '/',
+        type: 'dir'
+      },
+      parent: null
+    });
+
+    currentEntry.loadDeep(parts, entry => {
+      this.selectedStorageEntry(entry);
+      entry.open(true);
+      this.loading(false);
+    });
+  }
+
+  reload() {
+    this.loadPath(
+      apiHelper.getFromTotalStorage(
+        'assist',
+        'currentStoragePath_' + this.activeSource(),
+        this.activeSource() === 'hdfs' ? window.USER_HOME_DIR : '/'
+      )
+    );
   }
 
   init() {
-    const self = this;
-    if (self.initialized) {
+    if (this.initialized) {
       return;
     }
-    self.reload();
-    self.initialized = true;
+    this.reload();
+    this.initialized = true;
   }
 }
 
-componentUtils.registerStaticComponent('hue-assist-hdfs-panel', AssistHdfsPanel, TEMPLATE);
-
-export default HDFS_CONTEXT_ITEMS_TEMPLATE;
+componentUtils.registerStaticComponent('hue-assist-storage-panel', AssistStoragePanel, TEMPLATE);

+ 1 - 3
desktop/core/src/desktop/js/ko/ko.all.js

@@ -126,7 +126,6 @@ import 'ko/bindings/ko.typeahead';
 import 'ko/bindings/ko.verticalSlide';
 import 'ko/bindings/ko.visibleOnHover';
 
-import 'ko/components/assist/ko.assistAdlsPanel';
 import 'ko/components/assist/ko.assistDashboardPanel';
 import 'ko/components/assist/ko.assistDbPanel';
 import 'ko/components/assist/ko.assistDocumentsPanel';
@@ -134,11 +133,10 @@ import 'ko/components/assist/ko.assistEditorContextPanel';
 import 'ko/components/assist/ko.assistFunctionsPanel';
 import 'ko/components/assist/ko.assistGitPanel';
 import 'ko/components/assist/ko.assistHBasePanel';
-import 'ko/components/assist/ko.assistHdfsPanel';
 import 'ko/components/assist/ko.assistLangRefPanel';
 import 'ko/components/assist/ko.assistPanel';
-import 'ko/components/assist/ko.assistS3Panel';
 import 'ko/components/assist/ko.assistSchedulePanel';
+import 'ko/components/assist/ko.assistStoragePanel';
 import 'ko/components/assist/ko.rightAssistPanel';
 import 'ko/components/contextPopover/ko.contextPopover';
 import 'ko/components/simpleAceEditor/ko.simpleAceEditor';