Procházet zdrojové kódy

HUE-8330 [connectors] Add Edit connector instance page

Romain Rigaux před 6 roky
rodič
revize
bb7559bc7a

+ 1 - 1
desktop/core/src/desktop/js/onePageViewModel.js

@@ -522,7 +522,7 @@ class OnePageViewModel {
         app: function() {
           self.loadApp('connectors');
           self.getActiveAppViewModel(viewModel => {
-            viewModel.fetchConnectors();
+            viewModel.fetchConnector();
           });
         }
       },

+ 15 - 0
desktop/core/src/desktop/lib/connectors/__init__.py

@@ -0,0 +1,15 @@
+# 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.

+ 90 - 0
desktop/core/src/desktop/lib/connectors/api.py

@@ -0,0 +1,90 @@
+#!/usr/bin/env python
+# 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 json
+import logging
+
+from desktop.lib.connectors.lib.impala import Impala
+from desktop.lib.django_util import JsonResponse, render
+
+
+LOG = logging.getLogger(__name__)
+
+CONNECTOR_TYPES = [
+  {'name': connector.NAME, 'settings': connector.PROPERTIES}
+  for connector in [
+    Impala()
+  ]
+]
+
+CONNECTORS = {
+  "timestamp": "2019-04-05T23:36:47.533981",
+  "connectors": [
+    {"category": "Query Engines", "values": [
+      {"name": "Impala", "id": 1, "instances": []},
+      {"name": "SQL Database", "id": 2, "instances": []},
+      {"name": "Hive", "id": 3, "instances": [1, 2]},
+      {"name": "Hive Tez", "id": 4, "instances": []},
+      {"name": "Hive LLAP", "id": 5, "instances": []},
+      {"name": "Druid", "id": 6, "instances": []},
+      {"name": "Kafka SQL", "id": 6, "instances": []},
+      {"name": "SparkSQL", "id": 6, "instances": []},
+      {"name": "Presto", "id": 6, "instances": []},
+      {"name": "Athena", "id": 6, "instances": []},
+      {"name": "Redshift", "id": 6, "instances": []},
+      {"name": "Big Query", "id": 6, "instances": []},
+      {"name": "Oracle", "id": 6, "instances": []},
+    ]},
+    {"category": "Browsers", "values": [
+      {"name": "HDFS", "id": 30, "instances": []},
+      {"name": "YARN", "id": 30, "instances": []},
+      {"name": "S3", "id": 31, "instances": []},
+      {"name": "ADLS", "id": 32, "instances": []}
+    ]},
+    {"category": "Catalogs", "values": [
+      {"name": "Navigator", "id": 7, "instances": []},
+      {"name": "Atlas", "id": 8, "instances": []}
+    ]},
+    {"category": "Optimizers", "values": [
+      {"name": "Optimizer", "id": 9, "instances": []}
+    ]},
+    {"category": "Schedulers", "values": [
+      {"name": "Oozie", "id": 10, "instances": []},
+      {"name": "Celery", "id": 11, "instances": []}
+    ]},
+    {"category": "Apps", "values": []},
+    {"category": "Plugins", "values": []},
+  ]
+}
+
+
+def connectors(request):
+  return JsonResponse({
+    'connectors': CONNECTOR_TYPES
+  })
+
+
+def connector(request):
+  insance = json.loads(request.POST.get('id', 'null'))
+
+  return JsonResponse(CONNECTOR_TYPES[0])
+
+
+def connector_types(request):
+  global CONNECTORS
+
+  return JsonResponse(CONNECTORS)

+ 15 - 0
desktop/core/src/desktop/lib/connectors/lib/__init__.py

@@ -0,0 +1,15 @@
+# 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.

+ 6 - 5
desktop/core/src/desktop/lib/connectors/lib/impala.py

@@ -1,10 +1,11 @@
-class ImpalaConnector():
+
+class Impala():
   NAME = 'impala'
 
   VERSION = 1
   APP = 'notebook'
   INTERFACE = 'hiveserver2'
-  PROPERTIES = {
-    'server_host': '',
-    'server_port': '',
-  }
+  PROPERTIES = [
+    {'name': 'server_host', 'value': ''},
+    {'name': 'server_port', 'value': ''},
+  ]

+ 6 - 1
desktop/core/src/desktop/lib/connectors/urls.py

