Explorar o código

HUE-8758 [connectors] Refactor to combine both saved and available connectors templates

Romain Rigaux %!s(int64=6) %!d(string=hai) anos
pai
achega
c2f2a38ffe

+ 27 - 18
desktop/core/src/desktop/lib/connectors/api.py

@@ -83,28 +83,43 @@ CATEGORIES = [
   {"name": "Plugins", 'type': 'plugins', 'description': ''},
 ]
 
-AVAILABLE_CONNECTORS = {
-  "connectors": [{
-    'category': category['name'],
-    'values': [_connector for _connector in CONNECTOR_TYPES if _connector['category'] == category['type']],
+
+def _group_category_connectors(connectors):
+  return [{
+    'category': category['type'],
+    'category_name': category['name'],
     'description': category['description'],
-  } for category in CATEGORIES]
-}
+    'values': [_connector for _connector in connectors if _connector['category'] == category['type']],
+  } for category in CATEGORIES
+]
+
+AVAILABLE_CONNECTORS = _group_category_connectors(CONNECTOR_TYPES)
+
 
 # TODO: persist in DB
 # TODO: remove installed connectors that don't have a connector or are blacklisted
-# TODO: load back from DB and apply type defaults, interface...
+# TODO: load back from DB and apply Category properties, e.g. defaults, interface, category, category_name...
 # TODO: connector groups: if we want one type (e.g. Hive) to show-up with multiple computes and the same saved query.
 CONFIGURED_CONNECTORS = [
-  {'name': 'Impala', 'type': Impala().TYPE + '-1', 'connector_name': Impala().TYPE, 'interface': Impala().INTERFACE, 'settings': Impala().PROPERTIES, 'id': 1},
-  {'name': 'Hive', 'type': Hive().TYPE + '-2', 'connector_name': Hive().TYPE, 'interface': Hive().INTERFACE, 'settings': Hive().PROPERTIES, 'id': 2},
-  {'name': 'Hive c5', 'type': Hive().TYPE + '-3', 'connector_name': Hive().TYPE, 'interface': Hive().INTERFACE, 'settings': Hive().PROPERTIES, 'id': 3},
+  {'name': 'Impala', 'type': Impala().TYPE + '-1', 'connector_name': Impala().TYPE, 'interface': Impala().INTERFACE, 'settings': Impala().PROPERTIES, 'id': 1, 'category': 'engines', 'description': ''},
+  {'name': 'Hive', 'type': Hive().TYPE + '-2', 'connector_name': Hive().TYPE, 'interface': Hive().INTERFACE, 'settings': Hive().PROPERTIES, 'id': 2, 'category': 'engines', 'description': ''},
+  {'name': 'Hive c5', 'type': Hive().TYPE + '-3', 'connector_name': Hive().TYPE, 'interface': Hive().INTERFACE, 'settings': Hive().PROPERTIES, 'id': 3, 'category': 'engines', 'description': ''},
 ]
 
 
-def connectors(request):
+def get_connector_classes(request):
+  global AVAILABLE_CONNECTORS
+  global CATEGORIES
+
+  return JsonResponse({
+    'connectors': AVAILABLE_CONNECTORS,
+    'categories': CATEGORIES
+  })
+
+
+def get_installed_connectors(request):
   return JsonResponse({
-    'connectors': CONFIGURED_CONNECTORS
+    'connectors': _group_category_connectors(CONFIGURED_CONNECTORS),
   })
 
 
@@ -179,9 +194,3 @@ def _get_connector_by_id(id):
     return instance[0]
   else:
     raise PopupException(_('No connector with the id %s found.') % id)
-
-
-def connector_types(request):
-  global AVAILABLE_CONNECTORS
-
-  return JsonResponse(AVAILABLE_CONNECTORS)

+ 2 - 2
desktop/core/src/desktop/lib/connectors/urls.py

