Răsfoiți Sursa

HUE-7738 [editor] Switch to async function resolution in the autocomplete results

Johan Ahlen 5 ani în urmă
părinte
comite
5840203300

+ 58 - 47
desktop/core/src/desktop/js/sql/autocompleteResults.js

@@ -29,7 +29,7 @@ import { matchesType } from 'sql/reference/typeUtils';
 import { DIALECT } from 'apps/notebook2/snippet';
 import { cancelActiveRequest } from 'api/apiUtils';
 import { findBrowserConnector, getRootFilePath } from 'utils/hueConfig';
-import { getArgumentTypes } from './reference/sqlReferenceRepository';
+import { getArgumentTypes, getFunctionsWithReturnTypes } from './reference/sqlReferenceRepository';
 
 const normalizedColors = HueColors.getNormalizedColors();
 
@@ -476,7 +476,7 @@ class AutocompleteResults {
     self.handleColumnAliases();
     self.handleCommonTableExpressions();
     self.handleOptions();
-    self.handleFunctions(colRefDeferred);
+    self.activeDeferrals.push(self.handleFunctions(colRefDeferred));
     self.handleDatabases(databasesDeferred);
     const tablesDeferred = self.handleTables(databasesDeferred);
     self.activeDeferrals.push(tablesDeferred);
@@ -721,6 +721,7 @@ class AutocompleteResults {
 
   handleFunctions(colRefDeferred) {
     const self = this;
+    const functionsDeferred = $.Deferred();
     if (self.parseResult.suggestFunctions) {
       const functionSuggestions = [];
       if (
@@ -729,61 +730,71 @@ class AutocompleteResults {
       ) {
         initLoading(self.loadingFunctions, colRefDeferred);
 
-        colRefDeferred.done(colRef => {
-          const functionsToSuggest = SqlFunctions.getFunctionsWithReturnTypes(
-            self.dialect(),
-            [colRef.type.toUpperCase()],
-            self.parseResult.suggestAggregateFunctions || false,
-            self.parseResult.suggestAnalyticFunctions || false
-          );
+        colRefDeferred
+          .done(async colRef => {
+            const functionsToSuggest = await getFunctionsWithReturnTypes(
+              self.snippet.connector(),
+              [colRef.type.toUpperCase()],
+              self.parseResult.suggestAggregateFunctions || false,
+              self.parseResult.suggestAnalyticFunctions || false
+            );
 
-          Object.keys(functionsToSuggest).forEach(name => {
-            functionSuggestions.push({
-              category: CATEGORIES.UDF,
-              value: name + '()',
-              meta: functionsToSuggest[name].returnTypes.join('|'),
-              weightAdjust:
-                colRef.type.toUpperCase() !== 'T' &&
-                functionsToSuggest[name].returnTypes.some(otherType => {
-                  return otherType === colRef.type.toUpperCase();
-                })
-                  ? 1
-                  : 0,
-              popular: ko.observable(false),
-              details: functionsToSuggest[name]
+            Object.keys(functionsToSuggest).forEach(name => {
+              functionSuggestions.push({
+                category: CATEGORIES.UDF,
+                value: name + '()',
+                meta: functionsToSuggest[name].returnTypes.join('|'),
+                weightAdjust:
+                  colRef.type.toUpperCase() !== 'T' &&
+                  functionsToSuggest[name].returnTypes.some(otherType => {
+                    return otherType === colRef.type.toUpperCase();
+                  })
+                    ? 1
+                    : 0,
+                popular: ko.observable(false),
+                details: functionsToSuggest[name]
+              });
             });
-          });
 
-          self.appendEntries(functionSuggestions);
-        });
+            self.appendEntries(functionSuggestions);
+            functionsDeferred.resolve();
+          })
+          .fail(functionsDeferred.reject);
       } else {
         const types = self.parseResult.suggestFunctions.types || ['T'];
-        const functionsToSuggest = SqlFunctions.getFunctionsWithReturnTypes(
-          self.dialect(),
+
+        getFunctionsWithReturnTypes(
+          self.snippet.connector(),
           types,
           self.parseResult.suggestAggregateFunctions || false,
           self.parseResult.suggestAnalyticFunctions || false
-        );
-
-        Object.keys(functionsToSuggest).forEach(name => {
-          functionSuggestions.push({
-            category: CATEGORIES.UDF,
-            value: name + '()',
-            meta: functionsToSuggest[name].returnTypes.join('|'),
-            weightAdjust:
-              types[0].toUpperCase() !== 'T' &&
-              functionsToSuggest[name].returnTypes.some(otherType => {
-                return otherType === types[0].toUpperCase();
-              })
-                ? 1
-                : 0,
-            popular: ko.observable(false),
-            details: functionsToSuggest[name]
-          });
-        });
-        self.appendEntries(functionSuggestions);
+        )
+          .then(functionsToSuggest => {
+            Object.keys(functionsToSuggest).forEach(name => {
+              functionSuggestions.push({
+                category: CATEGORIES.UDF,
+                value: name + '()',
+                meta: functionsToSuggest[name].returnTypes.join('|'),
+                weightAdjust:
+                  types[0].toUpperCase() !== 'T' &&
+                  functionsToSuggest[name].returnTypes.some(otherType => {
+                    return otherType === types[0].toUpperCase();
+                  })
+                    ? 1
+                    : 0,
+                popular: ko.observable(false),
+                details: functionsToSuggest[name]
+              });
+            });
+            self.appendEntries(functionSuggestions);
+            functionsDeferred.resolve();
+          })
+          .catch(functionsDeferred.reject);
       }
+    } else {
+      functionsDeferred.resolve();
     }
+    return functionsDeferred;
   }
 
   handleDatabases(databasesDeferred) {

+ 23 - 3
desktop/core/src/desktop/js/sql/autocompleteResults.test.js

@@ -22,6 +22,8 @@ import dataCatalog from 'catalog/dataCatalog';
 import huePubSub from 'utils/huePubSub';
 import I18n from 'utils/i18n';
 import LOTS_OF_PARSE_RESULTS from './test/lotsOfParseResults';
+import * as sqlReferenceRepository from 'sql/reference/sqlReferenceRepository';
+import { defer } from '../utils/hueUtils';
 
 describe('AutocompleteResults.js', () => {
   const failResponse = {
@@ -339,16 +341,34 @@ describe('AutocompleteResults.js', () => {
     expect(subject.filtered()[1].value).toBe('foo');
   });
 
-  it('should handle parse results with functions', () => {
+  it('should handle parse results with functions', async () => {
     subject.entries([]);
 
+    const spy = spyOn(sqlReferenceRepository, 'getFunctionsWithReturnTypes').and.callFake(
+      async () =>
+        Promise.resolve({
+          count: {
+            returnTypes: ['BIGINT'],
+            arguments: [[{ type: 'T' }]],
+            signature: 'count(col)',
+            draggable: 'count()',
+            description: 'some desc'
+          }
+        })
+    );
+
     expect(subject.filtered().length).toBe(0);
-    subject.update({
+
+    await subject.update({
       lowerCase: false,
       suggestFunctions: {}
     });
 
-    expect(subject.filtered().length).toBeGreaterThan(0);
+    await defer();
+
+    expect(spy).toHaveBeenCalled();
+
+    expect(subject.filtered().length).toEqual(1);
     expect(subject.filtered()[0].details.arguments).toBeDefined();
     expect(subject.filtered()[0].details.signature).toBeDefined();
     expect(subject.filtered()[0].details.description).toBeDefined();

+ 3 - 1
desktop/core/src/desktop/js/sql/reference/generic/udfReference.js

@@ -49,4 +49,6 @@ export const AGGREGATE_FUNCTIONS = {
   }
 };
 
-export const UDF_CATEGORIES = [{ name: I18n('Aggregate'), functions: AGGREGATE_FUNCTIONS }];
+export const UDF_CATEGORIES = [
+  { name: I18n('Aggregate'), isAggregate: true, functions: AGGREGATE_FUNCTIONS }
+];

+ 2 - 2
desktop/core/src/desktop/js/sql/reference/hive/udfReference.js

@@ -1692,8 +1692,8 @@ const ANALYTIC_FUNCTIONS = {
 };
 
 export const UDF_CATEGORIES = [
-  { name: I18n('Aggregate'), functions: AGGREGATE_FUNCTIONS },
-  { name: I18n('Analytic'), functions: ANALYTIC_FUNCTIONS },
+  { name: I18n('Aggregate'), isAggregate: true, functions: AGGREGATE_FUNCTIONS },
+  { name: I18n('Analytic'), isAnalytic: true, functions: ANALYTIC_FUNCTIONS },
   { name: I18n('Collection'), functions: COLLECTION_FUNCTIONS },
   { name: I18n('Complex Type'), functions: COMPLEX_TYPE_CONSTRUCTS },
   { name: I18n('Conditional'), functions: CONDITIONAL_FUNCTIONS },

+ 2 - 2
desktop/core/src/desktop/js/sql/reference/impala/udfReference.js

@@ -1861,8 +1861,8 @@ const BIT_FUNCTIONS = {
 };
 
 export const UDF_CATEGORIES = [
-  { name: I18n('Aggregate'), functions: AGGREGATE_FUNCTIONS },
-  { name: I18n('Analytic'), functions: ANALYTIC_FUNCTIONS },
+  { name: I18n('Aggregate'), isAggregate: true, functions: AGGREGATE_FUNCTIONS },
+  { name: I18n('Analytic'), isAnalytic: true, functions: ANALYTIC_FUNCTIONS },
   { name: I18n('Bit'), functions: BIT_FUNCTIONS },
   { name: I18n('Conditional'), functions: CONDITIONAL_FUNCTIONS },
   { name: I18n('Date'), functions: DATE_FUNCTIONS },

+ 27 - 0
desktop/core/src/desktop/js/sql/reference/sqlReferenceRepository.js

@@ -14,6 +14,8 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+import { matchesType } from './typeUtils';
+
 const SET_REFS = {
   impala: async () => import(/* webpackChunkName: "impala-ref" */ './impala/setReference')
 };
@@ -50,6 +52,31 @@ export const findFunction = async (connector, functionName) => {
   return found;
 };
 
+export const getFunctionsWithReturnTypes = async (
+  connector,
+  returnTypes,
+  includeAggregate,
+  includeAnalytic
+) => {
+  const categories = await getUdfCategories(connector);
+  const result = {};
+  categories.forEach(category => {
+    if (
+      (!category.isAnalytic && !category.isAggregate) ||
+      (includeAggregate && category.isAggregate) ||
+      (includeAnalytic && category.isAnalytic)
+    ) {
+      Object.keys(category.functions).forEach(udfName => {
+        const udf = category.functions[udfName];
+        if (!returnTypes || matchesType(connector, returnTypes, udf.returnTypes)) {
+          result[udfName] = udf;
+        }
+      });
+    }
+  });
+  return result;
+};
+
 export const getArgumentTypes = async (connector, functionName, argumentPosition) => {
   const foundFunction = await findFunction(connector, functionName);
   if (!foundFunction) {

+ 0 - 54
desktop/core/src/desktop/js/sql/sqlFunctions.js

@@ -4786,59 +4786,6 @@ const SqlFunctions = (function() {
     return false;
   };
 
-  const addFunctions = function(functionIndex, dialect, returnTypes, result) {
-    const indexForDialect = functionIndex[dialect || 'generic'];
-    if (indexForDialect) {
-      Object.keys(indexForDialect).forEach(funcName => {
-        const func = indexForDialect[funcName];
-        if (
-          typeof returnTypes === 'undefined' ||
-          matchesType(dialect, returnTypes, func.returnTypes)
-        ) {
-          result[funcName] = func;
-        }
-      });
-    }
-    if (functionIndex.shared) {
-      Object.keys(functionIndex.shared).forEach(funcName => {
-        const func = functionIndex.shared[funcName];
-        if (
-          typeof returnTypes === 'undefined' ||
-          matchesType(dialect, returnTypes, func.returnTypes)
-        ) {
-          result[funcName] = func;
-        }
-      });
-    }
-  };
-
-  const getFunctionsWithReturnTypes = function(
-    dialect,
-    returnTypes,
-    includeAggregate,
-    includeAnalytic
-  ) {
-    const result = {};
-    addFunctions(BIT_FUNCTIONS, dialect, returnTypes, result);
-    addFunctions(COLLECTION_FUNCTIONS, dialect, returnTypes, result);
-    addFunctions(CONDITIONAL_FUNCTIONS, dialect, returnTypes, result);
-    addFunctions(COMPLEX_TYPE_CONSTRUCTS, dialect, returnTypes, result);
-    addFunctions(DATE_FUNCTIONS, dialect, returnTypes, result);
-    addFunctions(MATHEMATICAL_FUNCTIONS, dialect, returnTypes, result);
-    addFunctions(TYPE_CONVERSION_FUNCTIONS, dialect, returnTypes, result);
-    addFunctions(STRING_FUNCTIONS, dialect, returnTypes, result);
-    addFunctions(DATA_MASKING_FUNCTIONS, dialect, returnTypes, result);
-    addFunctions(MISC_FUNCTIONS, dialect, returnTypes, result);
-    addFunctions(TABLE_GENERATING_FUNCTIONS, dialect, returnTypes, result);
-    if (includeAggregate) {
-      addFunctions(AGGREGATE_FUNCTIONS, dialect, returnTypes, result);
-    }
-    if (includeAnalytic) {
-      addFunctions(ANALYTIC_FUNCTIONS, dialect, returnTypes, result);
-    }
-    return result;
-  };
-
   const findFunction = function(dialect, functionName) {
     return (
       BIT_FUNCTIONS[dialect][functionName] ||
@@ -4900,7 +4847,6 @@ const SqlFunctions = (function() {
   return {
     getArgumentTypes: getArgumentTypes,
     CATEGORIZED_FUNCTIONS: CATEGORIZED_FUNCTIONS,
-    getFunctionsWithReturnTypes: getFunctionsWithReturnTypes,
     getReturnTypes: getReturnTypes,
     matchesType: matchesType,
     findFunction: findFunction