Преглед на файлове

HUE-4546 [editor] Cannot execute any Hive query: 'NoneType' object has no attribute 'update_data'

Auto-strip invalid characters from name field of converted docs
Jenny Kim преди 9 години
родител
ревизия
40c6c45d22

+ 34 - 0
desktop/core/src/desktop/converter_tests.py

@@ -153,6 +153,40 @@ class TestDocumentConverter(object):
       query.delete()
       query2.delete()
 
+  def test_convert_hive_query_with_invalid_name(self):
+    sql = 'SELECT * FROM sample_07'
+    settings = [
+      {'key': 'hive.exec.scratchdir', 'value': '/tmp/mydir'},
+      {'key': 'hive.querylog.location', 'value': '/tmp/doc2'}
+    ]
+    file_resources = [{'type': 'jar', 'path': '/tmp/doc2/test.jar'}]
+    functions = [{'name': 'myUpper', 'class_name': 'org.hue.udf.MyUpper'}]
+    design = hql_query(sql, database='etl', settings=settings, file_resources=file_resources, functions=functions)
+
+    query = SavedQuery.objects.create(
+      type=SavedQuery.TYPES_MAPPING['hql'],
+      owner=self.user,
+      data=design.dumps(),
+      name='Test / Hive query',
+      desc='Test Hive query'
+    )
+    doc = Document.objects.link(query, owner=query.owner, extra=query.type, name=query.name, description=query.desc)
+
+    try:
+      # Test that corresponding doc2 is created after convert
+      assert_equal(0, Document2.objects.filter(owner=self.user, type='query-hive').count())
+
+      converter = DocumentConverter(self.user)
+      converter.convert()
+
+      assert_equal(1, Document2.objects.filter(owner=self.user, type='query-hive').count())
+
+      doc2 = Document2.objects.get(owner=self.user, type='query-hive', is_history=False)
+      # Verify Document2 name is stripped of invalid chars
+      assert_equal('Test  Hive query', doc2.data_dict['name'])
+    finally:
+      query.delete()
+
 
   def test_convert_impala_query(self):
     sql = 'SELECT * FROM sample_07'

+ 7 - 2
desktop/core/src/desktop/converters.py

@@ -17,13 +17,15 @@
 
 import json
 import logging
+import re
 import time
 
 from django.db import transaction
 from django.utils.translation import ugettext as _
 
 from desktop.lib.exceptions_renderable import PopupException
-from desktop.models import Document, DocumentPermission, DocumentTag, Document2, Directory, Document2Permission
+from desktop.models import Document, DocumentPermission, DocumentTag, Document2, Directory, Document2Permission, \
+  DOC2_NAME_INVALID_CHARS
 from notebook.api import _historify
 from notebook.models import import_saved_beeswax_query
 
@@ -202,10 +204,13 @@ class DocumentConverter(object):
   def _create_doc2(self, document, doctype, name=None, description=None, data=None):
     try:
       with transaction.atomic():
+        name = name if name else document.name
+        name = re.sub(DOC2_NAME_INVALID_CHARS, '', name)
+
         document2 = Document2.objects.create(
           owner=self.user,
           parent_directory=self._get_parent_directory(document),
-          name=name if name else document.name,
+          name=name,
           type=doctype,
           description=description,
           data=data

+ 5 - 2
desktop/core/src/desktop/models.py

@@ -53,6 +53,9 @@ SAMPLE_USER_OWNERS = ['hue', 'sample']
 UTC_TIME_FORMAT = "%Y-%m-%dT%H:%MZ"
 HUE_VERSION = None
 
+DOC2_NAME_INVALID_CHARS = "[<>/~`]"
+
+
 def uuid_default():
   return str(uuid.uuid4())
 
@@ -1186,9 +1189,9 @@ class Document2(models.Model):
 
   def validate(self):
     # Validate document name
-    invalid_chars = re.findall(re.compile(r"[<>/~`]"), self.name)
+    invalid_chars = re.findall(re.compile(DOC2_NAME_INVALID_CHARS), self.name)
     if invalid_chars:
-      raise FilesystemException(_('Document %s contains some special characters: %s') % (self.name, invalid_chars))
+      raise FilesystemException(_('Document %s contains some special characters: %s') % (self.name, ', '.join(invalid_chars)))
 
     # Validate home and Trash directories are only created once per user and cannot be created or modified after
     if self.name in [Document2.HOME_DIR, Document2.TRASH_DIR] and self.type == 'directory' and \

+ 1 - 1
desktop/core/src/desktop/tests_doc2.py

@@ -321,7 +321,7 @@ class TestDocument2(object):
     response = self.client.post('/desktop/api2/doc/mkdir', {'parent_uuid': json.dumps(self.home_dir.uuid), 'name': json.dumps(invalid_name)})
     data = json.loads(response.content)
     assert_equal(-1, data['status'], data)
-    assert_true("contains some special characters: [u'/']" in data['message'])
+    assert_true("contains some special characters: /" in data['message'])
 
 
   def test_validate_immutable_user_directories(self):

+ 8 - 7
desktop/libs/notebook/src/notebook/api.py

@@ -127,13 +127,14 @@ def _execute_notebook(request, notebook, snippet):
         else:
           _snippet['status'] = 'failed'
 
-        history.update_data(notebook)
-        history.save()
-
-        response['history_id'] = history.id
-        response['history_uuid'] = history.uuid
-        if notebook['isSaved']: # Keep track of history of saved queries
-          response['history_parent_uuid'] = history.dependencies.filter(type__startswith='query-').latest('last_modified').uuid
+        if history:  # If _historify failed, history will be None
+          history.update_data(notebook)
+          history.save()
+
+          response['history_id'] = history.id
+          response['history_uuid'] = history.uuid
+          if notebook['isSaved']: # Keep track of history of saved queries
+            response['history_parent_uuid'] = history.dependencies.filter(type__startswith='query-').latest('last_modified').uuid
   except QueryError, ex: # We inject the history information from _historify() to the failed queries
     if response.get('history_id'):
       ex.extra['history_id'] = response['history_id']

+ 3 - 1
desktop/libs/notebook/src/notebook/models.py

@@ -18,11 +18,13 @@
 import json
 import math
 import numbers
+import re
 import uuid
 
 from django.utils.html import escape
 
 from desktop.lib.i18n import smart_unicode
+from desktop.models import DOC2_NAME_INVALID_CHARS
 
 from notebook.connectors.base import Notebook
 
@@ -182,7 +184,7 @@ def import_saved_beeswax_query(bquery):
   design = bquery.get_design()
 
   return make_notebook(
-      name=bquery.name,
+      name=re.sub(DOC2_NAME_INVALID_CHARS, '', bquery.name),
       description=bquery.desc,
       editor_type=_convert_type(bquery.type, bquery.data),
       statement=design.hql_query,