@@ -23,8 +23,8 @@ from desktop.lib.connectors import views, api
 urlpatterns = [
   url(r'^$', views.index, name='desktop.lib.connectors.views.index'),
 
-  url(r'^api/instances/?$', api.connectors, name='connectors.api.connectors'),
-  url(r'^api/types/?$', api.connector_types, name='connectors.api.connector_types'),
+  url(r'^api/types/?$', api.get_connector_classes, name='connectors.api.get_connector_classes'),
+  url(r'^api/instances/?$', api.get_installed_connectors, name='connectors.api.get_installed_connectors'),
 
   url(r'^api/instance/new/(?P<type>[\w\-]+)$', api.new_connector, name='connectors.api.new_connector'),
   url(r'^api/instance/get/(?P<id>\d+)$', api.get_connector, name='connectors.api.get_connector'),

+ 106 - 93
desktop/core/src/desktop/templates/connectors.mako

@@ -39,18 +39,20 @@ else:
 
       self.apiHelper = window.apiHelper;
 
-      self.section = ko.observable('connectors-page');
+      self.section = ko.observable('installed-connectors-page');
+      self.categories = ko.observableArray();
+      self.selectedConnectorCategory = ko.observable('All');
+      self.connectorsFilter = ko.observable();
 
-      self.instances = ko.observableArray();
+      self.instances = ko.observableArray(); // Saved connectors
       self.instance = ko.observable();
 
-      self.connectors = ko.observableArray();
-      self.selectedConnectorCategory = ko.observable('All');
-      self.connectorsFilter = ko.observable();
+      self.connectors = ko.observableArray(); // Connector forms
       self.connector = ko.observable();
 
       self.selectedConnectors = ko.pureComputed(function () {
-        return self.connectors().filter(function (connector) {
+        const connectors = self.section() == 'installed-connectors-page' ? self.instances() : self.connectors();
+        return connectors.filter(function (connector) {
           return self.selectedConnectorCategory() == 'All' || connector.category == self.selectedConnectorCategory();
         });
       });
@@ -61,7 +63,7 @@ else:
           var lowerQuery = self.connectorsFilter().toLowerCase();
           var filteredConnectors = []
           connectors.forEach(function (connector) {
-            var _connector = {"category": connector.category, "values": []};
+            var _connector = {"category": connector.category, "category_name": $.grep(categories, function(cat) { return cat.type == connector.category})[0].category_name, "values": []};
             _connector.values = connector.values.filter(function (subMetricKey) {
               return subMetricKey.name.toLowerCase().indexOf(lowerQuery) !== -1;
             });
@@ -76,19 +78,27 @@ else:
       });
 
       self.addNewConnector = function () {
-        self.fetchConnectorTypes();
         self.section('add-connector-page');
       };
 
       self.fetchConnectors = function () {
+        self.fetchConnectorTypes(); // TODO: might be cleaner to chain below
         self.apiHelper.simpleGet('/desktop/connectors/api/instances/', {}, {successCallback: function (data) {
-          self.instances(ko.mapping.fromJS(data.connectors));
+          self.instances(data.connectors);
         }});
       };
+      self.editConnector = function (data) {
+        if (self.section() == 'installed-connectors-page') {
+          self.instance(data);
+        } else {
+          self.newConnector(data.type);
+        }
+        self.section('connector-page');
+      };
+
       self.newConnector = function (type) {
         self.apiHelper.simpleGet('/desktop/connectors/api/instance/new/' + type, {}, {successCallback: function (data) {
           self.instance(ko.mapping.fromJS(data.connector));
-          self.section('connector-page');
         }});
       };
       self.fetchConnector = function (id) {
@@ -98,15 +108,15 @@ else:
       };
       self.deleteConnector = function (connector) {
         self.apiHelper.simplePost('/desktop/connectors/api/instance/delete', {'connector': ko.mapping.toJSON(connector)}, {successCallback: function (data) {
-          self.section('connectors-page');
+          self.section('installed-connectors-page');
           self.fetchConnectors();
           huePubSub.publish('cluster.config.refresh.config');
         }});
       };
       self.updateConnector = function (connector) {
         self.apiHelper.simplePost('/desktop/connectors/api/instance/update', {'connector': ko.mapping.toJSON(connector)}, {successCallback: function (data) {
-          connector.id(data.connector.id)
-          self.section('connectors-page');
+          connector.id = data.connector.id;
+          self.section('installed-connectors-page');
           self.fetchConnectors();
           huePubSub.publish('cluster.config.refresh.config');
         }});
@@ -114,6 +124,7 @@ else:
       self.fetchConnectorTypes = function () {
         self.apiHelper.simpleGet('/desktop/connectors/api/types/', {}, {successCallback: function (data) {
           self.connectors(data.connectors);
+          self.categories(data.categories);
         }});
       };
     }
@@ -126,122 +137,70 @@ else:
 </script>
 
 
-${layout.menubar(section='connectors')}
+${ layout.menubar(section='connectors') }
 
 
 <div id="connectorsComponents" class="container-fluid">
 
-  <a href="javascript:void(0)" data-bind="click: function() { section('connectors-page'); }">
+  <a href="javascript:void(0)" data-bind="click: function() { selectedConnectorCategory('All'); section('installed-connectors-page'); }">
     ${ _('Connectors') }
   </a>
 
-  <!-- ko if: section() == 'connectors-page' -->
-    <div data-bind="template: { name: 'connectors-page', data: $root.instances() }"></div>
-  <!-- /ko -->
-
-  <!-- ko if: section() == 'connector-page' -->
-    <div data-bind="template: { name: 'connector-page', data: $root.instance() }"></div>
+  <!-- ko if: section() == 'installed-connectors-page' -->
+    <div data-bind="template: { name: 'installed-connectors-page' }"></div>
   <!-- /ko -->
 
   <!-- ko if: section() == 'add-connector-page' -->
     <div data-bind="template: { name: 'add-connector-page' }"></div>
   <!-- /ko -->
 
+  <!-- ko if: section() == 'connector-page' -->
+    <div data-bind="template: { name: 'connector-page', data: $root.instance() }"></div>
+  <!-- /ko -->
 </div>
 
 
-<script type="text/html" id="connectors-page">
+<script type="text/html" id="installed-connectors-page">
   <div class="row-fluid">
     <a href="javascript:void(0)" data-bind="click: $root.addNewConnector">
       + Connector
     </a>
   </div>
 
-  <div class="margin-top-10">
-    <table class="table table-condensed">
-      <thead>
-        <tr>
-          <th width="30%">${ _('Name') }</th>
-          <th>${ _('Type') }</th>
-        </tr>
-      </thead>
-      <tbody data-bind="foreach: $data">
-        <tr data-bind="click: function() { $root.instance($data); $root.section('connector-page'); }">
-          <td data-bind="text: name"></td>
-          <td data-bind="text: connector_name"></td>
-        </tr>
-      </tbody>
-    </table>
-    <!-- ko ifnot: $data -->
-      ${ _('There are no connectors configured.') }
-      <a href="javascript:void(0)" data-bind="click: $root.addNewConnector">
-        ${ _('Add one ?') }
-      </a>
-    <!-- /ko -->
+  <div data-bind="template: { name: 'connectors-page', data: $root.instances() }"></div>
+
 </script>
 
 
-<script type="text/html" id="connector-page">
+<script type="text/html" id="add-connector-page">
   <div class="row-fluid">
-    <input data-bind="value: name">
-    <!-- ko if: typeof id != 'undefined' -->
-      <!-- ko if: id -->
-        (<span data-bind="text: connector_name"></span>)
-        <a href="javascript:void(0)" data-bind="click: $root.updateConnector">
-          ${ _('Update') }
-        </a>
-        <a href="javascript:void(0)" data-bind="click: $root.deleteConnector">
-          ${ _('Delete') }
-        </a>
-      <!-- /ko -->
-      <!-- ko ifnot: id -->
-        <a href="javascript:void(0)" data-bind="click: $root.updateConnector">
-          ${ _('Save') }
-        </a>
-      <!-- /ko -->
-    <!-- /ko -->
-    <a href="javascript:void(0)">
-      ${ _('Test connection') }
-    </a>
-    <table class="table table-condensed">
-      <thead>
-        <tr>
-          <th width="30%">${ _('Name') }</th>
-          <th>${ _('Value') }</th>
-        </tr>
-      </thead>
-      <tbody data-bind="foreach: settings">
-        <tr>
-          <td data-bind="text: name"></td>
-          <td><input data-bind="value: value"></td>
-        </tr>
-      </tbody>
-    </table>
+    Add a Connector
   </div>
+
+  <div data-bind="template: { name: 'connectors-page', data: $root.connectors() }"></div>
+
 </script>
 
 
-<script type="text/html" id="add-connector-page">
+<script type="text/html" id="connectors-page">
   <div class="card card-small margin-top-10">
 
     <div class="card-body clearfix">
-
       <div class="span2">
         <div data-bind="dockable: { scrollable: ${ MAIN_SCROLLABLE }, jumpCorrection: 0, topSnap: '${ TOP_SNAP }', triggerAdjust: 0 }">
           <ul class="nav nav-pills nav-stacked">
             <li data-bind="css: { 'active': $root.selectedConnectorCategory() === 'All' }">
               <a href="javascript:void(0)" data-bind="text: 'All', click: function(){ $root.selectedConnectorCategory('All') }"></a>
             </li>
-            <!-- ko foreach: connectors() -->
-            <li data-bind="css: { 'active': $root.selectedConnectorCategory() === category }">
-              <a href="javascript:void(0)" data-bind="text: category, click: function(){ $root.selectedConnectorCategory(category) }"></a>
+            <!-- ko foreach: $root.categories -->
+            <li data-bind="css: { 'active': $root.selectedConnectorCategory() === type }">
+              <a href="javascript:void(0)" data-bind="text: name, click: function(){ $root.selectedConnectorCategory(type) }"></a>
             </li>
             <!-- /ko -->
           </ul>
         </div>
       </div>
 
-
       <div class="span10">
         <div data-bind="dockable: { scrollable: ${ MAIN_SCROLLABLE }, jumpCorrection: 0, topSnap: '${ TOP_SNAP }', triggerAdjust: 0 }">
           <span class="pull-right">
@@ -249,13 +208,13 @@ ${layout.menubar(section='connectors')}
               <i class="fa fa-question-circle"></i> ${ _('Help') }
             </a>
           </span>
-          <input type="text" data-bind="clearable: connectorsFilter, valueUpdate: 'afterkeydown'"
+          <input type="text" data-bind="clearable: $root.connectorsFilter, valueUpdate: 'afterkeydown'"
               class="input-xlarge pull-right margin-bottom-10" placeholder="${ _('Filter...') }">
         </div>
 
         <div class="margin-top-10">
-          <div data-bind="foreach: filteredConnectors()">
-            <h4 data-bind="text: category"></h4>
+          <div data-bind="foreach: $root.filteredConnectors()">
+            <h4 data-bind="text: category_name"></h4>
             <table class="table table-condensed">
               <thead>
                 <tr>
@@ -263,25 +222,32 @@ ${layout.menubar(section='connectors')}
                   <th>${ _('Description') }</th>
                 </tr>
               </thead>
-              <!-- ko if: $data.values -->
+              <!-- ko if: $data.values.length > 0 -->
               <tbody data-bind="foreach: values">
-                <tr data-bind="click: function() { $root.newConnector(type); }">
+                <tr data-bind="click: $root.editConnector">
                   <td data-bind="text: name"></td>
                   <td data-bind="text: description"></td>
                 </tr>
               </tbody>
               <!-- /ko -->
-              <!-- ko ifnot: $data.values -->
+              <!-- ko ifnot: $data.values.length > 0 -->
               <tfoot>
                 <tr>
-                  <td colspan="2">${ _('There are no connectors matching your filter') }</td>
+                  <td colspan="2">
+                    ${ _('No connectors') }
+                    <!-- ko if: $root.section() == 'installed-connectors-page' -->
+                      <a href="javascript:void(0)" data-bind="click: function() { $root.selectedConnectorCategory($data.category); $root.addNewConnector(); }">
+                        ${ _('Add one ?') }
+                      </a>
+                    <!-- /ko -->
+                  </td>
                 </tr>
               </tfoot>
               <!-- /ko -->
             </table>
           </div>
 
-          <!-- ko if: filteredConnectors().length == 0 -->
+          <!-- ko if: $root.filteredConnectors().length == 0 -->
           <table class="table table-condensed">
             <thead>
               <tr>
@@ -291,7 +257,14 @@ ${layout.menubar(section='connectors')}
             </thead>
             <tfoot>
               <tr>
-                <td colspan="2">${ _('There are no connectors matching your filter') }</td>
+                <td colspan="2">
+                  ${ _('No connectors') }
+                  <!-- ko if: $root.section() == 'installed-connectors-page' -->
+                    <a href="javascript:void(0)" data-bind="click: function() { $root.selectedConnectorCategory($data.category); $root.addNewConnector(); }">
+                      ${ _('Add one ?') }
+                    </a>
+                  <!-- /ko -->
+                </td>
               </tr>
             </tfoot>
           </table>
@@ -302,3 +275,43 @@ ${layout.menubar(section='connectors')}
 
   </div>
 </script>
+
+
+<script type="text/html" id="connector-page">
+  <div class="row-fluid">
+    <input data-bind="value: name">
+    <!-- ko if: typeof id != 'undefined' -->
+      <!-- ko if: id -->
+        (<span data-bind="text: connector_name"></span>)
+        <a href="javascript:void(0)" data-bind="click: $root.updateConnector">
+          ${ _('Update') }
+        </a>
+        <a href="javascript:void(0)" data-bind="click: $root.deleteConnector">
+          ${ _('Delete') }
+        </a>
+      <!-- /ko -->
+      <!-- ko ifnot: id -->
+        <a href="javascript:void(0)" data-bind="click: $root.updateConnector">
+          ${ _('Save') }
+        </a>
+      <!-- /ko -->
+    <!-- /ko -->
+    <a href="javascript:void(0)">
+      ${ _('Test connection') }
+    </a>
+    <table class="table table-condensed">
+      <thead>
+        <tr>
+          <th width="30%">${ _('Name') }</th>
+          <th>${ _('Value') }</th>
+        </tr>
+      </thead>
+      <tbody data-bind="foreach: settings">
+        <tr>
+          <td data-bind="text: name"></td>
+          <td><input data-bind="value: value"></td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</script>