@@ -17,8 +17,13 @@
 
 from django.conf.urls import url
 
-from desktop.lib.connectors import views
+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/instance/?$', api.connector, name='connectors.api.connector'),
 ]

+ 2 - 55
desktop/core/src/desktop/lib/connectors/views.py

@@ -15,66 +15,13 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import json
 import logging
 
-from desktop.lib.django_util import JsonResponse, render
+from desktop.lib.django_util import render
 
 
 LOG = logging.getLogger(__name__)
 
 
 def index(request):
-  CONNECTORS = {
-    "timestamp": "2019-04-05T23:36:47.533981",
-    "metric": [
-      {"category": "Query Engines", "values": [
-        {"name": "Impala", "id": 1, "instances": []},
-        {"name": "SQL Database", "id": 2, "instances": []},
-        {"name": "Hive", "id": 3, "instances": [1, 2]},
-        {"name": "Hive Tez", "id": 4, "instances": []},
-        {"name": "Hive LLAP", "id": 5, "instances": []},
-        {"name": "Druid", "id": 6, "instances": []},
-        {"name": "Kafka SQL", "id": 6, "instances": []},
-        {"name": "SparkSQL", "id": 6, "instances": []},
-        {"name": "Presto", "id": 6, "instances": []},
-        {"name": "Athena", "id": 6, "instances": []},
-        {"name": "Redshift", "id": 6, "instances": []},
-        {"name": "Big Query", "id": 6, "instances": []},
-        {"name": "Oracle", "id": 6, "instances": []},
-      ]},
-      {"category": "Browsers", "values": [
-        {"name": "HDFS", "id": 30, "instances": []},
-        {"name": "YARN", "id": 30, "instances": []},
-        {"name": "S3", "id": 31, "instances": []},
-        {"name": "ADLS", "id": 32, "instances": []}
-      ]},
-      {"category": "Catalogs", "values": [
-        {"name": "Navigator", "id": 7, "instances": []},
-        {"name": "Atlas", "id": 8, "instances": []}
-      ]},
-      {"category": "Optimizers", "values": [
-        {"name": "Optimizer", "id": 9, "instances": []}
-      ]},
-      {"category": "Schedulers", "values": [
-        {"name": "Oozie", "id": 10, "instances": []},
-        {"name": "Celery", "id": 11, "instances": []}
-      ]},
-      {"category": "Apps", "values": []},
-      {"category": "Plugins", "values": []},
-    ]
-  }
-
-  if request.is_ajax():
-    return JsonResponse(CONNECTORS)
-  else:
-    return render("connectors.mako", request, {'connectors': json.dumps(CONNECTORS), 'is_embeddable': request.GET.get('is_embeddable', False)})
-
-
-def instances(request):
-  insance = json.loads(request.POST.get('id', '{}'))
-
-  return JsonResponse({
-    'name': 'Impala',
-    'settings': []
-  })
+  return render("connectors.mako", request, {})

+ 169 - 80
desktop/core/src/desktop/templates/connectors.mako

@@ -21,20 +21,16 @@ from desktop.views import commonheader, commonfooter
 %>
 
 <%
-MAIN_SCROLLABLE = is_embeddable and "'.page-content'" or "window"
+MAIN_SCROLLABLE = "'.page-content'"
 if conf.CUSTOM.BANNER_TOP_HTML.get():
-  TOP_SNAP = is_embeddable and "78px" or "106px"
+  TOP_SNAP = "78px"
 else:
-  TOP_SNAP = is_embeddable and "50px" or "106px"
+  TOP_SNAP = "50px"
 %>
 
 <%namespace name="actionbar" file="actionbar.mako" />
 <%namespace name="layout" file="about_layout.mako" />
 
