Browse Source

HUE-8758 [connector] Refactoring for connector connection testing

Romain 5 years ago
parent
commit
91693a43ba

+ 13 - 5
desktop/core/src/desktop/lib/connectors/api.py

@@ -21,13 +21,14 @@ import logging
 from django.utils.translation import ugettext as _
 
 from useradmin.models import update_app_permissions
-from notebook.conf import config_validator
+from notebook.conf import config_validator, _connector_to_iterpreter
 
 from desktop.auth.decorators import admin_required
 from desktop.decorators import api_error_handler
 from desktop.lib.django_util import JsonResponse, render
 from desktop.lib.exceptions_renderable import PopupException
-from desktop.lib.connectors.models import _get_installed_connectors, get_connectors_types, Connector, _create_connector_examples
+from desktop.lib.connectors.models import _get_installed_connectors, get_connectors_types, Connector, _create_connector_examples, \
+    _augment_connector_properties
 from desktop.lib.connectors.types import get_connectors_types, get_connector_categories, get_connector_by_type
 
 
@@ -117,10 +118,17 @@ def delete_connector(request):
 def test_connector(request):
   connector = json.loads(request.POST.get('connector', '{}'))
 
-  connectors = [connector]
-  warnings = config_validator(user=request.user, connectors=connectors)
+  # Currently only Editor connectors are supported.
+  interpreter = _connector_to_iterpreter(
+    _augment_connector_properties(connector)
+  )
 
-  return JsonResponse({'warnings': warnings})
+  warnings = ''.join([
+    ''.join(warning)
+    for warning in config_validator(user=request.user, interpreters=[interpreter])
+  ])
+
+  return JsonResponse({'warnings': warnings, 'hasWarnings': bool(warnings)})
 
 
 @admin_required

+ 35 - 23
desktop/core/src/desktop/lib/connectors/models.py

@@ -101,7 +101,7 @@ def _get_installed_connectors(category=None, categories=None, dialect=None, inte
   if not Connector.objects.exists() and user and is_hue_admin(user):
     _create_connector_examples()
 
-  connectors = []
+
   connectors_objects = Connector.objects.all()
   if user is not None and not is_admin(user):  # Apply Permissions
     connectors_objects = connectors_objects.filter(huepermission__in=user.get_permissions())
@@ -118,34 +118,17 @@ def _get_installed_connectors(category=None, categories=None, dialect=None, inte
       }
       for connector in connectors_objects
   ]
+  connectors = []
 
   for connector in connector_instances:
-    connector_types = []
-
-    for connector_type in get_connectors_types():
-      if connector_type['dialect'] == connector['dialect']:
-        connector_types.insert(0, connector_type)
-      elif connector['interface'] and connector_type.get('interface') == connector['interface']:
-        connector_types.append(connector_type)
-
-    if not connector_types:
+    full_connector = _augment_connector_properties(connector)
+    if full_connector:
+      connectors.append(full_connector)
+    else:
       LOG.warn('Skipping connector %(id)s as connector dialect %(dialect)s or interface %(interface)s are not installed' % (
           {'id': connector['id'], 'dialect': connector['dialect'], 'interface': connector['interface']}
         )
       )
-    else:
-      connector_type = connector_types[0]
-      connectors.append({
-        'nice_name': connector['nice_name'],
-        'name': str(connector['id']),
-        'dialect': connector['dialect'],
-        'interface': connector['interface'] or connector_type['interface'],
-        'settings': connector['settings'],
-        'id': str(connector['id']),
-        'category': connector_type['category'],
-        'description': connector_type['description'],
-        'dialect_properties': connector_type.get('properties', {})
-      })
 
   if categories is not None:
     connectors = [connector for connector in connectors if connector['category'] in categories]
@@ -159,6 +142,35 @@ def _get_installed_connectors(category=None, categories=None, dialect=None, inte
   return connectors
 
 
+def _augment_connector_properties(connector):
+  '''
+  Add the connector properties based on the dialect type to each connector.
+  The connector type must exist in desktop/core/src/desktop/lib/connectors/types.py.
+  '''
+
+  connector_types = []
+
+  for connector_type in get_connectors_types():
+    if connector_type['dialect'] == connector['dialect']:
+      connector_types.insert(0, connector_type)
+    elif connector['interface'] and connector_type.get('interface') == connector['interface']:
+      connector_types.append(connector_type)
+
+  if connector_types:
+    connector_type = connector_types[0]
+    return {
+      'nice_name': connector['nice_name'],
+      'name': str(connector['id']),
+      'dialect': connector['dialect'],
+      'interface': connector['interface'] or connector_type['interface'],
+      'settings': connector['settings'],
+      'id': str(connector['id']),
+      'category': connector_type['category'],
+      'description': connector_type['description'],
+      'dialect_properties': connector_type.get('properties', {})
+    }
+
+
 def _create_connector_examples():
   for connector in _get_connector_examples():
     Connector.objects.create(

+ 26 - 1
desktop/core/src/desktop/templates/connectors/connectors.mako

@@ -44,6 +44,9 @@ else:
       self.selectedConnectorCategory = ko.observable('All');
       self.connectorsFilter = ko.observable();
 
+      self.testConnectionExecuted = ko.observable(false);
+      self.testConnectionErrors = ko.observable('');
+
       // Handle two types of objects are being listed: connector instances and types
       self.instances = ko.observableArray(); // Connector instances (e.g. connector to a MySql DB on a certain host)
       self.instance = ko.observable();
@@ -83,6 +86,8 @@ else:
       });
 
       self.addNewConnector = function () {
+        self.testConnectionExecuted(false);
+        self.testConnectionErrors('');
         self.section('add-connector-page');
       };
 
