gethue
Version:
Hue is an Open source SQL Query Editor for Databases/Warehouses
932 lines (871 loc) • 32.2 kB
JavaScript
// 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 $ from 'jquery';
import _ from 'lodash';
import * as ko from 'knockout';
import page from 'page';
import { CONFIG_REFRESHED_TOPIC, GET_KNOWN_CONFIG_TOPIC } from './config/events';
import huePubSub from './utils/huePubSub';
import I18n from './utils/i18n';
import waitForObservable from './utils/timing/waitForObservable';
import waitForVariable from './utils/timing/waitForVariable';
import getParameter from './utils/url/getParameter';
import getSearchParameter from './utils/url/getSearchParameter';
import { ASSIST_GET_DATABASE_EVENT, ASSIST_GET_SOURCE_EVENT } from './ko/components/assist/events';
class OnePageViewModel {
constructor() {
const self = this;
self.embeddable_cache = {};
self.currentApp = ko.observable();
self.currentContextParams = ko.observable(null);
self.currentQueryString = ko.observable(null);
self.isLoadingEmbeddable = ko.observable(false);
self.extraEmbeddableURLParams = ko.observable('');
self.getActiveAppViewModel = function (callback) {
const checkInterval = window.setInterval(() => {
const $koElement = $('#' + self.currentApp() + 'Components');
if (
$koElement.length > 0 &&
ko.dataFor($koElement[0]) &&
!(ko.dataFor($koElement[0]) instanceof OnePageViewModel)
) {
window.clearInterval(checkInterval);
callback(ko.dataFor($koElement[0]));
}
}, 25);
};
self.changeEditorType = function (type) {
self.getActiveAppViewModel(viewModel => {
if (viewModel && viewModel.selectedNotebook) {
waitForObservable(viewModel.selectedNotebook, () => {
if (viewModel.editorType() !== type) {
viewModel.selectedNotebook().selectedSnippet(type);
if (!window.ENABLE_HUE_5) {
viewModel.editorType(type);
}
viewModel.newNotebook(type);
}
});
}
});
};
self.currentApp.subscribe(newApp => {
huePubSub.publish('set.current.app.name', newApp);
self.getActiveAppViewModel(viewModel => {
huePubSub.publish('set.current.app.view.model', viewModel);
});
});
huePubSub.subscribe('get.current.app.view.model', () => {
self.getActiveAppViewModel(viewModel => {
huePubSub.publish('set.current.app.view.model', viewModel);
});
});
huePubSub.subscribe('get.current.app.name', callback => {
callback(self.currentApp());
});
huePubSub.subscribe('open.editor.query', resp => {
self.loadApp('editor');
const data = { uuid: resp.history_uuid };
if (resp.handle && resp.handle.session_id) {
data['session'] = {
type: resp.handle.session_type,
id: resp.handle.session_id,
session_id: resp.handle.session_guid,
properties: []
};
}
self.getActiveAppViewModel(viewModel => {
viewModel.openNotebook(data.uuid, null, null, null, data.session);
});
});
huePubSub.subscribe('open.importer.query', data => {
self.loadApp('importer');
self.getActiveAppViewModel(viewModel => {
waitForVariable(viewModel.createWizard, () => {
waitForVariable(viewModel.createWizard.prefill, () => {
viewModel.createWizard.prefill.source_type(data['source_type']);
viewModel.createWizard.prefill.target_type(data['target_type']);
viewModel.createWizard.prefill.target_path(data['target_path']);
viewModel.createWizard.destination.outputFormat(data['target_type']);
});
waitForVariable(viewModel.createWizard.source.query, () => {
viewModel.createWizard.source.query({ id: data.id, name: data.name });
});
waitForVariable(viewModel.createWizard.loadSampleData, () => {
viewModel.createWizard.loadSampleData(data);
});
});
});
});
huePubSub.subscribe('resize.form.actions', () => {
document.styleSheets[0].addRule(
'.form-actions',
'width: ' + $('.page-content').width() + 'px'
);
if ($('.content-panel:visible').length > 0) {
document.styleSheets[0].addRule('.form-actions', 'margin-left: -11px !important');
}
});
huePubSub.subscribe('split.panel.resized', () => {
huePubSub.publish('resize.form.actions');
huePubSub.publish('resize.plotly.chart');
});
huePubSub.publish('resize.form.actions');
huePubSub.subscribe('open.editor.new.query', statementOptions => {
self.loadApp('editor'); // Should open in Default
self.getActiveAppViewModel(viewModel => {
const editorType = statementOptions['type'] || 'hive'; // Next: use file extensions and default type of Editor for SQL
viewModel.newNotebook(editorType, () => {
self.changeEditorType(editorType);
if (statementOptions['statementPath']) {
viewModel
.selectedNotebook()
.snippets()[0]
.statementType(statementOptions['statementType']);
viewModel
.selectedNotebook()
.snippets()[0]
.statementPath(statementOptions['statementPath']);
}
if (statementOptions['directoryUuid']) {
viewModel.selectedNotebook().directoryUuid(statementOptions['directoryUuid']);
}
});
});
});
const loadedJs = [];
const loadedCss = [];
const loadedApps = [];
$('script[src]').each(function () {
loadedJs.push($(this).attr('src'));
});
$('link[href]').each(function () {
loadedCss.push($(this).attr('href'));
});
const loadScript = function (scriptUrl) {
const deferred = $.Deferred();
$.ajax({
url: scriptUrl,
converters: {
'text script': function (text) {
return text;
}
}
})
.done(contents => {
loadedJs.push(scriptUrl);
deferred.resolve({ url: scriptUrl, contents: contents });
})
.fail(() => {
deferred.resolve('');
});
return deferred.promise();
};
const loadScripts = function (scriptUrls) {
const promises = [];
while (scriptUrls.length) {
const scriptUrl = scriptUrls.shift();
if (loadedJs.indexOf(scriptUrl) !== -1) {
continue;
}
promises.push(loadScript(scriptUrl));
}
return promises;
};
const addGlobalCss = function ($el) {
const cssFile = $el.attr('href').split('?')[0];
if (loadedCss.indexOf(cssFile) === -1) {
loadedCss.push(cssFile);
$.ajaxSetup({ cache: true });
if (window.DEV) {
$el.attr('href', $el.attr('href') + '?dev=' + Math.random());
}
$el.clone().appendTo($('head'));
$.ajaxSetup({ cache: false });
}
$el.remove();
};
// Only load CSS and JS files that are not loaded before
self.processHeaders = function (response) {
const promise = $.Deferred();
const $rawHtml = $('<span>').html(response);
const $allScripts = $rawHtml.find('script[src]');
const scriptsToLoad = $allScripts
.map(function () {
return $(this).attr('src');
})
.toArray();
$allScripts.remove();
$rawHtml.find('link[href]').each(function () {
addGlobalCss($(this)); // Also removes the elements;
});
$rawHtml.find('a[href]').each(function () {
let link = $(this).attr('href');
if (link.startsWith('/') && !link.startsWith('/hue')) {
link = window.HUE_BASE_URL + '/hue' + link;
}
$(this).attr('href', link);
});
const scriptPromises = loadScripts(scriptsToLoad);
const evalScriptSync = function () {
if (scriptPromises.length) {
// Evaluate the scripts in the order they were defined in the page
const nextScriptPromise = scriptPromises.shift();
nextScriptPromise.done(scriptDetails => {
if (scriptDetails.contents) {
$.globalEval(scriptDetails.contents);
}
evalScriptSync();
});
} else {
// All evaluated
promise.resolve($rawHtml.children());
}
};
evalScriptSync();
return promise;
};
huePubSub.subscribe('hue4.process.headers', opts => {
self.processHeaders(opts.response).done(rawHtml => {
opts.callback(rawHtml);
});
});
let loadAppTimeout = -1;
self.loadApp = (app, loadDeep) => {
// This prevents loading an app twice when the URL is changed multiple times in one operation
window.clearTimeout(loadAppTimeout);
loadAppTimeout = window.setTimeout(() => {
self.loadAppThrottled(app, loadDeep);
}, 0);
};
self.loadAppThrottled = (app, loadDeep) => {
if (self.currentApp() === 'editor' && $('#editorComponents').length) {
const vm = ko.dataFor($('#editorComponents')[0]);
if (vm.isPresentationMode()) {
vm.selectedNotebook().isPresentationMode(false);
}
}
if (
self.currentApp() === 'editor' &&
self.embeddable_cache['editor'] &&
!$('#editorComponents').length
) {
self.embeddable_cache['editor'] = undefined;
}
self.currentApp(app);
if (!app.startsWith('security')) {
self.lastContext = null;
}
window.SKIP_CACHE.forEach(skipped => {
huePubSub.publish('app.dom.unload', skipped);
$('#embeddable_' + skipped).html('');
});
self.isLoadingEmbeddable(true);
loadedApps.forEach(loadedApp => {
window.pauseAppIntervals(loadedApp);
huePubSub.pauseAppSubscribers(loadedApp);
});
$('.tooltip').hide();
huePubSub.publish('hue.datatable.search.hide');
huePubSub.publish('hue.scrollleft.hide');
huePubSub.publish('context.panel.visible', false);
if (app.startsWith('oozie')) {
huePubSub.clearAppSubscribers('oozie');
}
if (app.startsWith('security')) {
$('#embeddable_security_hive').html('');
$('#embeddable_security_hdfs').html('');
$('#embeddable_security_hive2').html('');
$('#embeddable_security_solr').html('');
}
if (typeof self.embeddable_cache[app] === 'undefined') {
if (loadedApps.indexOf(app) === -1) {
loadedApps.push(app);
}
let baseURL = window.EMBEDDABLE_PAGE_URLS[app].url;
if (self.currentContextParams() !== null) {
if (loadDeep && self.currentContextParams()[0]) {
baseURL += self.currentContextParams()[0];
} else {
const route = new page.Route(baseURL);
route.keys.forEach(key => {
if (key.name === 0) {
if (typeof self.currentContextParams()[key.name] !== 'undefined') {
if (app === 'filebrowser') {
baseURL = baseURL
.replace('*', encodeURIComponent(self.currentContextParams()[key.name]))
.replace(/#/g, '%23');
} else {
baseURL = baseURL.replace(
'*',
encodeURI(self.currentContextParams()[key.name])
); // We have some really funky stuff in here, this should be encodeURIComponent
}
} else {
baseURL = baseURL.replace('*', '');
}
} else {
baseURL = baseURL.replace(
':' + key.name,
encodeURI(self.currentContextParams()[key.name])
); // We have some really funky stuff in here, this should be encodeURIComponent
}
});
}
self.currentContextParams(null);
}
if (self.currentQueryString() !== null) {
baseURL += (baseURL.indexOf('?') > -1 ? '&' : '?') + encodeURI(self.currentQueryString());
self.currentQueryString(null);
}
$.ajax({
url:
baseURL +
(baseURL.indexOf('?') > -1 ? '&' : '?') +
'is_embeddable=true' +
self.extraEmbeddableURLParams(),
beforeSend: function (xhr) {
xhr.setRequestHeader('X-Requested-With', 'Hue');
},
dataType: 'html',
success: function (response, status, xhr) {
const type = xhr.getResponseHeader('Content-Type');
if (type.indexOf('text/') > -1) {
window.clearAppIntervals(app);
huePubSub.clearAppSubscribers(app);
self.extraEmbeddableURLParams('');
self.processHeaders(response).done($rawHtml => {
if (window.SKIP_CACHE.indexOf(app) === -1) {
self.embeddable_cache[app] = $rawHtml;
}
$('#embeddable_' + app).html($rawHtml);
huePubSub.publish('app.dom.loaded', app);
window.setTimeout(() => {
self.isLoadingEmbeddable(false);
}, 0);
});
} else {
if (type.indexOf('json') > -1) {
const presponse = JSON.parse(response);
if (presponse && presponse.url) {
window.location.href = window.HUE_BASE_URL + presponse.url;
return;
}
}
window.location.href = window.HUE_BASE_URL + baseURL;
}
},
error: function (xhr) {
console.error('Route loading problem', xhr);
if ((xhr.status === 401 || xhr.status === 403) && app !== '403') {
self.loadApp('403');
} else if (app !== '500') {
self.loadApp('500');
} else {
$.jHueNotify.error(
I18n(
'It looks like you are offline or an unknown error happened. Please refresh the page.'
)
);
}
}
});
} else {
self.isLoadingEmbeddable(false);
}
window.document.title = 'Hue - ' + window.EMBEDDABLE_PAGE_URLS[app].title;
window.resumeAppIntervals(app);
huePubSub.resumeAppSubscribers(app);
$('.embeddable').hide();
$('#embeddable_' + app).show();
huePubSub.publish('app.gained.focus', app);
huePubSub.publish('resize.form.actions');
};
self.dropzoneError = function (filename) {
self.loadApp('importer');
self.getActiveAppViewModel(vm => {
vm.createWizard.source.path(DROPZONE_HOME_DIR + '/' + filename);
});
$('.dz-drag-hover').removeClass('dz-drag-hover');
};
const openImporter = function (path) {
let databasename = '';
let interpreter = '';
const table = path
.substring(path.lastIndexOf(':') + 1, path.lastIndexOf(';'))
.slice(0, -4)
.replace(/[^a-zA-Z0-9]/g, '_');
self.loadApp('importer');
huePubSub.publish(ASSIST_GET_SOURCE_EVENT, source => {
interpreter = source;
});
huePubSub.publish(ASSIST_GET_DATABASE_EVENT, {
connector: { id: interpreter },
callback: databaseDef => {
databasename = databaseDef.name;
}
});
self.getActiveAppViewModel(vm => {
vm.createWizard.source.path(path);
vm.currentStep(2);
vm.createWizard.source.interpreter(interpreter);
vm.createWizard.destination.name(databasename + '.' + table);
waitForObservable(vm.createWizard.destination.name, () => {
vm.createWizard.indexFile();
});
});
};
const openImporterFilebrowser = function (path) {
self.loadApp('importer');
self.getActiveAppViewModel(vm => {
vm.createWizard.source.inputFormat('file');
window.setTimeout(() => {
vm.createWizard.source.path(path);
}, 0);
});
};
self.dropzoneComplete = function (path) {
if (path.toLowerCase().endsWith('.csv')) {
openImporter(path);
} else {
huePubSub.publish('open.link', '/filebrowser/view=' + path);
}
$('.dz-drag-hover').removeClass('dz-drag-hover');
};
huePubSub.subscribe('open.in.importer', openImporterFilebrowser);
huePubSub.subscribe('assist.dropzone.complete', self.dropzoneComplete);
// prepend /hue to all the link on this page
$('a[href]').each(function () {
let link = $(this).attr('href');
if (link.startsWith('/') && !link.startsWith('/hue')) {
link = window.HUE_BASE_URL + '/hue' + link;
}
$(this).attr('href', link);
});
page.base(window.HUE_BASE_URL + '/hue');
const getUrlParameter = name => getParameter(name) || '';
self.lastContext = null;
const pageMapping = [
{ url: '/403', app: '403' },
{ url: '/500', app: '500' },
{ url: '/about/', app: 'admin_wizard' },
{ url: '/about/admin_wizard', app: 'admin_wizard' },
{
url: '/accounts/logout',
app: function () {
location.href = window.HUE_BASE_URL + '/accounts/logout';
}
},
{
url: '/dashboard/admin/collections',
app: function (ctx) {
page('/home/?type=search-dashboard');
}
},
{ url: '/dashboard/*', app: 'dashboard' },
{
url: '/desktop/api/desktop/api2/doc/export*',
app: function () {
const documents = getUrlParameter('documents');
location.href = window.HUE_BASE_URL + '/desktop/api2/doc/export?documents=' + documents;
}
},
{ url: '/desktop/dump_config', app: 'dump_config' },
{
url: '/desktop/debug/threads',
app: function () {
self.loadApp('threads');
self.getActiveAppViewModel(viewModel => {
viewModel.fetchThreads();
});
}
},
{
url: '/gist',
app: function () {
const uuid = getUrlParameter('uuid');
location.href = '/desktop/api2/gist/open?uuid=' + uuid;
}
},
{
url: '/desktop/metrics',
app: function () {
self.loadApp('metrics');
self.getActiveAppViewModel(viewModel => {
viewModel.fetchMetrics();
});
}
},
{
url: '/desktop/connectors',
app: function () {
self.loadApp('connectors');
}
},
{
url: '/desktop/analytics',
app: function () {
self.loadApp('analytics');
self.getActiveAppViewModel(viewModel => {
viewModel.fetchAnalytics();
});
}
},
{
url: '/desktop/download_logs',
app: function () {
location.href = window.HUE_BASE_URL + '/desktop/download_logs';
}
},
{
url: '/editor',
app: function () {
// Defer to allow window.location param update
_.defer(() => {
if (typeof self.embeddable_cache['editor'] === 'undefined') {
if (getUrlParameter('editor') !== '') {
self.extraEmbeddableURLParams('&editor=' + getUrlParameter('editor'));
} else if (getUrlParameter('type') !== '' && getUrlParameter('type') !== 'notebook') {
self.extraEmbeddableURLParams('&type=' + getUrlParameter('type'));
}
self.loadApp('editor');
} else {
self.loadApp('editor');
if (getUrlParameter('editor') !== '') {
self.getActiveAppViewModel(viewModel => {
self.isLoadingEmbeddable(true);
viewModel
.openNotebook(getUrlParameter('editor'))
[window.ENABLE_HUE_5 ? 'finally' : 'always'](() => {
self.isLoadingEmbeddable(false);
});
});
} else if (getUrlParameter('type') !== '') {
self.changeEditorType(getUrlParameter('type'));
}
}
});
}
},
{
url: '/notebook/editor',
app: function (ctx) {
page('/editor?' + ctx.querystring);
}
},
{ url: '/filebrowser/view=*', app: 'filebrowser' },
{
url: '/filebrowser/*',
app: function () {
page('/filebrowser/view=' + DROPZONE_HOME_DIR);
}
},
{ url: '/hbase/', app: 'hbase' },
{ url: '/help', app: 'help' },
{
url: '/home2*',
app: function (ctx) {
page(ctx.path.replace(/home2/gi, 'home'));
}
},
{ url: '/home*', app: 'home' },
{ url: '/catalog', app: 'catalog' },
{ url: '/kafka/', app: 'kafka' },
{ url: '/indexer/topics/*', app: 'kafka' },
{ url: '/indexer/indexes', app: 'indexes' },
{ url: '/indexer/indexes/*', app: 'indexes' },
{ url: '/indexer/', app: 'indexes' },
{ url: '/indexer/importer/', app: 'importer' },
{
url: '/indexer/importer/prefill/*',
app: function (ctx) {
self.loadApp('importer');
self.getActiveAppViewModel(viewModel => {
const _params = ctx.path.match(
/\/indexer\/importer\/prefill\/?([^/]+)\/?([^/]+)\/?([^/]+)?/
);
if (!_params) {
console.warn('Could not match ' + ctx.path);
}
waitForVariable(viewModel.createWizard, () => {
waitForVariable(viewModel.createWizard.prefill, () => {
viewModel.createWizard.prefill.source_type(_params && _params[1] ? _params[1] : '');
viewModel.createWizard.prefill.target_type(_params && _params[2] ? _params[2] : '');
viewModel.createWizard.prefill.target_path(_params && _params[3] ? _params[3] : '');
});
});
});
}
},
{
url: '/jobbrowser/jobs/job_*',
app: function (ctx) {
page.redirect(
'/jobbrowser#!id=application_' + _.trimEnd(ctx.params[0], '/').split('/')[0]
);
}
},
{
url: '/jobbrowser/jobs/application_*',
app: function (ctx) {
page.redirect(
'/jobbrowser#!id=application_' + _.trimEnd(ctx.params[0], '/').split('/')[0]
);
}
},
{ url: '/jobbrowser*', app: 'jobbrowser' },
{ url: '/logs', app: 'logs' },
{
url: '/metastore',
app: function () {
page('/metastore/tables');
}
},
{ url: '/metastore/*', app: 'metastore' },
{
url: '/notebook',
app: function (ctx) {
self.loadApp('notebook');
const notebookId = getSearchParameter('?' + ctx.querystring, 'notebook');
if (notebookId !== '') {
self.getActiveAppViewModel(viewModel => {
self.isLoadingEmbeddable(true);
viewModel.openNotebook(notebookId)[window.ENABLE_HUE_5 ? 'finally' : 'always'](() => {
self.isLoadingEmbeddable(false);
});
});
} else {
self.getActiveAppViewModel(viewModel => {
viewModel.newNotebook('notebook');
});
}
}
},
{
url: '/notebook/notebook',
app: function (ctx) {
page('/notebook?' + ctx.querystring);
}
},
{
url: '/notebook/notebooks',
app: function (ctx) {
page('/home/?' + ctx.querystring);
}
},
{
url: '/oozie/editor/bundle/list',
app: function (ctx) {
page('/home/?type=oozie-bundle');
}
},
{ url: '/oozie/editor/bundle/*', app: 'oozie_bundle' },
{
url: '/oozie/editor/coordinator/list',
app: function (ctx) {
page('/home/?type=oozie-coordinator');
}
},
{ url: '/oozie/editor/coordinator/*', app: 'oozie_coordinator' },
{
url: '/oozie/editor/workflow/list',
app: function (ctx) {
page('/home/?type=oozie-workflow');
}
},
{ url: '/oozie/editor/workflow/*', app: 'oozie_workflow' },
{ url: '/oozie/list_oozie_info', app: 'oozie_info' },
{
url: '/oozie/list_oozie_sla',
app: function () {
page.redirect('/jobbrowser/#!slas');
}
},
{
url: '/pig',
app: function () {
self.loadApp('editor');
self.changeEditorType('pig');
}
},
{ url: '/search/*', app: 'dashboard' },
{
url: '/security/hdfs',
app: function (ctx) {
if (self.lastContext == null || ctx.path !== self.lastContext.path) {
self.loadApp('security_hdfs');
}
self.lastContext = ctx;
}
},
{
url: '/security/hive',
app: function (ctx) {
if (self.lastContext == null || ctx.path !== self.lastContext.path) {
self.loadApp('security_hive');
}
self.lastContext = ctx;
}
},
{
url: '/security/hive2',
app: function (ctx) {
if (self.lastContext == null || ctx.path !== self.lastContext.path) {
self.loadApp('security_hive2');
}
self.lastContext = ctx;
}
},
{
url: '/security/solr',
app: function (ctx) {
if (self.lastContext == null || ctx.path !== self.lastContext.path) {
self.loadApp('security_solr');
}
self.lastContext = ctx;
}
},
{
url: '/security',
app: function () {
page('/security/hive');
}
},
{ url: '/sqoop', app: 'sqoop' },
{ url: '/jobsub', app: 'jobsub' },
{ url: '/useradmin/configurations/', app: 'useradmin_configurations' },
{ url: '/useradmin/organizations/', app: 'useradmin_organizations' },
{ url: '/useradmin/groups/', app: 'useradmin_groups' },
{ url: '/useradmin/groups/new', app: 'useradmin_newgroup' },
{ url: '/useradmin/groups/edit/:group', app: 'useradmin_editgroup' },
{ url: '/useradmin/permissions/', app: 'useradmin_permissions' },
{ url: '/useradmin/permissions/edit/*', app: 'useradmin_editpermission' },
{ url: '/useradmin/users/', app: 'useradmin_users' },
{ url: '/useradmin/users/add_ldap_users', app: 'useradmin_addldapusers' },
{ url: '/useradmin/users/add_ldap_groups', app: 'useradmin_addldapgroups' },
{ url: '/useradmin/users/edit/:user', app: 'useradmin_edituser' },
{ url: '/useradmin/users/new', app: 'useradmin_newuser' },
{ url: '/useradmin', app: 'useradmin_users' }
];
window.OTHER_APPS.forEach(otherApp => {
pageMapping.push({
url: '/' + otherApp + '*',
app: ctx => {
self.currentContextParams(ctx.params);
self.currentQueryString(ctx.querystring);
self.loadApp(otherApp, true);
}
});
});
const getModifiedCtxParamForFilePath = (ctx, mappingUrl) => {
// FIX. The page library decodes the ctx.params differently than it decodes
// the ctx.path, e.g. '+' is turned into ' ' which we don't want. Therefore we use
// the path to extract the params manually and get the correct characters.
const pathWithoutParams = mappingUrl.slice(0, -1);
const paramsOnly = ctx.path.replace(pathWithoutParams, '');
const filePathParam = decodeURIComponent(paramsOnly);
// Unfortunate hack needed since % has to be double encoded since the page library
// decodes it twice. This is temporarily double encoding only between the
// huePubSub.publish('open.link', fullUrl); and the onePgeViewModel.js.
return filePathParam.replaceAll('%25', '%');
};
pageMapping.forEach(mapping => {
page(
mapping.url,
_.isFunction(mapping.app)
? mapping.app
: ctx => {
const ctxParams =
mapping.app === 'filebrowser'
? { 0: getModifiedCtxParamForFilePath(ctx, mapping.url) }
: ctx.params;
self.currentContextParams(ctxParams);
self.currentQueryString(ctx.querystring);
self.loadApp(mapping.app);
}
);
});
const configUpdated = clusterConfig => {
page('/', () => {
page(clusterConfig['main_button_action'].page);
});
page('*', ctx => {
console.error('Route not found', ctx);
self.loadApp('404');
});
page();
};
huePubSub.publish(GET_KNOWN_CONFIG_TOPIC, configUpdated);
huePubSub.subscribe(CONFIG_REFRESHED_TOPIC, configUpdated);
huePubSub.subscribe('open.link', href => {
if (href) {
const prefix = '/hue';
if (href.startsWith('/')) {
if (window.HUE_BASE_URL.length && href.startsWith(window.HUE_BASE_URL)) {
page(href);
} else if (href.startsWith(prefix)) {
page(window.HUE_BASE_URL + href);
} else {
page(window.HUE_BASE_URL + prefix + href);
}
} else if (href.indexOf('#') == 0) {
// Only place that seem to use this is hbase onclick row
window.location.hash = href;
} else {
page(href);
}
} else {
console.warn('Received an open.link without href.');
}
});
huePubSub.subscribe(
'open.filebrowserlink',
({ pathPrefix, decodedPath, fileBrowserModel, browserTarget }) => {
if (pathPrefix.includes('download=')) {
// The download view on the backend requires the slashes not to
// be encoded in order for the file to be correctly named.
const encodedPath = encodeURIComponent(decodedPath).replaceAll('%2F', '/');
const possibleKnoxUrlPathPrefix = window.HUE_BASE_URL;
window.location = possibleKnoxUrlPathPrefix + pathPrefix + encodedPath;
return;
}
const appPrefix = '/hue';
const urlEncodedPercentage = '%25';
// Fix. The '%' character needs to be encoded twice due to a bug in the page library
// that decodes the url twice. Even when we don't directly call page() we still need this
// fix since the user can reload the page which will trigger a call to page().
const pageFixedEncodedPath = encodeURIComponent(
decodedPath.replaceAll('%', urlEncodedPercentage)
);
const href = window.HUE_BASE_URL + appPrefix + pathPrefix + pageFixedEncodedPath;
if (browserTarget) {
window.open(href, browserTarget);
return;
}
// We don't want reload the entire filebrowser when navigating between folders
// and already on the listdir_components page.
if (fileBrowserModel) {
fileBrowserModel.targetPath(pathPrefix + encodeURIComponent(decodedPath));
window.history.pushState(null, '', href);
fileBrowserModel.retrieveData();
} else {
page(href);
}
}
);
}
}
export default OnePageViewModel;