-% if not is_embeddable:
-${ commonheader(_('Connectors'), "about", user, request) | n,unicode }
-% endif
-
 
 <script type="text/javascript">
   (function () {
@@ -42,39 +38,61 @@ ${ commonheader(_('Connectors'), "about", user, request) | n,unicode }
       var self = this;
 
       self.apiHelper = window.apiHelper;
-      self.metrics = ko.observableArray();
-      self.selectedMetric = ko.observable('All');
-      self.metricsFilter = ko.observable();
 
-      self.selectedMetrics = ko.pureComputed(function () {
-        return self.metrics().filter(function (metric) {
-          return self.selectedMetric() == 'All' || metric.category == self.selectedMetric();
+      self.section = ko.observable('connectors-page');
+
+      self.instances = ko.observableArray();
+      self.instance = ko.observable();
+
+      self.connectors = ko.observableArray();
+      self.selectedConnectorCategory = ko.observable('All');
+      self.connectorsFilter = ko.observable();
+      self.connector = ko.observable();
+
+      self.selectedConnectors = ko.pureComputed(function () {
+        return self.connectors().filter(function (connector) {
+          return self.selectedConnectorCategory() == 'All' || connector.category == self.selectedConnectorCategory();
         });
       });
-      self.filteredMetrics = ko.pureComputed(function () {
-        var metrics = self.selectedMetrics();
-
-        if (self.metricsFilter()) {
-          var lowerQuery = self.metricsFilter().toLowerCase();
-          var filteredMetrics = []
-          metrics.forEach(function (metric) {
-            var _metric = {"category": metric.category, "values": []};
-            _metric.values = metric.values.filter(function (subMetricKey) {
+      self.filteredConnectors = ko.pureComputed(function () {
+        var connectors = self.selectedConnectors();
+
+        if (self.connectorsFilter()) {
+          var lowerQuery = self.connectorsFilter().toLowerCase();
+          var filteredConnectors = []
+          connectors.forEach(function (connectors) {
+            var _connectors = {"category": connectors.category, "values": []};
+            _connectors.values = connectors.values.filter(function (subMetricKey) {
               return subMetricKey.name.toLowerCase().indexOf(lowerQuery) !== -1;
             });
-            if (_metric.values.length > 0){
-              filteredMetrics.push(_metric);
+            if (_connectors.values.length > 0) {
+              filteredConnectors.push(_connectors);
             }
           });
-          metrics = filteredMetrics;
+          connectors = filteredConnectors;
         }
 
-        return metrics;
+        return connectors;
       });
 
+      self.addNewConnector = function () {
+        self.fetchConnectorTypes();
+        self.section('add-connector-page');
+      };
+
       self.fetchConnectors = function () {
-        self.apiHelper.simpleGet('/desktop/connectors/', {}, {successCallback: function (data) {
-          self.metrics(data.metric);
+        self.apiHelper.simpleGet('/desktop/connectors/api/instances/', {}, {successCallback: function (data) {
+          self.instances(data.connectors);
+        }});
+      };
+      self.fetchConnector = function () {
+        self.apiHelper.simplePost('/desktop/connectors/api/instance/', {'id': 1}, {successCallback: function (data) {
+          self.instance(data.connector);
+        }});
+      };
+      self.fetchConnectorTypes = function () {
+        self.apiHelper.simpleGet('/desktop/connectors/api/types/', {}, {successCallback: function (data) {
+          self.connectors(data.connectors);
         }});
       };
     }
@@ -86,28 +104,129 @@ ${ commonheader(_('Connectors'), "about", user, request) | n,unicode }
   })();
 </script>
 
+
 ${layout.menubar(section='connectors')}
 
+
 <div id="connectorsComponents" class="container-fluid">
+
+  <a href="javascript:void(0)" data-bind="click: function() { section('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 -->
+
+  <!-- ko if: section() == 'add-connector-page' -->
+    <div data-bind="template: { name: 'add-connector-page' }"></div>
+  <!-- /ko -->
+
+</div>
+
+
+<script type="text/html" id="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>${ _('') }</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: ''"></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 -->
+</script>
+
+
+<script type="text/html" id="connector-page">
+  <div class="row-fluid">
+    <span data-bind="text: name"></span>
+    <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 data-bind="text: value"></td>
+        </tr>
+      </tbody>
+    </table>
+  </div>
+</script>
+
+
+<script type="text/html" id="add-connector-page">
   <div class="card card-small margin-top-10">
-    <div data-bind="dockable: { scrollable: ${ MAIN_SCROLLABLE }, jumpCorrection: 0,topSnap: '${ TOP_SNAP }', triggerAdjust: ${ is_embeddable and "0" or "106" }}">
-      <ul class="nav nav-pills">
-        <li data-bind="css: { 'active': $root.selectedMetric() === 'All' }">
-          <a href="javascript:void(0)" data-bind="text: 'All', click: function(){ $root.selectedMetric('All') }"></a>
-        </li>
-        <!-- ko foreach: filteredMetrics() -->
-        <li data-bind="css: { 'active': $root.selectedMetric() === category }">
-          <a href="javascript:void(0)" data-bind="text: category, click: function(){ $root.selectedMetric(category) }"></a>
-        </li>
-        <!-- /ko -->
-      </ul>
-      <input type="text" data-bind="clearable: metricsFilter, valueUpdate: 'afterkeydown'"
-          class="input-xlarge pull-right margin-bottom-10" placeholder="${ _('Filter connectors...') }">
-    </div>
+      <div data-bind="dockable: { scrollable: ${ MAIN_SCROLLABLE }, jumpCorrection: 0, topSnap: '${ TOP_SNAP }', triggerAdjust: 0 }">
+        <ul class="nav nav-pills">
+          <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>
+          </li>
+          <!-- /ko -->
+        </ul>
+        <input type="text" data-bind="clearable: connectorsFilter, valueUpdate: 'afterkeydown'"
+            class="input-xlarge pull-right margin-bottom-10" placeholder="${ _('Filter connectors...') }">
+      </div>
 
-    <div class="margin-top-10">
-      <div data-bind="foreach: filteredMetrics()">
-        <h4 data-bind="text: category"></h4>
+      <div class="margin-top-10">
+        <div data-bind="foreach: filteredConnectors()">
+          <h4 data-bind="text: category"></h4>
+          <table class="table table-condensed">
+            <thead>
+              <tr>
+                <th width="30%">${ _('Name') }</th>
+                <th>${ _('Instances') }</th>
+              </tr>
+            </thead>
+            <!-- ko if: $data.values -->
+            <tbody data-bind="foreach: values">
+              <tr data-bind="click: function() { $root.instance(name); }">
+                <td data-bind="text: name"></td>
+                <td data-bind="text: instances.length > 0 ? instances.length : ''"></td>
+              </tr>
+            </tbody>
+            <!-- /ko -->
+            <!-- ko ifnot: $data.values -->
+            <tfoot>
+              <tr>
+                <td colspan="2">${ _('There are no connectors matching your filter') }</td>
+              </tr>
+            </tfoot>
+            <!-- /ko -->
+          </table>
+        </div>
+
+        <!-- ko if: filteredConnectors().length == 0 -->
         <table class="table table-condensed">
           <thead>
             <tr>
@@ -115,43 +234,13 @@ ${layout.menubar(section='connectors')}
               <th>${ _('Instances') }</th>
             </tr>
           </thead>
-          <!-- ko if: $data.values -->
-          <tbody data-bind="foreach: values">
-            <tr>
-              <td data-bind="text: name"></td>
-              <td data-bind="text: instances.length > 0 ? instances.length : ''"></td>
-            </tr>
-          </tbody>
-          <!-- /ko -->
-          <!-- ko ifnot: $data.values -->
           <tfoot>
             <tr>
-              <td colspan="2">${ _('There are no metrics matching your filter') }</td>
+              <td colspan="2">${ _('There are no connectors matching your filter') }</td>
             </tr>
           </tfoot>
-          <!-- /ko -->
-          </table>
+        </table>
+        <!-- /ko -->
       </div>
-
-      <!-- ko if: filteredMetrics().length == 0 -->
-      <table class="table table-condensed">
-        <thead>
-          <tr>
-            <th width="30%">${ _('Name') }</th>
-            <th>${ _('Instances') }</th>
-          </tr>
-        </thead>
-        <tfoot>
-          <tr>
-            <td colspan="2">${ _('There are no metrics matching your filter') }</td>
-          </tr>
-        </tfoot>
-      </table>
-      <!-- /ko -->
-  </div>
-</div>
-
-
-% if not is_embeddable:
- ${ commonfooter(request, messages) | n,unicode }
-% endif
+    </div>
+</script>

+ 1 - 1
desktop/libs/indexer/src/indexer/templates/indexes.mako

@@ -138,7 +138,7 @@ ${ assist.assistPanel() }
 
 
           <div class="indexer-main">
-            <!-- ko template: { name: 'indexes-breadcrumbs' }--><!-- /ko -->
+            <!-- ko template: { name: 'indexes-breadcrumbs' } --><!-- /ko -->
 
             <!-- ko if: section() === 'list-indexes' -->
             <%actionbar:render>