@@ -148,6 +153,19 @@ else:
           }
         });
       };
+      self.testConnector = function (connector) {
+        self.testConnectionExecuted(false);
+        self.testConnectionErrors('');
+
+        self.apiHelper.simplePost('/desktop/connectors/api/instance/test', {
+            'connector': ko.mapping.toJSON(connector)
+          }, {
+          successCallback: function (data) {
+            self.testConnectionExecuted(true);
+            self.testConnectionErrors(data.warnings);
+          }
+        });
+      };
     }
 
     $(document).ready(function () {
@@ -320,9 +338,16 @@ ${ layout.menubar(section='connectors') }
         </a>
       <!-- /ko -->
     <!-- /ko -->
-    <a href="javascript:void(0)">
+    <a href="javascript:void(0)" data-bind="click: $root.testConnector">
       ${ _('Test connection') }
     </a>
+    <span>
+      <i class="fa fa-question" data-bind="visible: !$root.testConnectionExecuted()"></i>
+      <i class="fa fa-check" data-bind="visible: $root.testConnectionExecuted() && $root.testConnectionErrors().length == 0"></i>
+      <i class="fa fa-exclamation" data-bind="visible: $root.testConnectionExecuted() && $root.testConnectionErrors().length != 0"></i>
+      <span data-bind="visible: $root.testConnectionExecuted() && $root.testConnectionErrors().length != 0, text: $root.testConnectionErrors">
+      </span>
+    </span>
     <table class="table table-condensed">
       <thead>
         <tr>

+ 19 - 13
desktop/libs/notebook/src/notebook/conf.py

@@ -54,19 +54,25 @@ def check_permissions(user, interpreter, user_apps=None):
          (interpreter in ('java', 'spark2', 'mapreduce', 'shell', 'sqoop1', 'distcp') and 'oozie' not in user_apps)
 
 
+def _connector_to_iterpreter(connector):
+  return {
+      'name': connector['nice_name'],
+      'type': connector['name'],  # Aka id
+      'dialect': connector['dialect'],
+      'category': connector['category'],
+      'is_sql': connector['dialect_properties']['is_sql'],
+      'interface': connector['interface'],
+      'options': {setting['name']: setting['value'] for setting in connector['settings']},
+      'dialect_properties': connector['dialect_properties'],
+  }
+
+
 def get_ordered_interpreters(user=None):
   if has_connectors():
     from desktop.lib.connectors.api import _get_installed_connectors
-    interpreters = [{
-        'name': connector['nice_name'],
-        'type': connector['name'],
-        'dialect': connector['dialect'],
-        'category': connector['category'],
-        'is_sql': connector.get('is_sql', False),
-        'interface': connector['interface'],
-        'options': {setting['name']: setting['value'] for setting in connector['settings']},
-        'dialect_properties': connector['dialect_properties'],
-      } for connector in _get_installed_connectors(categories=['editor', 'catalogs'], user=user)
+    interpreters = [
+      _connector_to_iterpreter(connector)
+      for connector in _get_installed_connectors(categories=['editor', 'catalogs'], user=user)
     ]
   else:
     if not INTERPRETERS.get():
@@ -312,7 +318,7 @@ def _default_interpreters(user):
   INTERPRETERS.set_for_testing(OrderedDict(interpreters))
 
 
-def config_validator(user, connectors=None):
+def config_validator(user, interpreters=None):
   res = []
 
   if not has_connectors():
@@ -324,10 +330,10 @@ def config_validator(user, connectors=None):
   if not user.is_authenticated():
     res.append(('Editor', _('Could not authenticate with user %s to validate interpreters') % user))
 
-  if connectors is None:
+  if interpreters is None:
     connectors = get_ordered_interpreters(user=user)
 
-  for interpreter in connectors:
+  for interpreter in interpreters:
     if interpreter.get('is_sql'):
       connector_id = interpreter['type']