diff --git a/.eslintrc.js b/.eslintrc.js index c843d8f7d0d..c650fb7c34e 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -2,7 +2,6 @@ const normalGlobals = []; const hueGlobals = [ // global_js_constants.mako - 'AUTOCOMPLETE_TIMEOUT', 'CACHEABLE_TTL', 'CSRF_TOKEN', 'DOCUMENT_TYPES', diff --git a/apps/beeswax/src/beeswax/templates/execute.mako b/apps/beeswax/src/beeswax/templates/execute.mako index b77e99d0ac0..f841e0ef11e 100644 --- a/apps/beeswax/src/beeswax/templates/execute.mako +++ b/apps/beeswax/src/beeswax/templates/execute.mako @@ -1173,7 +1173,6 @@ var autocompleter = new AceAutocompleteWrapper({ user: HIVE_AUTOCOMPLETE_USER, oldEditor: true, optEnabled: false, - timeout: AUTOCOMPLETE_TIMEOUT }); var truncateOutput = function (obj) { diff --git a/desktop/conf.dist/hue.ini b/desktop/conf.dist/hue.ini index 4e4cf3ce4d7..d1f943ab7bc 100644 --- a/desktop/conf.dist/hue.ini +++ b/desktop/conf.dist/hue.ini @@ -239,9 +239,11 @@ http_500_debug_mode=false # Choose whether to allow multi tenancy or not. ## enable_organizations=false -# Editor autocomplete timeout (ms) when fetching columns, fields, tables etc. -# To disable this type of autocompletion set the value to 0. -## editor_autocomplete_timeout=30000 +# Choose whether the editor autocomplete should gather suggestions from external source or not. The editor +# autocomplete uses various sources for its suggestions, listing databases, tables, columns files etc. The results are +# cached on the client (see cacheable_ttl) so the calls are kept to a minimum but if you prefer to disable these calls +# all together from the editor set this to true. +## disable_source_autocomplete=false # Enable saved default configurations for Hive, Impala, Spark, and Oozie. ## use_default_configuration=false diff --git a/desktop/conf/pseudo-distributed.ini.tmpl b/desktop/conf/pseudo-distributed.ini.tmpl index 1016cf7f04c..601b675eebe 100644 --- a/desktop/conf/pseudo-distributed.ini.tmpl +++ b/desktop/conf/pseudo-distributed.ini.tmpl @@ -244,9 +244,11 @@ # Choose whether to allow multi tenancy or not. ## enable_organizations=false - # Editor autocomplete timeout (ms) when fetching columns, fields, tables etc. - # To disable this type of autocompletion set the value to 0. - ## editor_autocomplete_timeout=30000 + # Choose whether the editor autocomplete should gather suggestions from external source or not. The editor + # autocomplete uses various sources for its suggestions, listing databases, tables, columns files etc. The results are + # cached on the client (see cacheable_ttl) so the calls are kept to a minimum but if you prefer to disable these calls + # all together from the editor set this to true. + ## disable_source_autocomplete=false # Enable saved default configurations for Hive, Impala, Spark, and Oozie. ## use_default_configuration=false diff --git a/desktop/core/src/desktop/conf.py b/desktop/core/src/desktop/conf.py index 14331110c66..0ea2590b876 100644 --- a/desktop/core/src/desktop/conf.py +++ b/desktop/core/src/desktop/conf.py @@ -1926,11 +1926,11 @@ def get_instrumentation_default(): help=_('Choose whether to enable SQL syntax check or not.') ) -EDITOR_AUTOCOMPLETE_TIMEOUT = Config( - key='editor_autocomplete_timeout', - type=int, - default=30000, - help=_('Timeout value in ms for autocomplete of columns, tables, values etc. 0 = disabled.') +DISABLE_SOURCE_AUTOCOMPLETE = Config( + key='disable_source_autocomplete', + type=coerce_bool, + default=False, + help=_('Choose whether the editor autocomplete should gather suggestions from external sources or not.') ) ENABLE_HUE_5 = Config( @@ -2057,7 +2057,6 @@ def is_chunked_fileuploader_enabled(): ), )) - def task_server_default_result_directory(): """Local directory to store task results.""" return 'file://%s' % get_run_root('logs') diff --git a/desktop/core/src/desktop/js/apps/editor/EditorViewModel.js b/desktop/core/src/desktop/js/apps/editor/EditorViewModel.js index f4dae772ace..9d0eb3de3e6 100644 --- a/desktop/core/src/desktop/js/apps/editor/EditorViewModel.js +++ b/desktop/core/src/desktop/js/apps/editor/EditorViewModel.js @@ -108,7 +108,6 @@ export default class EditorViewModel { } }); - this.autocompleteTimeout = options.autocompleteTimeout; this.lastNotifiedDialect = undefined; this.combinedContent = ko.observable(); diff --git a/desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AutocompleteResults.test.ts b/desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AutocompleteResults.test.ts new file mode 100644 index 00000000000..b8b05698921 --- /dev/null +++ b/desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AutocompleteResults.test.ts @@ -0,0 +1,306 @@ +// 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 * as Vue from 'vue'; +import * as CatalogApi from 'catalog/api'; +import { CancellablePromise } from 'api/cancellablePromise'; +import AutocompleteResults, { Suggestion } from './AutocompleteResults'; +import dataCatalog from 'catalog/dataCatalog'; +import huePubSub from 'utils/huePubSub'; +import I18n from 'utils/i18n'; +import * as sqlUdfRepository from 'sql/reference/sqlUdfRepository'; +import sqlReferenceRepository from 'sql/reference/sqlReferenceRepository'; +import sleep from 'utils/timing/sleep'; +import * as hueConfig from 'config/hueConfig'; +import { Ace } from 'ext/ace'; +import Executor from '../../../execution/executor'; +import sqlAnalyzerRepository from 'catalog/analyzer/sqlAnalyzerRepository'; +import { AutocompleteParseResult } from 'parse/types'; +import { SetDetails, UdfDetails } from 'sql/reference/types'; +import { HueConfig } from 'config/types'; + +describe('AutocompleteResults.ts', () => { + jest.spyOn(Vue, 'onBeforeUnmount').mockImplementation(() => undefined); + + const sourceMetaSpy = jest + .spyOn(CatalogApi, 'fetchSourceMetadata') + .mockImplementation(options => { + if (options.entry.path.length === 0) { + return CancellablePromise.resolve(JSON.parse('{"status": 0, "databases": ["default"]}')); + } + if (options.entry.path.length === 1) { + return CancellablePromise.resolve( + JSON.parse( + '{"status": 0, "tables_meta": [{"comment": "comment", "type": "Table", "name": "foo"}, {"comment": null, "type": "View", "name": "bar_view"}, {"comment": null, "type": "Table", "name": "bar"}]}' + ) + ); + } + if (options.entry.path.length === 2) { + return CancellablePromise.resolve( + JSON.parse( + '{"status": 0, "support_updates": false, "hdfs_link": "/filebrowser/view=/user/hive/warehouse/customers", "extended_columns": [{"comment": "", "type": "int", "name": "id"}, {"comment": "", "type": "string", "name": "name"}, {"comment": "", "type": "struct>", "name": "email_preferences"}, {"comment": "", "type": "map>", "name": "addresses"}, {"comment": "", "type": "array>>>", "name": "orders"}], "columns": ["id", "name", "email_preferences", "addresses", "orders"], "partition_keys": []}' + ) + ); + } + if (options.entry.path.length === 3) { + return CancellablePromise.resolve( + JSON.parse( + '{"status": 0, "comment": "", "type": "struct", "name": "email_preferences", "fields": [{"type": "string", "name": "email_format"}, {"type": "string", "name": "frequency"}, {"fields": [{"type": "boolean", "name": "promos"}, {"type": "boolean", "name": "surveys"}], "type": "struct", "name": "categories"}]}' + ) + ); + } + if (options.entry.path.length > 3) { + return CancellablePromise.resolve( + JSON.parse( + '{"status": 0, "fields": [{"type": "boolean", "name": "promos"}, {"type": "boolean", "name": "surveys"}], "type": "struct", "name": "categories"}' + ) + ); + } + return CancellablePromise.reject(); + }); + + const createSubject = (): AutocompleteResults => { + const mockEditor = () => ({ + getTextBeforeCursor: () => 'foo', + getTextAfterCursor: () => 'bar' + }); + + const mockExecutor = { + connector: () => ({ id: 'hive', dialect: 'hive' }), + database: () => 'default', + namespace: () => ({ id: 'defaultNamespace' }), + compute: () => ({ id: 'defaultCompute' }) + } as Executor; + + return new AutocompleteResults({ + temporaryOnly: false, + executor: mockExecutor, + sqlAnalyzerProvider: sqlAnalyzerRepository, + sqlReferenceProvider: sqlReferenceRepository, + editor: mockEditor as unknown as Ace.Editor + }); + }; + + beforeEach(() => { + huePubSub.publish('assist.clear.all.caches'); + dataCatalog.disableCache(); + }); + + afterEach(() => { + dataCatalog.enableCache(); + jest.resetAllMocks(); + }); + + it('should handle parse results with keywords', async () => { + const subject = createSubject(); + const suggestions: Suggestion[] = []; + + await subject.update( + { + lowerCase: true, + suggestKeywords: [ + { value: 'BAR', weight: 1 }, + { value: 'FOO', weight: 2 } + ] + } as AutocompleteParseResult, + suggestions + ); + + expect(suggestions.length).toBe(2); + expect(suggestions[0].meta).toBe(I18n('keyword')); + expect(suggestions[0].value).toBe('bar'); + expect(suggestions[0].weightAdjust).toBe(1); + expect(suggestions[1].meta).toBe(I18n('keyword')); + expect(suggestions[1].value).toBe('foo'); + expect(suggestions[1].weightAdjust).toBe(2); + }); + + it('should handle parse results with identifiers', async () => { + const subject = createSubject(); + const suggestions: Suggestion[] = []; + + await subject.update( + { + lowerCase: false, + suggestIdentifiers: [ + { name: 'foo', type: 'alias' }, + { name: 'bar', type: 'table' } + ] + } as AutocompleteParseResult, + suggestions + ); + + expect(suggestions.length).toBe(2); + expect(suggestions[1].meta).toBe('table'); + expect(suggestions[1].value).toBe('bar'); + expect(suggestions[0].meta).toBe('alias'); + expect(suggestions[0].value).toBe('foo'); + }); + + it('should handle parse results with functions', async () => { + const spy = jest + .spyOn(sqlUdfRepository, 'getUdfsWithReturnTypes') + .mockImplementation(async () => + Promise.resolve([ + { + name: 'count', + returnTypes: ['BIGINT'], + arguments: [[{ type: 'T' }]], + signature: 'count(col)', + draggable: 'count()', + description: 'some desc' + } + ]) + ); + const subject = createSubject(); + const suggestions: Suggestion[] = []; + + await subject.update( + { + lowerCase: false, + suggestFunctions: {} + } as AutocompleteParseResult, + suggestions + ); + + await sleep(0); + + expect(spy).toHaveBeenCalled(); + + expect(suggestions.length).toEqual(1); + const udfDetails = suggestions[0].details as UdfDetails; + expect(udfDetails.arguments).toBeDefined(); + expect(udfDetails.signature).toBeDefined(); + expect(udfDetails.description).toBeDefined(); + }); + + it('should handle parse results with udf argument keywords', async () => { + const spy = jest + .spyOn(sqlUdfRepository, 'getArgumentDetailsForUdf') + .mockImplementation(async () => Promise.resolve([{ type: 'T', keywords: ['a', 'b'] }])); + const subject = createSubject(); + const suggestions: Suggestion[] = []; + + await subject.update( + { + lowerCase: false, + udfArgument: { + name: 'someudf', + position: 1 + } + } as AutocompleteParseResult, + suggestions + ); + + await sleep(0); + + expect(spy).toHaveBeenCalled(); + + expect(suggestions.length).toEqual(2); + expect(suggestions[0].value).toEqual('a'); + expect(suggestions[1].value).toEqual('b'); + }); + + it('should handle parse results set options', async () => { + const spy = jest.spyOn(sqlReferenceRepository, 'getSetOptions').mockImplementation( + async dialect => + new Promise(resolve => { + expect(dialect).toEqual(subject.executor.connector().dialect); + resolve({ + OPTION_1: { + description: 'Desc 1', + type: 'Integer', + default: 'Some default' + }, + OPTION_2: { + description: 'Desc 2', + type: 'Integer', + default: 'Some default' + } + }); + }) + ); + const subject = createSubject(); + const suggestions: Suggestion[] = []; + + await subject.update( + { + lowerCase: false, + suggestSetOptions: {} + } as AutocompleteParseResult, + suggestions + ); + + await sleep(0); + + expect(spy).toHaveBeenCalled(); + + expect(suggestions.length).toEqual(2); + expect((suggestions[0].details as SetDetails).description).toBeDefined(); + expect((suggestions[1].details as SetDetails).type).toBeDefined(); + }); + + it('should fetch from source when disable_source_autocomplete is set to false', async () => { + jest.spyOn(hueConfig, 'getLastKnownConfig').mockImplementation( + () => + ({ + app_config: { + editor: { + source_autocomplete_disabled: false + } + } + } as HueConfig) + ); + + const subject = createSubject(); + const suggestions: Suggestion[] = []; + + await subject.update( + { + lowerCase: false, + suggestDatabases: {} + } as AutocompleteParseResult, + suggestions + ); + + expect(sourceMetaSpy).toHaveBeenCalled(); + }); + + it('should not fetch from source when disable_source_autocomplete is set to true', async () => { + jest.spyOn(hueConfig, 'getLastKnownConfig').mockImplementation( + () => + ({ + app_config: { + editor: { + source_autocomplete_disabled: true + } + } + } as HueConfig) + ); + const subject = createSubject(); + const suggestions: Suggestion[] = []; + + await subject.update( + { + lowerCase: false, + suggestDatabases: {} + } as AutocompleteParseResult, + suggestions + ); + + expect(sourceMetaSpy).not.toHaveBeenCalled(); + }); +}); diff --git a/desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AutocompleteResults.ts b/desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AutocompleteResults.ts index 47f9193bbe5..76b57e12b50 100644 --- a/desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AutocompleteResults.ts +++ b/desktop/core/src/desktop/js/apps/editor/components/aceEditor/autocomplete/AutocompleteResults.ts @@ -52,7 +52,7 @@ import { hueWindow } from 'types/types'; import sqlUtils from 'sql/sqlUtils'; import { matchesType } from 'sql/reference/typeUtils'; import { cancelActiveRequest } from 'api/apiUtils'; -import { findBrowserConnector, getRootFilePath } from 'config/hueConfig'; +import { findBrowserConnector, getLastKnownConfig, getRootFilePath } from 'config/hueConfig'; import { findUdf, getArgumentDetailsForUdf, @@ -153,6 +153,7 @@ const locateSubQuery = (subQueries: SubQuery[], subQueryName: string): SubQuery class AutocompleteResults { executor: Executor; editor: Ace.Editor; + sourceAutocompleteDisabled: boolean; temporaryOnly: boolean; activeDatabase: string; sqlReferenceProvider: SqlReferenceProvider; @@ -175,6 +176,8 @@ class AutocompleteResults { this.sqlAnalyzer = options.sqlAnalyzerProvider?.getSqlAnalyzer(options.executor.connector()); this.executor = options.executor; this.editor = options.editor; + this.sourceAutocompleteDisabled = + !!getLastKnownConfig()?.app_config?.editor?.source_autocomplete_disabled; this.temporaryOnly = options.temporaryOnly; this.activeDatabase = this.executor.database(); } @@ -213,31 +216,33 @@ class AutocompleteResults { return promise; }; - const colRefPromise = this.handleColumnReference(); - const databasesPromise = this.loadDatabases(); - trackPromise(this.handleKeywords()); - trackPromise(this.handleColRefKeywords(colRefPromise)); trackPromise(this.handleIdentifiers()); trackPromise(this.handleColumnAliases()); trackPromise(this.handleCommonTableExpressions()); trackPromise(this.handleOptions()); - trackPromise(this.handleFunctions(colRefPromise)); - trackPromise(this.handleDatabases(databasesPromise)); - const tablesPromise = trackPromise(this.handleTables(databasesPromise)); - const columnsPromise = trackPromise(this.handleColumns(colRefPromise, tablesPromise)); - trackPromise(this.handleValues(colRefPromise)); - trackPromise(this.handlePaths()); - - if (!this.temporaryOnly) { - trackPromise(this.handleJoins()); - trackPromise(this.handleJoinConditions()); - trackPromise(this.handleAggregateFunctions()); - trackPromise(this.handleGroupBys(columnsPromise)); - trackPromise(this.handleOrderBys(columnsPromise)); - trackPromise(this.handleFilters()); - trackPromise(this.handlePopularTables(tablesPromise)); - trackPromise(this.handlePopularColumns(columnsPromise)); + + if (!this.sourceAutocompleteDisabled) { + const colRefPromise = this.handleColumnReference(); + const databasesPromise = this.loadDatabases(); + trackPromise(this.handleColRefKeywords(colRefPromise)); + trackPromise(this.handleFunctions(colRefPromise)); + trackPromise(this.handleDatabases(databasesPromise)); + const tablesPromise = trackPromise(this.handleTables(databasesPromise)); + const columnsPromise = trackPromise(this.handleColumns(colRefPromise, tablesPromise)); + trackPromise(this.handleValues(colRefPromise)); + trackPromise(this.handlePaths()); + + if (!this.temporaryOnly) { + trackPromise(this.handleJoins()); + trackPromise(this.handleJoinConditions()); + trackPromise(this.handleAggregateFunctions()); + trackPromise(this.handleGroupBys(columnsPromise)); + trackPromise(this.handleOrderBys(columnsPromise)); + trackPromise(this.handleFilters()); + trackPromise(this.handlePopularTables(tablesPromise)); + trackPromise(this.handlePopularColumns(columnsPromise)); + } } return Promise.all(promises); @@ -433,12 +438,18 @@ class AutocompleteResults { }); } else if (type === 'UDFREF' && columnAlias.udfRef) { try { - const types = await getReturnTypesForUdf( - this.sqlReferenceProvider, - this.executor.connector(), - columnAlias.udfRef - ); - const resolvedType = types.length === 1 ? types[0] : 'T'; + let resolvedType = 'T'; + if (!this.sourceAutocompleteDisabled) { + const types = await getReturnTypesForUdf( + this.sqlReferenceProvider, + this.executor.connector(), + columnAlias.udfRef + ); + if (types.length === 1) { + resolvedType = types[0]; + } + } + columnAliasSuggestions.push({ value: columnAlias.name, meta: resolvedType, @@ -1234,8 +1245,7 @@ class AutocompleteResults { resolve(); }, silenceErrors: true, - errorCallback: resolve, - timeout: (window).AUTOCOMPLETE_TIMEOUT + errorCallback: resolve }) ); }); diff --git a/desktop/core/src/desktop/js/apps/editor/snippet.js b/desktop/core/src/desktop/js/apps/editor/snippet.js index e4b88e4dcc4..e852e3684a4 100644 --- a/desktop/core/src/desktop/js/apps/editor/snippet.js +++ b/desktop/core/src/desktop/js/apps/editor/snippet.js @@ -580,8 +580,7 @@ export default class Snippet { this.autocompleter = new AceAutocompleteWrapper({ snippet: this, user: this.parentVm.user, - optEnabled: false, - timeout: this.parentVm.autocompleteTimeout + optEnabled: false }); this.activeExecutable = ko.observable(); diff --git a/desktop/core/src/desktop/js/apps/notebook/NotebookViewModel.js b/desktop/core/src/desktop/js/apps/notebook/NotebookViewModel.js index 085c170a541..03a128fe77f 100644 --- a/desktop/core/src/desktop/js/apps/notebook/NotebookViewModel.js +++ b/desktop/core/src/desktop/js/apps/notebook/NotebookViewModel.js @@ -170,7 +170,6 @@ export default class NotebookViewModel { ); return foundInterpreter?.displayName || foundInterpreter?.name || self.editorType(); }); - self.autocompleteTimeout = options.autocompleteTimeout; self.selectedNotebook = ko.observable(); self.combinedContent = ko.observable(); diff --git a/desktop/core/src/desktop/js/apps/notebook/aceAutocompleteWrapper.js b/desktop/core/src/desktop/js/apps/notebook/aceAutocompleteWrapper.js index 642775b052e..18a30ba96be 100644 --- a/desktop/core/src/desktop/js/apps/notebook/aceAutocompleteWrapper.js +++ b/desktop/core/src/desktop/js/apps/notebook/aceAutocompleteWrapper.js @@ -22,21 +22,18 @@ class AceAutocompleteWrapper { * @param options.snippet * @param options.user * @param options.optEnabled - * @param {Number} options.timeout * @constructor */ constructor(options) { const self = this; self.snippet = options.snippet; - self.timeout = options.timeout; self.topTables = {}; const initializeAutocompleter = function () { self.autocompleter = new HdfsAutocompleter({ user: options.user, - snippet: options.snippet, - timeout: options.timeout + snippet: options.snippet }); }; if (window.ENABLE_HUE_5) { diff --git a/desktop/core/src/desktop/js/apps/notebook/snippet.js b/desktop/core/src/desktop/js/apps/notebook/snippet.js index a557b383f42..ed183cabf03 100644 --- a/desktop/core/src/desktop/js/apps/notebook/snippet.js +++ b/desktop/core/src/desktop/js/apps/notebook/snippet.js @@ -2793,8 +2793,7 @@ class Snippet { self.autocompleter = new AceAutocompleteWrapper({ snippet: self, user: vm.user, - optEnabled: false, - timeout: vm.autocompleteTimeout + optEnabled: false }); self.init = function () { diff --git a/desktop/core/src/desktop/js/config/types.ts b/desktop/core/src/desktop/js/config/types.ts index 0092a4d1324..80849c4a884 100644 --- a/desktop/core/src/desktop/js/config/types.ts +++ b/desktop/core/src/desktop/js/config/types.ts @@ -49,6 +49,7 @@ export interface AppConfig { export interface EditorConfig extends AppConfig { default_limit: number | null; default_sql_interpreter: string; + source_autocomplete_disabled: boolean; } export enum AppType { diff --git a/desktop/core/src/desktop/js/jest/jest.init.js b/desktop/core/src/desktop/js/jest/jest.init.js index d6d68e696ee..afc17282edc 100644 --- a/desktop/core/src/desktop/js/jest/jest.init.js +++ b/desktop/core/src/desktop/js/jest/jest.init.js @@ -39,7 +39,6 @@ class Autocomplete {} const globalVars = { ko: ko, - AUTOCOMPLETE_TIMEOUT: 1, CACHEABLE_TTL: 1, HAS_LINK_SHARING: true, HAS_SQL_ANALYZER: false, diff --git a/desktop/core/src/desktop/js/sql/autocompleteResults.js b/desktop/core/src/desktop/js/sql/autocompleteResults.js index eb2433323f8..f3afcbf781b 100644 --- a/desktop/core/src/desktop/js/sql/autocompleteResults.js +++ b/desktop/core/src/desktop/js/sql/autocompleteResults.js @@ -27,7 +27,7 @@ import sqlUtils from 'sql/sqlUtils'; import { matchesType } from 'sql/reference/typeUtils'; import { DIALECT } from 'apps/editor/snippet'; import { cancelActiveRequest } from 'api/apiUtils'; -import { findBrowserConnector, getRootFilePath } from 'config/hueConfig'; +import { findBrowserConnector, getLastKnownConfig, getRootFilePath } from 'config/hueConfig'; import equalIgnoreCase from 'utils/string/equalIgnoreCase'; import { findUdf, @@ -256,6 +256,8 @@ class AutocompleteResults { this.snippet = options.snippet; this.dialect = () => this.snippet.type(); this.editor = options.editor; + this.sourceAutocompleteDisabled = + !!getLastKnownConfig()?.app_config?.editor?.source_autocomplete_disabled; this.temporaryOnly = options.snippet.autocompleteSettings && options.snippet.autocompleteSettings.temporaryOnly; @@ -450,31 +452,32 @@ class AutocompleteResults { return promise; }; - const colRefPromise = this.handleColumnReference(); - const databasesPromise = this.loadDatabases(); - trackPromise(this.handleKeywords()); - trackPromise(this.handleColRefKeywords(colRefPromise)); trackPromise(this.handleIdentifiers()); trackPromise(this.handleColumnAliases()); trackPromise(this.handleCommonTableExpressions()); trackPromise(this.handleOptions()); - trackPromise(this.handleFunctions(colRefPromise)); - trackPromise(this.handleDatabases(databasesPromise)); - const tablesPromise = trackPromise(this.handleTables(databasesPromise)); - const columnsPromise = trackPromise(this.handleColumns(colRefPromise, tablesPromise)); - trackPromise(this.handleValues(colRefPromise)); - trackPromise(this.handlePaths()); - - if (!this.temporaryOnly) { - trackPromise(this.handleJoins()); - trackPromise(this.handleJoinConditions()); - trackPromise(this.handleAggregateFunctions()); - trackPromise(this.handleGroupBys(columnsPromise)); - trackPromise(this.handleOrderBys(columnsPromise)); - trackPromise(this.handleFilters()); - trackPromise(this.handlePopularTables(tablesPromise)); - trackPromise(this.handlePopularColumns(columnsPromise)); + + if (!this.sourceAutocompleteDisabled) { + const colRefPromise = this.handleColumnReference(); + const databasesPromise = this.loadDatabases(); + trackPromise(this.handleColRefKeywords(colRefPromise)); + trackPromise(this.handleFunctions(colRefPromise)); + trackPromise(this.handleDatabases(databasesPromise)); + const tablesPromise = trackPromise(this.handleTables(databasesPromise)); + const columnsPromise = trackPromise(this.handleColumns(colRefPromise, tablesPromise)); + trackPromise(this.handleValues(colRefPromise)); + trackPromise(this.handlePaths()); + if (!this.temporaryOnly) { + trackPromise(this.handleJoins()); + trackPromise(this.handleJoinConditions()); + trackPromise(this.handleAggregateFunctions()); + trackPromise(this.handleGroupBys(columnsPromise)); + trackPromise(this.handleOrderBys(columnsPromise)); + trackPromise(this.handleFilters()); + trackPromise(this.handlePopularTables(tablesPromise)); + trackPromise(this.handlePopularColumns(columnsPromise)); + } } await Promise.all(promises); @@ -672,12 +675,17 @@ class AutocompleteResults { }); } else if (type === 'UDFREF') { try { - const types = await getReturnTypesForUdf( - sqlReferenceRepository, - this.snippet.connector(), - columnAlias.udfRef - ); - const resolvedType = types.length === 1 ? types[0] : 'T'; + let resolvedType = 'T'; + if (!this.sourceAutocompleteDisabled) { + const types = await getReturnTypesForUdf( + sqlReferenceRepository, + this.snippet.connector(), + columnAlias.udfRef + ); + if (types.length === 1) { + resolvedType = types[0]; + } + } columnAliasSuggestions.push({ value: columnAlias.name, meta: resolvedType, @@ -1483,8 +1491,7 @@ class AutocompleteResults { resolve(); }, silenceErrors: true, - errorCallback: resolve, - timeout: AUTOCOMPLETE_TIMEOUT + errorCallback: resolve }) ); }); diff --git a/desktop/core/src/desktop/js/sql/autocompleteResults.test.js b/desktop/core/src/desktop/js/sql/autocompleteResults.test.js index d8fd6a7dff5..b8ead745c54 100644 --- a/desktop/core/src/desktop/js/sql/autocompleteResults.test.js +++ b/desktop/core/src/desktop/js/sql/autocompleteResults.test.js @@ -28,317 +28,77 @@ import LOTS_OF_PARSE_RESULTS from './test/lotsOfParseResults'; import * as sqlUdfRepository from 'sql/reference/sqlUdfRepository'; import sqlReferenceRepository from 'sql/reference/sqlReferenceRepository'; import sleep from 'utils/timing/sleep'; +import * as hueConfig from 'config/hueConfig'; describe('AutocompleteResults.js', () => { - const failResponse = { - status: 500 - }; - - jest.spyOn(CatalogApi, 'fetchSourceMetadata').mockImplementation(options => { - if (Math.random() < 0.5) { - return CancellablePromise.reject(failResponse); - } - if (options.entry.path.length === 0) { - return CancellablePromise.resolve(JSON.parse('{"status": 0, "databases": ["default"]}')); - } - if (options.entry.path.length === 1) { - return CancellablePromise.resolve( - JSON.parse( - '{"status": 0, "tables_meta": [{"comment": "comment", "type": "Table", "name": "foo"}, {"comment": null, "type": "View", "name": "bar_view"}, {"comment": null, "type": "Table", "name": "bar"}]}' - ) - ); - } - if (options.entry.path.length === 2) { - return CancellablePromise.resolve( - JSON.parse( - '{"status": 0, "support_updates": false, "hdfs_link": "/filebrowser/view=/user/hive/warehouse/customers", "extended_columns": [{"comment": "", "type": "int", "name": "id"}, {"comment": "", "type": "string", "name": "name"}, {"comment": "", "type": "struct>", "name": "email_preferences"}, {"comment": "", "type": "map>", "name": "addresses"}, {"comment": "", "type": "array>>>", "name": "orders"}], "columns": ["id", "name", "email_preferences", "addresses", "orders"], "partition_keys": []}' - ) - ); - } - if (options.entry.path.length === 3) { - return CancellablePromise.resolve( - JSON.parse( - '{"status": 0, "comment": "", "type": "struct", "name": "email_preferences", "fields": [{"type": "string", "name": "email_format"}, {"type": "string", "name": "frequency"}, {"fields": [{"type": "boolean", "name": "promos"}, {"type": "boolean", "name": "surveys"}], "type": "struct", "name": "categories"}]}' - ) - ); - } - if (options.entry.path.length > 3) { - return CancellablePromise.resolve( - JSON.parse( - '{"status": 0, "fields": [{"type": "boolean", "name": "promos"}, {"type": "boolean", "name": "surveys"}], "type": "struct", "name": "categories"}' - ) - ); - } - return CancellablePromise.reject(); - }); - - jest.spyOn(ApiHelper, 'fetchHdfsPath').mockImplementation(options => { - const deferred = $.Deferred(); - - deferred.done(options.successCallback); - - deferred.resolve({ - superuser: 'hdfs', - current_request_path: '/filebrowser/view=///var', - current_dir_path: '///var', - show_download_button: true, - cwd_set: true, - breadcrumbs: [ - { - url: '/', - label: '/' - }, - { - url: '/var', - label: 'var' - } - ], - apps: [ - 'help', - 'sqoop', - 'pig', - 'hbase', - 'rdbms', - 'indexer', - 'metastore', - 'beeswax', - 'jobsub', - 'metadata', - 'zookeeper', - 'search', - 'useradmin', - 'notebook', - 'proxy', - 'oozie', - 'spark', - 'filebrowser', - 'about', - 'jobbrowser', - 'dashboard', - 'security', - 'impala' - ], - show_upload_button: true, - files: [ - { - humansize: '0\u00a0bytes', - url: '/filebrowser/view=/', - stats: { - size: 0, - group: 'supergroup', - blockSize: 0, - replication: 0, - user: 'hdfs', - mtime: 1476970119, - path: '///var/..', - atime: 0, - mode: 16877 - }, - name: '..', - mtime: 'October 20, 2016 06:28 AM', - rwx: 'drwxr-xr-x', - path: '/', - is_sentry_managed: false, - type: 'dir', - mode: '40755' - }, - { - humansize: '0\u00a0bytes', - url: '/filebrowser/view=/var', - stats: { - size: 0, - group: 'supergroup', - blockSize: 0, - replication: 0, - user: 'hdfs', - mtime: 1470887321, - path: '///var', - atime: 0, - mode: 16877 - }, - name: '.', - mtime: 'August 10, 2016 08:48 PM', - rwx: 'drwxr-xr-x', - path: '/var', - is_sentry_managed: false, - type: 'dir', - mode: '40755' - }, - { - humansize: '0\u00a0bytes', - url: '/filebrowser/view=/var/lib', - stats: { - size: 0, - group: 'supergroup', - blockSize: 0, - replication: 0, - user: 'hdfs', - mtime: 1470887321, - path: '/var/lib', - atime: 0, - mode: 16877 - }, - name: 'lib', - mtime: 'August 10, 2016 08:48 PM', - rwx: 'drwxr-xr-x', - path: '/var/lib', - is_sentry_managed: false, - type: 'dir', - mode: '40755' - }, - { - humansize: '0\u00a0bytes', - url: '/filebrowser/view=/var/log', - stats: { - size: 0, - group: 'mapred', - blockSize: 0, - replication: 0, - user: 'yarn', - mtime: 1470887196, - path: '/var/log', - atime: 0, - mode: 17405 - }, - name: 'log', - mtime: 'August 10, 2016 08:46 PM', - rwx: 'drwxrwxr-xt', - path: '/var/log', - is_sentry_managed: false, - type: 'dir', - mode: '41775' - } - ], - users: [], - is_embeddable: false, - supergroup: 'supergroup', - descending: 'false', - groups: [], - is_trash_enabled: true, - pagesize: 50, - file_filter: 'any', - is_fs_superuser: false, - is_sentry_managed: false, - home_directory: '/user/admin', - path: '///var', - page: { - num_pages: 1, - total_count: 2, - next_page_number: 1, - end_index: 2, - number: 1, - previous_page_number: 1, - start_index: 1 + const sourceMetaSpy = jest + .spyOn(CatalogApi, 'fetchSourceMetadata') + .mockImplementation(options => { + if (options.entry.path.length === 0) { + return CancellablePromise.resolve(JSON.parse('{"status": 0, "databases": ["default"]}')); } - }); - - return deferred.promise(); - }); - - jest.spyOn(apiUtils, 'fetchUdfs').mockImplementation(() => Promise.resolve([])); - - const subject = new AutocompleteResults({ - snippet: { - autocompleteSettings: { - temporaryOnly: false - }, - type: function () { - return 'hive'; - }, - connector: function () { - return { id: 'hive', dialect: 'hive' }; - }, - database: function () { - return 'default'; - }, - namespace: function () { - return { id: 'defaultNamespace' }; - }, - compute: function () { - return { id: 'defaultCompute' }; - }, - whenContextSet: function () { - return $.Deferred().resolve(); + if (options.entry.path.length === 1) { + return CancellablePromise.resolve( + JSON.parse( + '{"status": 0, "tables_meta": [{"comment": "comment", "type": "Table", "name": "foo"}, {"comment": null, "type": "View", "name": "bar_view"}, {"comment": null, "type": "Table", "name": "bar"}]}' + ) + ); } - }, - editor: function () { - return { - getTextBeforeCursor: function () { - return 'foo'; - }, - getTextAfterCursor: function () { - return 'bar'; - } - }; - } - }); - - describe('Test a whole lot of different parse results', () => { - const LOADING_OBSERVABLES = [ - 'loadingKeywords', - 'loadingFunctions', - 'loadingDatabases', - 'loadingTables', - 'loadingColumns', - 'loadingValues', - 'loadingPaths', - 'loadingJoins', - 'loadingJoinConditions', - 'loadingAggregateFunctions', - 'loadingGroupBys', - 'loadingOrderBys', - 'loadingFilters', - 'loadingPopularTables', - 'loadingPopularColumns' - ]; - - beforeEach(() => { - dataCatalog.disableCache(); - window.AUTOCOMPLETE_TIMEOUT = 1; - global.AUTOCOMPLETE_TIMEOUT = 1; - - huePubSub.publish('assist.clear.all.caches'); + if (options.entry.path.length === 2) { + return CancellablePromise.resolve( + JSON.parse( + '{"status": 0, "support_updates": false, "hdfs_link": "/filebrowser/view=/user/hive/warehouse/customers", "extended_columns": [{"comment": "", "type": "int", "name": "id"}, {"comment": "", "type": "string", "name": "name"}, {"comment": "", "type": "struct>", "name": "email_preferences"}, {"comment": "", "type": "map>", "name": "addresses"}, {"comment": "", "type": "array>>>", "name": "orders"}], "columns": ["id", "name", "email_preferences", "addresses", "orders"], "partition_keys": []}' + ) + ); + } + if (options.entry.path.length === 3) { + return CancellablePromise.resolve( + JSON.parse( + '{"status": 0, "comment": "", "type": "struct", "name": "email_preferences", "fields": [{"type": "string", "name": "email_format"}, {"type": "string", "name": "frequency"}, {"fields": [{"type": "boolean", "name": "promos"}, {"type": "boolean", "name": "surveys"}], "type": "struct", "name": "categories"}]}' + ) + ); + } + if (options.entry.path.length > 3) { + return CancellablePromise.resolve( + JSON.parse( + '{"status": 0, "fields": [{"type": "boolean", "name": "promos"}, {"type": "boolean", "name": "surveys"}], "type": "struct", "name": "categories"}' + ) + ); + } + return CancellablePromise.reject(); }); - afterEach(() => { - AUTOCOMPLETE_TIMEOUT = 0; - dataCatalog.enableCache(); + const createSubject = () => + new AutocompleteResults({ + snippet: { + autocompleteSettings: { + temporaryOnly: false + }, + type: () => 'hive', + connector: () => ({ id: 'hive', dialect: 'hive' }), + database: () => 'default', + namespace: () => ({ id: 'defaultNamespace' }), + compute: () => ({ id: 'defaultCompute' }), + whenContextSet: () => Promise.resolve() + }, + editor: () => ({ + getTextBeforeCursor: () => 'foo', + getTextAfterCursor: () => 'bar' + }) }); - for (const parseResult of LOTS_OF_PARSE_RESULTS) { - it('should handle parse result no. ' + parseResult.index, async () => { - if (parseResult.suggestKeywords) { - const cleanedKeywords = []; - parseResult.suggestKeywords.forEach(keyword => { - if (!keyword.value) { - cleanedKeywords.push({ value: keyword }); - } else { - cleanedKeywords.push(keyword); - } - }); - parseResult.suggestKeywords = cleanedKeywords; - } - try { - await subject.update(parseResult); - } catch (e) { - fail('Got exception'); - console.error(e); - } - if (subject.loading()) { - LOADING_OBSERVABLES.forEach(observable => { - if (subject[observable]()) { - fail('Still loading (' + observable + '() == true), missing ajax spec?'); - } - }); - } + beforeEach(() => { + huePubSub.publish('assist.clear.all.caches'); + dataCatalog.disableCache(); + }); - expect(subject.loading()).toBeFalsy(); - }); - } + afterEach(() => { + dataCatalog.enableCache(); + jest.resetAllMocks(); }); it('should handle parse results with keywords', async () => { - subject.entries([]); + const subject = createSubject(); expect(subject.filtered().length).toBe(0); @@ -359,7 +119,7 @@ describe('AutocompleteResults.js', () => { }); it('should handle parse results with identifiers', async () => { - subject.entries([]); + const subject = createSubject(); expect(subject.filtered().length).toBe(0); await subject.update({ @@ -379,7 +139,7 @@ describe('AutocompleteResults.js', () => { }); it('should handle parse results with functions', async () => { - subject.entries([]); + const subject = createSubject(); const spy = jest .spyOn(sqlUdfRepository, 'getUdfsWithReturnTypes') @@ -414,7 +174,7 @@ describe('AutocompleteResults.js', () => { }); it('should handle parse results with udf argument keywords', async () => { - subject.entries([]); + const subject = createSubject(); const spy = jest .spyOn(sqlUdfRepository, 'getArgumentDetailsForUdf') @@ -440,7 +200,7 @@ describe('AutocompleteResults.js', () => { }); it('should handle parse results set options', async () => { - subject.entries([]); + const subject = createSubject(); const spy = jest.spyOn(sqlReferenceRepository, 'getSetOptions').mockImplementation( async dialect => @@ -476,4 +236,257 @@ describe('AutocompleteResults.js', () => { expect(subject.filtered()[0].details.description).toBeDefined(); expect(subject.filtered()[1].details.type).toBeDefined(); }); + + it('should fetch from source when disable_source_autocomplete is set to false', async () => { + jest.spyOn(hueConfig, 'getLastKnownConfig').mockImplementation(() => ({ + app_config: { + editor: { + source_autocomplete_disabled: false + } + } + })); + + const subject = createSubject(); + + await subject.update({ + lowerCase: false, + suggestDatabases: {} + }); + + expect(sourceMetaSpy).toHaveBeenCalled(); + }); + + it('should not fetch from source when disable_source_autocomplete is set to true', async () => { + jest.spyOn(hueConfig, 'getLastKnownConfig').mockImplementation(() => ({ + app_config: { + editor: { + source_autocomplete_disabled: true + } + } + })); + const subject = createSubject(); + + await subject.update({ + lowerCase: false, + suggestDatabases: {} + }); + + expect(sourceMetaSpy).not.toHaveBeenCalled(); + }); + + describe('Test a whole lot of different parse results', () => { + const LOADING_OBSERVABLES = [ + 'loadingKeywords', + 'loadingFunctions', + 'loadingDatabases', + 'loadingTables', + 'loadingColumns', + 'loadingValues', + 'loadingPaths', + 'loadingJoins', + 'loadingJoinConditions', + 'loadingAggregateFunctions', + 'loadingGroupBys', + 'loadingOrderBys', + 'loadingFilters', + 'loadingPopularTables', + 'loadingPopularColumns' + ]; + + for (const parseResult of LOTS_OF_PARSE_RESULTS) { + it('should handle parse result no. ' + parseResult.index, async () => { + jest.spyOn(apiUtils, 'fetchUdfs').mockImplementation(() => Promise.resolve([])); + jest.spyOn(ApiHelper, 'fetchHdfsPath').mockImplementation(options => { + const deferred = $.Deferred(); + + deferred.done(options.successCallback); + + deferred.resolve({ + superuser: 'hdfs', + current_request_path: '/filebrowser/view=///var', + current_dir_path: '///var', + show_download_button: true, + cwd_set: true, + breadcrumbs: [ + { + url: '/', + label: '/' + }, + { + url: '/var', + label: 'var' + } + ], + apps: [ + 'help', + 'sqoop', + 'pig', + 'hbase', + 'rdbms', + 'indexer', + 'metastore', + 'beeswax', + 'jobsub', + 'metadata', + 'zookeeper', + 'search', + 'useradmin', + 'notebook', + 'proxy', + 'oozie', + 'spark', + 'filebrowser', + 'about', + 'jobbrowser', + 'dashboard', + 'security', + 'impala' + ], + show_upload_button: true, + files: [ + { + humansize: '0\u00a0bytes', + url: '/filebrowser/view=/', + stats: { + size: 0, + group: 'supergroup', + blockSize: 0, + replication: 0, + user: 'hdfs', + mtime: 1476970119, + path: '///var/..', + atime: 0, + mode: 16877 + }, + name: '..', + mtime: 'October 20, 2016 06:28 AM', + rwx: 'drwxr-xr-x', + path: '/', + is_sentry_managed: false, + type: 'dir', + mode: '40755' + }, + { + humansize: '0\u00a0bytes', + url: '/filebrowser/view=/var', + stats: { + size: 0, + group: 'supergroup', + blockSize: 0, + replication: 0, + user: 'hdfs', + mtime: 1470887321, + path: '///var', + atime: 0, + mode: 16877 + }, + name: '.', + mtime: 'August 10, 2016 08:48 PM', + rwx: 'drwxr-xr-x', + path: '/var', + is_sentry_managed: false, + type: 'dir', + mode: '40755' + }, + { + humansize: '0\u00a0bytes', + url: '/filebrowser/view=/var/lib', + stats: { + size: 0, + group: 'supergroup', + blockSize: 0, + replication: 0, + user: 'hdfs', + mtime: 1470887321, + path: '/var/lib', + atime: 0, + mode: 16877 + }, + name: 'lib', + mtime: 'August 10, 2016 08:48 PM', + rwx: 'drwxr-xr-x', + path: '/var/lib', + is_sentry_managed: false, + type: 'dir', + mode: '40755' + }, + { + humansize: '0\u00a0bytes', + url: '/filebrowser/view=/var/log', + stats: { + size: 0, + group: 'mapred', + blockSize: 0, + replication: 0, + user: 'yarn', + mtime: 1470887196, + path: '/var/log', + atime: 0, + mode: 17405 + }, + name: 'log', + mtime: 'August 10, 2016 08:46 PM', + rwx: 'drwxrwxr-xt', + path: '/var/log', + is_sentry_managed: false, + type: 'dir', + mode: '41775' + } + ], + users: [], + is_embeddable: false, + supergroup: 'supergroup', + descending: 'false', + groups: [], + is_trash_enabled: true, + pagesize: 50, + file_filter: 'any', + is_fs_superuser: false, + is_sentry_managed: false, + home_directory: '/user/admin', + path: '///var', + page: { + num_pages: 1, + total_count: 2, + next_page_number: 1, + end_index: 2, + number: 1, + previous_page_number: 1, + start_index: 1 + } + }); + + return deferred.promise(); + }); + + const subject = createSubject(); + if (parseResult.suggestKeywords) { + const cleanedKeywords = []; + parseResult.suggestKeywords.forEach(keyword => { + if (!keyword.value) { + cleanedKeywords.push({ value: keyword }); + } else { + cleanedKeywords.push(keyword); + } + }); + parseResult.suggestKeywords = cleanedKeywords; + } + try { + await subject.update(parseResult); + } catch (e) { + fail('Got exception'); + console.error(e); + } + if (subject.loading()) { + LOADING_OBSERVABLES.forEach(observable => { + if (subject[observable]()) { + fail('Still loading (' + observable + '() == true), missing ajax spec?'); + } + }); + } + + expect(subject.loading()).toBeFalsy(); + }); + } + }); }); diff --git a/desktop/core/src/desktop/js/sql/sqlAutocompleter.test.ignore.js b/desktop/core/src/desktop/js/sql/sqlAutocompleter.test.ignore.js index 7c46c044c03..f4463343d9d 100644 --- a/desktop/core/src/desktop/js/sql/sqlAutocompleter.test.ignore.js +++ b/desktop/core/src/desktop/js/sql/sqlAutocompleter.test.ignore.js @@ -27,7 +27,6 @@ xdescribe('sqlAutocomplete.js', () => { beforeEach(() => { dataCatalog.disableCache(); - window.AUTOCOMPLETE_TIMEOUT = 1; jasmine.Ajax.install(); jasmine.Ajax.stubRequest(/.*\/notebook\/api\/autocomplete\/$/).mockReturnValue({ @@ -65,7 +64,6 @@ xdescribe('sqlAutocomplete.js', () => { } fail('Still loading, missing ajax spec?'); } - AUTOCOMPLETE_TIMEOUT = 0; dataCatalog.enableCache(); jasmine.Ajax.uninstall(); }); diff --git a/desktop/core/src/desktop/js/types/types.ts b/desktop/core/src/desktop/js/types/types.ts index 9228a9db9c8..0677256eddb 100644 --- a/desktop/core/src/desktop/js/types/types.ts +++ b/desktop/core/src/desktop/js/types/types.ts @@ -35,7 +35,6 @@ declare global { } export interface hueWindow { - AUTOCOMPLETE_TIMEOUT?: number; CACHEABLE_TTL?: { default?: number; sqlAnalyzer?: number }; CLOSE_SESSIONS?: { [dialect: string]: boolean }; CSRF_TOKEN?: string; diff --git a/desktop/core/src/desktop/js/utils/hdfsAutocompleter.js b/desktop/core/src/desktop/js/utils/hdfsAutocompleter.js index c6b31dae79b..d0d5e137130 100644 --- a/desktop/core/src/desktop/js/utils/hdfsAutocompleter.js +++ b/desktop/core/src/desktop/js/utils/hdfsAutocompleter.js @@ -22,7 +22,6 @@ class HdfsAutocompleter { /** * @param {object} options * @param {string} options.user - * @param {Number} options.timeout * @param {Snippet} options.snippet * * @constructor @@ -31,7 +30,6 @@ class HdfsAutocompleter { const self = this; self.user = options.user; self.snippet = options.snippet; - self.timeout = options.timeout; } hasExpired(timestamp) { @@ -88,7 +86,6 @@ class HdfsAutocompleter { successCallback: successCallback, silenceErrors: true, errorCallback: onFailure, - timeout: self.timeout, editor: editor }); } else { diff --git a/desktop/core/src/desktop/models.py b/desktop/core/src/desktop/models.py index 8ff02fe120c..22ea3acd5cf 100644 --- a/desktop/core/src/desktop/models.py +++ b/desktop/core/src/desktop/models.py @@ -50,9 +50,9 @@ from desktop import appmanager from desktop.auth.backend import is_admin -from desktop.conf import get_clusters, IS_MULTICLUSTER_ONLY, ENABLE_ORGANIZATIONS, ENABLE_PROMETHEUS, \ - has_connectors, TASK_SERVER, APP_BLACKLIST, COLLECT_USAGE, ENABLE_SHARING, ENABLE_CONNECTORS, ENABLE_UNIFIED_ANALYTICS, RAZ, \ - HUE_IMAGE_VERSION, HUE_HOST_NAME +from desktop.conf import APP_BLACKLIST, COLLECT_USAGE, DISABLE_SOURCE_AUTOCOMPLETE, ENABLE_CONNECTORS, \ + ENABLE_ORGANIZATIONS, ENABLE_PROMETHEUS, ENABLE_SHARING, ENABLE_UNIFIED_ANALYTICS, get_clusters, has_connectors, \ + HUE_HOST_NAME, HUE_IMAGE_VERSION, IS_MULTICLUSTER_ONLY, RAZ, TASK_SERVER from desktop.lib import fsmanager from desktop.lib.connectors.api import _get_installed_connectors from desktop.lib.connectors.models import Connector @@ -1930,7 +1930,8 @@ def _get_editor(self): 'default_limit': None if DEFAULT_LIMIT.get() == 0 else DEFAULT_LIMIT.get(), 'interpreter_names': [interpreter['type'] for interpreter in interpreters], 'page': interpreters[0]['page'], - 'default_sql_interpreter': next((interpreter['type'] for interpreter in interpreters if interpreter.get('is_sql')), 'hive') + 'default_sql_interpreter': next((interpreter['type'] for interpreter in interpreters if interpreter.get('is_sql')), 'hive'), + 'source_autocomplete_disabled': DISABLE_SOURCE_AUTOCOMPLETE.get() } else: return None diff --git a/desktop/core/src/desktop/templates/common_header_m.mako b/desktop/core/src/desktop/templates/common_header_m.mako index fbb28e770dc..257e8549763 100644 --- a/desktop/core/src/desktop/templates/common_header_m.mako +++ b/desktop/core/src/desktop/templates/common_header_m.mako @@ -88,8 +88,6 @@ if USE_NEW_EDITOR.get(): sqlAnalyzer: ${ OPTIMIZER.CACHEABLE_TTL.get() } }; - var AUTOCOMPLETE_TIMEOUT = ${ conf.EDITOR_AUTOCOMPLETE_TIMEOUT.get() }; - window.LEAFLET_DEFAULTS = { layer: '${ leaflet['layer'] |n,unicode }', attribution: '${ leaflet['attribution'] |n,unicode }' diff --git a/desktop/core/src/desktop/templates/global_js_constants.mako b/desktop/core/src/desktop/templates/global_js_constants.mako index d3e04a4babb..638bd12a8d3 100644 --- a/desktop/core/src/desktop/templates/global_js_constants.mako +++ b/desktop/core/src/desktop/templates/global_js_constants.mako @@ -52,7 +52,6 @@ <% current_app, other_apps, apps = _get_apps(user) %> - window.AUTOCOMPLETE_TIMEOUT = ${ conf.EDITOR_AUTOCOMPLETE_TIMEOUT.get() }; window.BANNER_TOP_HTML = '${ conf.CUSTOM.BANNER_TOP_HTML.get() }'; window.DISABLE_LOCAL_STORAGE = '${ conf.DISABLE_LOCAL_STORAGE.get() }' === 'True'; diff --git a/desktop/libs/notebook/src/notebook/templates/editor2.mako b/desktop/libs/notebook/src/notebook/templates/editor2.mako index 8ac4d0ef3dc..053e82e794b 100644 --- a/desktop/libs/notebook/src/notebook/templates/editor2.mako +++ b/desktop/libs/notebook/src/notebook/templates/editor2.mako @@ -1083,7 +1083,6 @@ There is no bridge to KO for components using this integration. Example using in userId: ${ user.id }, suffix: 'editor', assistAvailable: true, - autocompleteTimeout: AUTOCOMPLETE_TIMEOUT, snippetViewSettings: { default: { placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }', diff --git a/desktop/libs/notebook/src/notebook/templates/editor_components.mako b/desktop/libs/notebook/src/notebook/templates/editor_components.mako index 49577f1d7b1..e3c1d556045 100644 --- a/desktop/libs/notebook/src/notebook/templates/editor_components.mako +++ b/desktop/libs/notebook/src/notebook/templates/editor_components.mako @@ -2076,7 +2076,6 @@ ${ sqlSyntaxDropdown.sqlSyntaxDropdown() } userId: ${ user.id }, suffix: '${ suffix }', assistAvailable: true, - autocompleteTimeout: AUTOCOMPLETE_TIMEOUT, snippetViewSettings: { default: { placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }', diff --git a/desktop/libs/notebook/src/notebook/templates/editor_m.mako b/desktop/libs/notebook/src/notebook/templates/editor_m.mako index fc2141d6bc7..e8d4d092579 100644 --- a/desktop/libs/notebook/src/notebook/templates/editor_m.mako +++ b/desktop/libs/notebook/src/notebook/templates/editor_m.mako @@ -176,7 +176,6 @@ ${ commonheader_m(editor_type, editor_type, user, request, "68px") | n,unicode } user: '${ user.username }', userId: ${ user.id }, assistAvailable: true, - autocompleteTimeout: AUTOCOMPLETE_TIMEOUT, snippetViewSettings: { default: { placeHolder: '${ _("Example: SELECT * FROM tablename, or press CTRL + space") }',