UNPKG

gethue

Version:

Hue is an Open source SQL Query Editor for Databases/Warehouses

377 lines (339 loc) 12 kB
// 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 sqlAnalyzerRepository from '../../../catalog/analyzer/sqlAnalyzerRepository'; import $ from 'jquery'; import * as ko from 'knockout'; import { ASSIST_ACTIVE_DB_CHANGED_EVENT } from './events'; import dataCatalog from '../../../catalog/dataCatalog'; import AssistDbEntry from '../../../ko/components/assist/assistDbEntry'; import huePubSub from '../../../utils/huePubSub'; import { getFromLocalStorage, setInLocalStorage } from '../../../utils/storageUtils'; class AssistDbNamespace { /** * @param {Object} options * @param {Object} options.i18n * @param {string} options.sourceType * @param {Connector} options.connector * @param {ContextNamespace} options.namespace * @param {boolean} options.nonSqlType - Optional, default false * @param {Object} options.navigationSettings * @constructor */ constructor(options) { const self = this; self.i18n = options.i18n; self.navigationSettings = options.navigationSettings; self.sourceType = options.sourceType; self.connector = options.connector; self.nonSqlType = options.nonSqlType; self.namespace = options.namespace; self.status = ko.observable(options.namespace.status); // TODO: Compute selection in assist? self.compute = ko.observable(); if (self.namespace.computes.length) { self.compute(self.namespace.computes[0]); } self.name = options.namespace.name; self.dbIndex = {}; self.databases = ko.observableArray(); self.selectedDatabase = ko.observable(); self.highlight = ko.observable(false); self.loadedPromise = $.Deferred(); self.loadedDeferred = $.Deferred(); self.loaded = ko.observable(false); self.loading = ko.observable(false); self.reloading = ko.observable(false); self.hasErrors = ko.observable(false); self.loadingTables = ko.pureComputed( () => typeof self.selectedDatabase() !== 'undefined' && self.selectedDatabase() !== null && self.selectedDatabase().loading() ); self.filter = { querySpec: ko.observable({}).extend({ rateLimit: 300 }) }; self.hasEntries = ko.pureComputed(() => self.databases().length > 0); self.filteredEntries = ko.pureComputed(() => { if ( !self.filter.querySpec() || typeof self.filter.querySpec().query === 'undefined' || !self.filter.querySpec().query ) { return self.databases(); } return self .databases() .filter( database => database.catalogEntry.name .toLowerCase() .indexOf(self.filter.querySpec().query.toLowerCase()) !== -1 ); }); self.autocompleteFromEntries = (nonPartial, partial) => { const result = []; const partialLower = partial.toLowerCase(); self.databases().forEach(db => { if (db.catalogEntry.name.toLowerCase().indexOf(partialLower) === 0) { result.push(nonPartial + partial + db.catalogEntry.name.substring(partial.length)); } }); return result; }; self.selectedDatabase.subscribe(() => { const db = self.selectedDatabase(); if (db) { huePubSub.publish(ASSIST_ACTIVE_DB_CHANGED_EVENT, { connector: this.connector, namespace: this.namespace, database: db.name }); } if (window.HAS_SQL_ANALYZER && db && !db.popularityIndexSet && !self.nonSqlType) { const sqlAnalyzer = sqlAnalyzerRepository.getSqlAnalyzer(db.catalogEntry.getConnector()); db.catalogEntry .loadSqlAnalyzerPopularityForChildren({ silenceErrors: true, sqlAnalyzer }) .then(() => { const applyPopularity = () => { db.entries().forEach(entry => { if ( entry.catalogEntry.sqlAnalyzerPopularity && entry.catalogEntry.sqlAnalyzerPopularity.popularity >= 5 ) { entry.popularity(entry.catalogEntry.sqlAnalyzerPopularity.popularity); } }); }; if (db.loading()) { const subscription = db.loading.subscribe(() => { subscription.dispose(); applyPopularity(); }); } else if (db.entries().length === 0) { const subscription = db.entries.subscribe(newEntries => { if (newEntries.length > 0) { subscription.dispose(); applyPopularity(); } }); } else { applyPopularity(); } }); } }); self.selectedDatabaseChanged = () => { if (self.selectedDatabase()) { if (!self.selectedDatabase().hasEntries() && !self.selectedDatabase().loading()) { self.selectedDatabase().loadEntries(); } if (!self.navigationSettings.rightAssist) { const entry = self.selectedDatabase().catalogEntry; setInLocalStorage( 'assist_' + entry.getConnector().id + '_' + entry.namespace.id + '.lastSelectedDb', entry.name ); huePubSub.publish('assist.database.set', entry); } } else { setInLocalStorage( 'assist_' + self.connector.id + '_' + self.namespace.id + '.lastSelectedDb', null ); } }; const nestedFilter = { querySpec: ko.observable({}).extend({ rateLimit: 300 }) }; self.setDatabase = databaseName => { if ( databaseName && self.selectedDatabase() && databaseName === self.selectedDatabase().catalogEntry.name ) { return; } if (databaseName && self.dbIndex[databaseName]) { self.selectedDatabase(self.dbIndex[databaseName]); self.selectedDatabaseChanged(); return; } let lastSelectedDb = getFromLocalStorage( 'assist_' + self.sourceType + '_' + self.namespace.id + '.lastSelectedDb' ); if (!lastSelectedDb && lastSelectedDb !== '') { lastSelectedDb = 'default'; } if (lastSelectedDb && self.dbIndex[lastSelectedDb]) { self.selectedDatabase(self.dbIndex[lastSelectedDb]); self.selectedDatabaseChanged(); } else if (lastSelectedDb && self.databases().length > 0) { self.selectedDatabase(self.databases()[0]); self.selectedDatabaseChanged(); } }; self.initDatabases = callback => { if (self.loading()) { return; } self.loading(true); self.hasErrors(false); const lastSelectedDbName = self.selectedDatabase() ? self.selectedDatabase().catalogEntry.name : null; self.selectedDatabase(null); self.databases([]); dataCatalog .getEntry({ namespace: self.namespace, compute: self.compute(), connector: self.connector, path: [], definition: { type: 'source' } }) .then(catalogEntry => { self.catalogEntry = catalogEntry; self.catalogEntry .getChildren({ silenceErrors: self.navigationSettings.rightAssist }) .then(databaseEntries => { self.dbIndex = {}; let hasNavMeta = false; const dbs = []; databaseEntries.forEach(catalogEntry => { hasNavMeta = hasNavMeta || !!catalogEntry.navigatorMeta; const database = new AssistDbEntry( catalogEntry, null, self, nestedFilter, self.i18n, self.navigationSettings ); self.dbIndex[catalogEntry.name] = database; if (catalogEntry.name === lastSelectedDbName) { self.selectedDatabase(database); self.selectedDatabaseChanged(); } dbs.push(database); }); if (!hasNavMeta && !self.nonSqlType) { try { self.catalogEntry.loadNavigatorMetaForChildren({ silenceErrors: true }); } catch (err) {} } self.databases(dbs); if (typeof callback === 'function') { callback(); } }) .catch(() => { self.hasErrors(true); }) .finally(() => { self.loaded(true); self.loadedDeferred.resolve(); self.loading(false); self.reloading(false); }); }); }; self.modalItem = ko.observable(); if (!self.navigationSettings.rightAssist) { huePubSub.subscribe('data.catalog.entry.refreshed', details => { if ( self.namespace.id !== details.entry.namespace.id || details.entry.getConnector().id !== self.sourceType ) { return; } if (self.catalogEntry === details.entry) { self.initDatabases(); } else { const targetPath = details.entry.path.join('.'); const findAndReloadInside = entries => { return entries.some(entry => { if (entry.catalogEntry.path.join('.') === targetPath) { entry.catalogEntry = details.entry; entry.loadEntries(); return true; } return findAndReloadInside(entry.entries()); }); }; findAndReloadInside(self.databases()); } }); } } whenLoaded(callback) { const self = this; self.loadedDeferred.done(callback); } highlightInside(catalogEntry) { const self = this; let foundDb; const findDatabase = () => { self.databases().forEach(db => { db.highlight(false); if (db.databaseName === catalogEntry.path[0]) { foundDb = db; } }); if (foundDb) { const whenLoaded = () => { if (self.selectedDatabase() !== foundDb) { self.selectedDatabase(foundDb); } if (!foundDb.open()) { foundDb.open(true); } window.setTimeout(() => { huePubSub.subscribeOnce('assist.db.scrollToComplete', () => { foundDb.highlight(true); // Timeout is for animation effect window.setTimeout(() => { foundDb.highlight(false); }, 1800); }); if (catalogEntry.path.length > 1) { foundDb.highlightInside(catalogEntry.path.slice(1)); } else { huePubSub.publish('assist.db.scrollTo', foundDb); } }, 0); }; if (foundDb.hasEntries()) { whenLoaded(); } else { foundDb.loadEntries(whenLoaded); } } }; if (!self.loaded()) { self.initDatabases(findDatabase); } else { findDatabase(); } } triggerRefresh() { const self = this; if (self.catalogEntry) { self.catalogEntry.clearCache(); } } } export default AssistDbNamespace;