gethue
Version:
Hue is an Open source SQL Query Editor for Databases/Warehouses
1,749 lines (1,636 loc) • 49.3 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 * as ko from 'knockout';
import {
assistErrorCallback,
cancelActiveRequest,
simpleGet,
simplePost,
successResponseIsError
} from './apiUtils';
import * as URLS from './urls';
import apiQueueManager from '../api/apiQueueManager';
import CancellableJqPromise from '../api/cancellableJqPromise';
import hueDebug from '../utils/hueDebug';
import huePubSub from '../utils/huePubSub';
import { getFromLocalStorage, setInLocalStorage } from '../utils/storageUtils';
export const LINK_SHARING_PERMS = {
READ: 'read',
WRITE: 'write',
OFF: 'off'
};
class ApiHelper {
constructor() {
this.queueManager = apiQueueManager;
this.cancelActiveRequest = cancelActiveRequest; // TODO: Remove when job_browser.mako is in webpack
huePubSub.subscribe('assist.clear.git.cache', () => {
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'git' }), {});
});
huePubSub.subscribe('assist.clear.collections.cache', () => {
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'collections' }), {});
});
huePubSub.subscribe('assist.clear.hbase.cache', () => {
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'hbase' }), {});
});
huePubSub.subscribe('assist.clear.document.cache', () => {
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'document' }), {});
});
const clearAllCaches = () => {
this.clearDbCache({
sourceType: 'hive',
clearAll: true
});
this.clearDbCache({
sourceType: 'impala',
clearAll: true
});
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'hdfs' }), {});
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'adls' }), {});
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'abfs' }), {});
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'ofs' }), {});
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'git' }), {});
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 's3' }), {});
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'collections' }), {});
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'hbase' }), {});
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: 'document' }), {});
};
huePubSub.subscribe('assist.clear.all.caches', clearAllCaches);
if (window.performance && window.performance.navigation) {
if (window.performance.navigation.type === 1 && location.href.indexOf('/metastore') !== -1) {
// Browser refresh of the metastore page
clearAllCaches();
}
}
}
clearStorageCache(sourceType) {
setInLocalStorage(this.getAssistCacheIdentifier({ sourceType: sourceType }), {});
}
hasExpired(timestamp, cacheType) {
if (typeof hueDebug !== 'undefined' && typeof hueDebug.cacheTimeout !== 'undefined') {
return new Date().getTime() - timestamp > hueDebug.cacheTimeout;
}
return new Date().getTime() - timestamp > CACHEABLE_TTL[cacheType];
}
/**
*
* @param {Object} options
* @param {string} options.sourceType
* @param {string} options.url
* @param {boolean} options.refreshCache
* @param {string} [options.hash] - Optional hash to use as well as the url
* @param {Function} options.fetchFunction
* @param {Function} options.successCallback
* @param {string} [options.cacheType] - Possible values 'default'|'sqlAnalyzer'. Default value 'default'
* @param {Object} [options.editor] - Ace editor
* @param {Object} [options.promise] - Optional promise that will be resolved if cached data exists
*/
fetchCached(options) {
const cacheIdentifier = this.getAssistCacheIdentifier(options);
const cachedData = getFromLocalStorage(cacheIdentifier) || {};
const cachedId = options.hash ? options.url + options.hash : options.url;
if (
options.refreshCache ||
typeof cachedData[cachedId] == 'undefined' ||
this.hasExpired(cachedData[cachedId].timestamp, options.cacheType || 'default')
) {
if (typeof options.editor !== 'undefined' && options.editor !== null) {
options.editor.showSpinner();
}
return options.fetchFunction(data => {
cachedData[cachedId] = {
timestamp: new Date().getTime(),
data: data
};
try {
setInLocalStorage(cacheIdentifier, cachedData);
} catch (e) {}
});
} else {
if (options.promise) {
options.promise.resolve(cachedData[cachedId].data);
}
options.successCallback(cachedData[cachedId].data);
}
}
/**
* @param {object} options
* @param {string} options.sourceType
* @param {string} [options.cacheType] - Default value 'default'
* @returns {string}
*/
getAssistCacheIdentifier(options) {
return 'hue.assist.' + (options.cacheType || 'default') + '.' + options.sourceType;
}
/**
* @param {Object} data
* @param {Object} options
* @param {function} [options.successCallback]
*/
saveSnippetToFile(data, options) {
$.post(
URLS.SAVE_TO_FILE_API,
data,
result => {
if (typeof options.successCallback !== 'undefined') {
options.successCallback(result);
}
},
'json'
).fail(assistErrorCallback(options));
}
fetchUsersAndGroups(options) {
$.ajax({
method: 'GET',
url: '/desktop/api/users/autocomplete',
data: options.data || {},
contentType: 'application/json'
})
.done(response => {
options.successCallback(response);
})
.fail(response => {
options.errorCallback(response);
});
}
fetchUsersByIds(options) {
$.ajax({
method: 'GET',
url: '/desktop/api/users',
data: { userids: options.userids },
contentType: 'application/json'
})
.done(response => {
options.successCallback(response);
})
.fail(response => {
options.errorCallback(response);
});
}
/**
*
* @param {Object} options
* @param {string} options.location
* @param {boolean} [options.silenceErrors]
*/
fetchTopo(options) {
const url = URLS.TOPO_URL + options.location;
return simpleGet(url, undefined, options);
}
/**
*
* @param {Object} options
* @param {string[]} options.path
* @param {string} options.type - 's3', 'adls', 'abfs', 'ofs' or 'hdfs'
* @param {number} [options.offset]
* @param {number} [options.length]
* @param {boolean} [options.silenceErrors]
*/
fetchStoragePreview(options) {
let url;
if (options.type === 's3') {
url = URLS.S3_API_PREFIX;
} else if (options.type === 'adls') {
url = URLS.ADLS_API_PREFIX;
} else if (options.type === 'abfs') {
url = URLS.ABFS_API_PREFIX;
} else if (options.type === 'ofs') {
url = URLS.OFS_API_PREFIX;
} else {
url = URLS.HDFS_API_PREFIX;
}
const clonedPath = options.path.concat();
if (clonedPath.length && clonedPath[0] === '/') {
clonedPath.shift();
}
url += clonedPath.join('/').replace(/#/g, '%23') + '?compression=none&mode=text';
url += '&offset=' + (options.offset || 0);
url += '&length=' + (options.length || 118784);
const deferred = $.Deferred();
$.ajax({
dataType: 'json',
url: url,
success: data => {
if (successResponseIsError(data)) {
deferred.reject(assistErrorCallback(options)(data));
} else {
deferred.resolve(data);
}
},
fail: deferred.reject
});
return deferred.promise();
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
* @param {Number} [options.timeout]
* @param {Object} [options.editor] - Ace editor
*
* @param {string[]} options.pathParts
* @param {number} [options.pageSize] - Default 500
* @param {number} [options.page] - Default 1
* @param {string} [options.filter]
*/
fetchHdfsPath(options) {
if (
options.pathParts.length > 0 &&
(options.pathParts[0] === '/' || options.pathParts[0] === '')
) {
options.pathParts.shift();
}
let url =
URLS.HDFS_API_PREFIX +
encodeURI(options.pathParts.join('/')) +
'?format=json&sortby=name&descending=false&pagesize=' +
(options.pageSize || 500) +
'&pagenum=' +
(options.page || 1);
if (options.filter) {
url += '&filter=' + options.filter;
}
const fetchFunction = storeInCache => {
if (options.timeout === 0) {
assistErrorCallback(options)({ status: -1 });
return;
}
return $.ajax({
dataType: 'json',
url: url,
timeout: options.timeout,
success: data => {
if (
!data.error &&
!successResponseIsError(data) &&
typeof data.files !== 'undefined' &&
data.files !== null
) {
if (data.files.length > 2 && !options.filter) {
storeInCache(data);
}
options.successCallback(data);
} else {
assistErrorCallback(options)(data);
}
}
})
.fail(assistErrorCallback(options))
.always(() => {
if (typeof options.editor !== 'undefined' && options.editor !== null) {
options.editor.hideSpinner();
}
});
};
return this.fetchCached(
$.extend({}, options, {
sourceType: 'hdfs',
url: url,
fetchFunction: fetchFunction
})
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
* @param {Number} [options.timeout]
* @param {Object} [options.editor] - Ace editor
*
* @param {string[]} options.pathParts
* @param {number} [options.pageSize] - Default 500
* @param {number} [options.page] - Default 1
* @param {string} [options.filter]
*/
fetchAdlsPath(options) {
options.pathParts.shift();
let url =
URLS.ADLS_API_PREFIX +
encodeURI(options.pathParts.join('/')) +
'?format=json&sortby=name&descending=false&pagesize=' +
(options.pageSize || 500) +
'&pagenum=' +
(options.page || 1);
if (options.filter) {
url += '&filter=' + options.filter;
}
const fetchFunction = storeInCache => {
if (options.timeout === 0) {
assistErrorCallback(options)({ status: -1 });
return;
}
return $.ajax({
dataType: 'json',
url: url,
timeout: options.timeout,
success: data => {
if (
!data.error &&
!successResponseIsError(data) &&
typeof data.files !== 'undefined' &&
data.files !== null
) {
if (data.files.length > 2 && !options.filter) {
storeInCache(data);
}
options.successCallback(data);
} else {
assistErrorCallback(options)(data);
}
}
})
.fail(assistErrorCallback(options))
.always(() => {
if (typeof options.editor !== 'undefined' && options.editor !== null) {
options.editor.hideSpinner();
}
});
};
return this.fetchCached(
$.extend({}, options, {
sourceType: 'adls',
url: url,
fetchFunction: fetchFunction
})
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
* @param {Number} [options.timeout]
* @param {Object} [options.editor] - Ace editor
*
* @param {string[]} options.pathParts
* @param {number} [options.pageSize] - Default 500
* @param {number} [options.page] - Default 1
* @param {string} [options.filter]
*/
fetchAbfsPath(options) {
let url =
URLS.ABFS_API_PREFIX +
encodeURI(options.pathParts.join('/')) +
'?format=json&sortby=name&descending=false&pagesize=' +
(options.pageSize || 500) +
'&pagenum=' +
(options.page || 1);
if (options.filter) {
url += '&filter=' + options.filter;
}
const fetchFunction = storeInCache => {
if (options.timeout === 0) {
assistErrorCallback(options)({ status: -1 });
return;
}
return $.ajax({
dataType: 'json',
url: url,
timeout: options.timeout,
success: data => {
if (
!data.error &&
!successResponseIsError(data) &&
typeof data.files !== 'undefined' &&
data.files !== null
) {
if (data.files.length > 2 && !options.filter) {
storeInCache(data);
}
options.successCallback(data);
} else {
assistErrorCallback(options)(data);
}
}
})
.fail(assistErrorCallback(options))
.always(() => {
if (typeof options.editor !== 'undefined' && options.editor !== null) {
options.editor.hideSpinner();
}
});
};
return this.fetchCached(
$.extend({}, options, {
sourceType: 'abfs',
url: url,
fetchFunction: fetchFunction
})
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
* @param {Number} [options.timeout]
* @param {Object} [options.editor] - Ace editor
*
* @param {string[]} options.pathParts
* @param {number} [options.pageSize] - Default 500
* @param {number} [options.page] - Default 1
* @param {string} [options.filter]
*/
fetchOfsPath(options) {
let url =
URLS.OFS_API_PREFIX +
options.pathParts.join('/') +
'?format=json&sortby=name&descending=false&pagesize=' +
(options.pageSize || 500) +
'&pagenum=' +
(options.page || 1);
if (options.filter) {
url += '&filter=' + options.filter;
}
const fetchFunction = storeInCache => {
if (options.timeout === 0) {
assistErrorCallback(options)({ status: -1 });
return;
}
return $.ajax({
dataType: 'json',
url: url,
timeout: options.timeout,
success: data => {
if (
!data.error &&
!successResponseIsError(data) &&
typeof data.files !== 'undefined' &&
data.files !== null
) {
if (data.files.length > 2 && !options.filter) {
storeInCache(data);
}
options.successCallback(data);
} else {
assistErrorCallback(options)(data);
}
}
})
.fail(assistErrorCallback(options))
.always(() => {
if (typeof options.editor !== 'undefined' && options.editor !== null) {
options.editor.hideSpinner();
}
});
};
return this.fetchCached(
$.extend({}, options, {
sourceType: 'ofs',
url: url,
fetchFunction: fetchFunction
})
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
* @param {Number} [options.timeout]
*
* @param {string[]} options.pathParts
* @param {string} options.fileType
*/
fetchGitContents(options) {
const url =
URLS.GIT_API_PREFIX +
'?path=' +
encodeURI(options.pathParts.join('/')) +
'&fileType=' +
options.fileType;
const fetchFunction = storeInCache => {
if (options.timeout === 0) {
assistErrorCallback(options)({ status: -1 });
return;
}
$.ajax({
dataType: 'json',
url: url,
timeout: options.timeout,
success: data => {
if (!data.error && !successResponseIsError(data)) {
if (
data.fileType === 'dir' &&
typeof data.files !== 'undefined' &&
data.files !== null
) {
if (data.files.length > 2) {
storeInCache(data);
}
options.successCallback(data);
} else if (
data.fileType === 'file' &&
typeof data.content !== 'undefined' &&
data.content !== null
) {
options.successCallback(data);
}
} else {
assistErrorCallback(options)(data);
}
}
}).fail(assistErrorCallback(options));
};
this.fetchCached(
$.extend({}, options, {
sourceType: 'git',
url: url,
fetchFunction: fetchFunction
})
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
* @param {Number} [options.timeout]
* @param {Object} [options.editor] - Ace editor
*
* @param {string[]} options.pathParts
* @param {number} [options.pageSize] - Default 500
* @param {number} [options.page] - Default 1
* @param {string} [options.filter]
*/
fetchS3Path(options) {
options.pathParts.shift(); // remove the trailing /
let url =
URLS.S3_API_PREFIX +
encodeURI(options.pathParts.join('/')) +
'?format=json&sortby=name&descending=false&pagesize=' +
(options.pageSize || 500) +
'&pagenum=' +
(options.page || 1);
if (options.filter) {
url += '&filter=' + options.filter;
}
const fetchFunction = storeInCache => {
if (options.timeout === 0) {
assistErrorCallback(options)({ status: -1 });
return;
}
$.ajax({
dataType: 'json',
url: url,
timeout: options.timeout,
success: data => {
if (
!data.error &&
!successResponseIsError(data) &&
typeof data.files !== 'undefined' &&
data.files !== null
) {
if (data.files.length > 2 && !options.filter) {
storeInCache(data);
}
options.successCallback(data);
} else {
assistErrorCallback(options)(data);
}
}
})
.fail(assistErrorCallback(options))
.always(() => {
if (typeof options.editor !== 'undefined' && options.editor !== null) {
options.editor.hideSpinner();
}
});
};
this.fetchCached(
$.extend({}, options, {
sourceType: 's3',
url: url,
fetchFunction: fetchFunction
})
);
}
async fetchFavoriteApp(options) {
return new Promise((resolve, reject) => {
simpleGet('/desktop/api2/user_preferences/default_app').done(resolve).fail(reject);
});
}
async setFavoriteAppAsync(options) {
return new Promise((resolve, reject) => {
simplePost('/desktop/api2/user_preferences/default_app', options).done(resolve).fail(reject);
});
}
/**
* @param {Object} options
* @param {String} options.collectionName
* @param {String} options.fieldName
* @param {String} options.prefix
* @param {String} [options.engine]
* @param {Function} options.successCallback
* @param {Function} [options.alwaysCallback]
* @param {Number} [options.timeout]
*
*/
fetchDashboardTerms(options) {
if (options.timeout === 0) {
assistErrorCallback(options)({ status: -1 });
return;
}
$.ajax({
dataType: 'json',
url: URLS.DASHBOARD_TERMS_API,
type: 'POST',
data: {
collection: ko.mapping.toJSON({
id: '',
name: options.collectionName,
engine: options.engine || 'solr'
}),
analysis: ko.mapping.toJSON({
name: options.fieldName,
terms: {
prefix: options.prefix || ''
}
})
},
timeout: options.timeout,
success: data => {
if (!data.error && !successResponseIsError(data) && data.status === 0) {
options.successCallback(data);
} else {
assistErrorCallback(options)(data);
}
}
})
.fail(assistErrorCallback(options))
.always(options.alwaysCallback);
}
/**
* @param {Object} options
* @param {String} options.collectionName
* @param {String} options.fieldName
* @param {String} [options.engine]
* @param {Function} options.successCallback
* @param {Function} [options.alwaysCallback]
* @param {Number} [options.timeout]
*
*/
fetchDashboardStats(options) {
if (options.timeout === 0) {
assistErrorCallback(options)({ status: -1 });
return;
}
$.ajax({
dataType: 'json',
url: URLS.DASHBOARD_STATS_API,
type: 'POST',
data: {
collection: ko.mapping.toJSON({
id: '',
name: options.collectionName,
engine: options.engine || 'solr'
}),
analysis: ko.mapping.toJSON({
name: options.fieldName,
stats: {
facet: ''
}
}),
query: ko.mapping.toJSON({
qs: [{ q: '' }],
fqs: []
})
},
timeout: options.timeout,
success: data => {
if (!data.error && !successResponseIsError(data) && data.status === 0) {
options.successCallback(data);
} else if (data.status === 1) {
options.notSupportedCallback(data);
} else {
assistErrorCallback(options)(data);
}
}
})
.fail(assistErrorCallback(options))
.always(options.alwaysCallback);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
* @param {Number} [options.timeout]
* @param {Object} [options.editor] - Ace editor
*/
fetchHBase(options) {
let suffix = 'getClusters';
if (options.parent.name !== '') {
suffix = 'getTableList/' + options.parent.name;
}
const url = URLS.HBASE_API_PREFIX + suffix;
const fetchFunction = storeInCache => {
if (options.timeout === 0) {
assistErrorCallback(options)({ status: -1 });
return;
}
$.ajax({
dataType: 'json',
url: url,
timeout: options.timeout,
success: data => {
if (!data.error && !successResponseIsError(data)) {
storeInCache(data);
options.successCallback(data);
} else {
assistErrorCallback(options)(data);
}
}
})
.fail(assistErrorCallback(options))
.always(() => {
if (typeof options.editor !== 'undefined' && options.editor !== null) {
options.editor.hideSpinner();
}
});
};
this.fetchCached(
$.extend({}, options, {
sourceType: 'hbase',
url: url,
fetchFunction: fetchFunction
})
);
}
/**
* @param {Object} options
* @param {Number} options.pastMs
* @param {Number} options.stepMs
*
* @return {Promise}
*/
fetchResourceStats(options) {
const queryMetric = metricName => {
const now = Date.now();
return simplePost('/metadata/api/prometheus/query', {
query: ko.mapping.toJSON(metricName),
start: Math.floor((now - options.pastMs) / 1000),
end: Math.floor(now / 1000),
step: options.stepMs / 1000
});
};
const combinedDeferred = $.Deferred();
$.when(
queryMetric('round((go_memstats_alloc_bytes / go_memstats_sys_bytes) * 100)'), // CPU percentage
queryMetric('round((go_memstats_alloc_bytes / go_memstats_sys_bytes) * 100)'), // Memory percentage
queryMetric('round((go_memstats_alloc_bytes / go_memstats_sys_bytes) * 100)'), // IO percentage
queryMetric('impala_queries_count{datawarehouse="' + options.clusterName + '"}'), // Sum of all queries in flight (currently total query executed for testing purpose)
queryMetric('impala_queries{datawarehouse="' + options.clusterName + '"}') // Queued queries
)
.done(() => {
const timestampIndex = {};
for (let j = 0; j < arguments.length; j++) {
const response = arguments[j];
if (response.data.result[0]) {
const values = response.data.result[0].values;
for (let i = 0; i < values.length; i++) {
if (!timestampIndex[values[i][0]]) {
timestampIndex[values[i][0]] = [values[i][0] * 1000, 0, 0, 0, 0, 0]; // Adjust back to milliseconds
}
timestampIndex[values[i][0]][j + 1] = parseFloat(values[i][1]);
}
}
}
const result = [];
Object.keys(timestampIndex).forEach(key => {
result.push(timestampIndex[key]);
});
result.sort((a, b) => {
return a[0] - b[0];
});
combinedDeferred.resolve(result);
})
.fail(combinedDeferred.reject);
return combinedDeferred.promise();
}
/**
* @param {Object} options
* @param {Function} [options.successCallback]
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*/
fetchConfigurations(options) {
simpleGet(URLS.CONFIG_APPS_API, {}, options);
}
saveGlobalConfiguration(options) {
simplePost(
URLS.CONFIG_APPS_API,
{
configuration: ko.mapping.toJSON(options.configuration)
},
options
);
}
/**
* @param {Object} options
* @param {Function} [options.successCallback]
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*
* @param {string} options.app
* @param {Object} options.properties
* @param {boolean} [options.isDefault]
* @param {Number} [options.groupId]
* @param {Number} [options.userId]
*/
saveConfiguration(options) {
simplePost(
URLS.CONFIG_SAVE_API,
{
app: options.app,
properties: ko.mapping.toJSON(options.properties),
is_default: options.isDefault,
group_id: options.groupId,
user_id: options.userId
},
options
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*
* @param {string} [options.uuid]
*/
fetchDocuments(options) {
let id = '';
if (options.uuid) {
id += options.uuid;
}
if (options.type && options.type !== 'all') {
id += options.type;
}
let promise = this.queueManager.getQueued(URLS.DOCUMENTS_API, id);
const firstInQueue = typeof promise === 'undefined';
if (firstInQueue) {
promise = $.Deferred();
this.queueManager.addToQueue(promise, URLS.DOCUMENTS_API, id);
}
promise.done(options.successCallback).fail(assistErrorCallback(options));
if (!firstInQueue) {
return;
}
const data = {
uuid: options.uuid
};
if (options.type && options.type !== 'all') {
data.type = ['directory', options.type];
}
$.ajax({
url: URLS.DOCUMENTS_API,
data: data,
traditional: true,
success: data => {
if (!successResponseIsError(data)) {
promise.resolve(data);
} else {
promise.reject(data);
}
}
}).fail(promise.reject);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*
* @param {string} [options.path]
* @param {string} [options.query]
* @param {string} [options.type]
* @param {int} [options.page]
* @param {int} [options.limit]
*/
searchDocuments(options) {
return $.ajax({
url: URLS.DOCUMENTS_SEARCH_API,
data: {
uuid: options.uuid,
text: options.query,
type: options.type,
page: options.page,
limit: options.limit,
include_trashed: options.include_trashed
},
success: data => {
if (!successResponseIsError(data)) {
options.successCallback(data);
} else {
assistErrorCallback(options)(data);
}
}
}).fail(assistErrorCallback(options));
}
/**
* @param {Object} options
* @param {number} options.uuid
* @param {boolean} [options.dependencies]
* @param {boolean} [options.silenceErrors]
* @param {boolean} [options.fetchContents]
*
* @return {CancellableJqPromise}
*/
fetchDocument(options) {
const deferred = $.Deferred();
const request = $.ajax({
url: URLS.DOCUMENTS_API,
data: {
uuid: options.uuid,
data: !!options.fetchContents,
dependencies: options.dependencies
},
success: data => {
if (!successResponseIsError(data)) {
deferred.resolve(data);
} else {
deferred.reject(
assistErrorCallback({
silenceErrors: options.silenceErrors
})
);
}
}
}).fail(
assistErrorCallback({
silenceErrors: options.silenceErrors,
errorCallback: deferred.reject
})
);
return new CancellableJqPromise(deferred, request);
}
/**
* @param {Object} options
* @param {string} options.uuid
* @param {boolean} [options.silenceErrors]
* @param {boolean} [options.dependencies]
* @param {boolean} [options.fetchContents]
*
* @param options
* @return {Promise<unknown>}
*/
async fetchDocumentAsync(options) {
return new Promise((resolve, reject) => {
this.fetchDocument(options).done(resolve).fail(reject);
});
}
/**
* @param {Object} options
* @param {string} options.uuid
* @param {string} options.perm - See LINK_SHARING_PERMS
* @param {boolean} [options.silenceErrors]
*
* @param options
* @return {Promise<unknown>}
*/
async setLinkSharingPermsAsync(options) {
return new Promise((resolve, reject) => {
simplePost('/desktop/api2/doc/share/link', {
uuid: JSON.stringify(options.uuid),
perm: JSON.stringify(options.perm)
})
.done(resolve)
.fail(reject);
});
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*
* @param {string} options.parentUuid
* @param {string} options.name
*/
createDocumentsFolder(options) {
simplePost(
URLS.DOCUMENTS_API + 'mkdir',
{
parent_uuid: ko.mapping.toJSON(options.parentUuid),
name: ko.mapping.toJSON(options.name)
},
options
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*
* @param {string} options.uuid
* @param {string} options.name
*/
updateDocument(options) {
simplePost(
URLS.DOCUMENTS_API + 'update',
{
uuid: ko.mapping.toJSON(options.uuid),
name: options.name
},
options
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {Function} [options.progressHandler]
* @param {boolean} [options.silenceErrors]
*
* @param {FormData} options.formData
*/
uploadDocument(options) {
$.ajax({
url: URLS.DOCUMENTS_API + 'import',
type: 'POST',
success: data => {
if (!successResponseIsError(data)) {
options.successCallback(data);
} else {
assistErrorCallback(options)(data);
}
},
xhr: () => {
const myXhr = $.ajaxSettings.xhr();
if (myXhr.upload && options.progressHandler) {
myXhr.upload.addEventListener('progress', options.progressHandler, false);
}
return myXhr;
},
dataType: 'json',
data: options.formData,
cache: false,
contentType: false,
processData: false
}).fail(assistErrorCallback(options));
}
/**
*
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*
* @param {number} options.sourceId - The ID of the source document
* @param {number} options.destinationId - The ID of the target document
*/
moveDocument(options) {
simplePost(
URLS.DOCUMENTS_API + 'move',
{
source_doc_uuid: ko.mapping.toJSON(options.sourceId),
destination_doc_uuid: ko.mapping.toJSON(options.destinationId)
},
options
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*
* @param {string} options.uuid
* @param {string} [options.skipTrash] - Default false
*/
deleteDocument(options) {
simplePost(
URLS.DOCUMENTS_API + 'delete',
{
uuid: ko.mapping.toJSON(options.uuid),
skip_trash: ko.mapping.toJSON(options.skipTrash || false)
},
options
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*
* @param {string} options.uuid
*/
copyDocument(options) {
simplePost(
URLS.DOCUMENTS_API + 'copy',
{
uuid: ko.mapping.toJSON(options.uuid)
},
options
);
}
/**
* @param {Object} options
* @param {Function} options.successCallback
* @param {Function} [options.errorCallback]
* @param {boolean} [options.silenceErrors]
*
* @param {string} options.uuid
*/
restoreDocument(options) {
simplePost(
URLS.DOCUMENTS_API + 'restore',
{
uuids: ko.mapping.toJSON(options.uuids)
},
options
);
}
/**
*
* @param {Object} options
* @param {string} options.sourceType
* @param {string} [options.databaseName]
* @param {string} [options.tableName]
* @param {string} [options.cacheType] - Possible values 'default', 'sqlAnalyzer'. Default value 'default'
* @param {string[]} [options.fields]
* @param {boolean} [options.clearAll]
*/
clearDbCache(options) {
const cacheIdentifier = this.getAssistCacheIdentifier(options);
if (options.clearAll) {
setInLocalStorage(cacheIdentifier, {});
} else {
let url = URLS.AUTOCOMPLETE_API_PREFIX;
if (options.databaseName) {
url += options.databaseName + '/';
}
if (options.tableName) {
url += options.tableName + '/';
}
if (options.fields) {
url += options.fields.length > 0 ? '/' + options.fields.join('/') : '';
}
const cachedData = getFromLocalStorage(cacheIdentifier) || {};
delete cachedData[url];
getFromLocalStorage(cacheIdentifier, cachedData);
}
}
/**
* @param {Object} options
* @param {string} options.sourceType
* @param {string} options.invalidate - 'invalidate' or 'invalidateAndFlush'
* @param {string[]} [options.path]
* @param {ContextCompute} [options.compute]
* @param {boolean} [options.silenceErrors]
*/
invalidateSourceMetadata(options) {
const deferred = $.Deferred();
if (
options.sourceType === 'impala' &&
(options.invalidate === 'invalidate' || options.invalidate === 'invalidateAndFlush')
) {
const data = {
flush_all: options.invalidate === 'invalidateAndFlush',
cluster: JSON.stringify(options.compute)
};
if (options.path && options.path.length > 0) {
data.database = options.path[0];
}
if (options.path && options.path.length > 1) {
data.table = options.path[1];
}
const request = simplePost(URLS.IMPALA_INVALIDATE_API, data, options)
.done(deferred.resolve)
.fail(deferred.reject);
return new CancellableJqPromise(deferred, request);
}
return deferred.resolve().promise();
}
/**
* Fetches the partitions for the given path
*
* @param {Object} options
* @param {boolean} [options.silenceErrors]
*
* @param {string[]} options.path
* @param {ContextCompute} options.compute
*
* @return {CancellableJqPromise}
*/
fetchPartitions(options) {
const deferred = $.Deferred();
// TODO: No sourceType needed?
const request = $.post('/metastore/table/' + options.path.join('/') + '/partitions', {
format: 'json',
cluster: JSON.stringify(options.compute)
})
.done(response => {
if (!successResponseIsError(response)) {
if (!response) {
response = {};
}
response.hueTimestamp = Date.now();
deferred.resolve(response);
} else {
assistErrorCallback({
silenceErrors: options.silenceErrors,
errorCallback: deferred.reject
})(response);
}
})
.fail(response => {
// Don't report any partitions if it's not partitioned instead of error to prevent unnecessary calls
if (
response &&
response.responseText &&
response.responseText.indexOf('is not partitioned') !== -1
) {
deferred.resolve({
hueTimestamp: Date.now(),
partition_keys_json: [],
partition_values_json: []
});
} else {
assistErrorCallback({
silenceErrors: options.silenceErrors,
errorCallback: deferred.reject
})(response);
}
});
return new CancellableJqPromise(deferred, request);
}
clearNotebookHistory(options) {
const data = {
notebook: options.notebookJson,
doc_type: options.docType,
is_notification_manager: options.isNotificationManager
};
return simplePost('/notebook/api/clear_history', data);
}
closeNotebook(options) {
const data = {
notebook: options.notebookJson,
editorMode: options.editorMode
};
return simplePost('/notebook/api/notebook/close', data);
}
async checkStatus(options) {
return new Promise((resolve, reject) => {
const data = {
notebook: options.notebookJson
};
$.post({
url: '/notebook/api/check_status',
data: data
})
.done(data => {
// 0, -3 and other negative values have meaning for this endpoint
if (data && typeof data.status !== 'undefined') {
resolve(data);
} else if (successResponseIsError(data)) {
reject(assistErrorCallback(options)(data));
} else {
reject();
}
})
.fail(assistErrorCallback(options));
});
}
getExternalStatement(options) {
const data = {
notebook: options.notebookJson,
snippet: options.snippetJson
};
return simplePost('/notebook/api/get_external_statement', data);
}
fetchResultSize(options) {
const data = {
notebook: options.notebookJson,
snippet: options.snippetJson
};
return simplePost('/notebook/api/fetch_result_size', data);
}
getLogs(options) {
const data = {
notebook: options.notebookJson,
snippet: options.snippetJson,
from: options.from,
jobs: options.jobsJson,
full_log: options.fullLog,
operationId: options.executable.operationId
};
return simplePost('/notebook/api/get_logs', data);
}
async saveNotebook(options) {
const data = {
notebook: options.notebookJson,
editorMode: options.editorMode
};
return new Promise((resolve, reject) => {
simplePost('/notebook/api/notebook/save', data).then(resolve).catch(reject);
});
}
async getHistory(options) {
return new Promise((resolve, reject) => {
$.get('/api/v1/editor/get_history', {
doc_type: options.type,
limit: options.limit || 50,
page: options.page || 1,
doc_text: options.docFilter,
is_notification_manager: options.isNotificationManager
})
.done(data => {
if (successResponseIsError(data)) {
reject(assistErrorCallback(options)(data));
return;
}
resolve(data);
})
.fail(reject);
});
}
/**
*
* @param {Object} options
* @param {Snippet} options.snippet
*
* @return {CancellableJqPromise<string>}
*/
async explainAsync(options) {
const data = {
notebook: await options.snippet.parentNotebook.toContextJson(),
snippet: options.snippet.toContextJson()
};
return new Promise((resolve, reject) => {
simplePost('/notebook/api/explain', data, options)
.done(response => {
resolve(response.explanation);
})
.fail(reject);
});
}
/**
*
* @param {Object} options
* @param {statement} options.statement
* @param {doc_type} options.doc_type
* @param {name} options.name
* @param {description} options.description
*
* @return {CancellableJqPromise<string>}
*/
async createGistAsync(options) {
const data = {
statement: options.statement,
doc_type: options.doc_type,
name: options.name,
description: options.description
};
return new Promise((resolve, reject) => {
simplePost(URLS.GIST_API + 'create', data, options)
.done(response => {
resolve(response.link);
})
.fail(reject);
});
}
/**
*
* @param {Object} options
* @param {channel} options.channel
* @param {message} options.message
*
* @return {Promise<void>}
*/
async sendSlackMessageAsync(options) {
const data = {
channel: options.channel,
message: options.message
};
return new Promise((resolve, reject) => {
simplePost(URLS.SEND_SLACK_MESSAGE, data, options).done(resolve).fail(reject);
});
}
/**
*
* @param {Object} options
*
* @return {Promise<Object>}
*/
async getSlackChannelsAsync(options) {
return new Promise((resolve, reject) => {
simpleGet(URLS.GET_SLACK_CHANNELS, {}, options)
.done(response => {
resolve(response.channels);
})
.fail(reject);
});
}
/**
* @param {Object} options
* @param {boolean} [options.silenceErrors]
* @param {ContextCompute} options.compute
* @param {string} options.queryId
* @return {CancellableJqPromise}
*/
fetchQueryExecutionAnalysis(options) {
//var url = '/metadata/api/workload_analytics/get_impala_query/';
const url = '/impala/api/query/alanize';
const deferred = $.Deferred();
let tries = 0;
const cancellablePromises = [];
const promise = new CancellableJqPromise(deferred, undefined, cancellablePromises);
const pollForAnalysis = () => {
if (tries === 10) {
deferred.reject();
return;
}
tries++;
cancellablePromises.pop(); // Remove the last one
cancellablePromises.push(
deferred,
simplePost(
url,
{
cluster: JSON.stringify(options.compute),
query_id: '"' + options.queryId + '"'
},
options
)
.done(response => {
if (response && response.data) {
deferred.resolve(response.data);
} else {
const timeout = window.setTimeout(() => {
pollForAnalysis();
}, 1000 + tries * 500); // TODO: Adjust once fully implemented;
promise.onCancel(() => {
window.clearTimeout(timeout);
});
}
})
.fail(deferred.reject)
);
};
pollForAnalysis();
return promise;
}
fixQueryExecutionAnalysis(options) {
const url = '/impala/api/query/alanize/fix';
const deferred = $.Deferred();
const request = simplePost(
url,
{
cluster: JSON.stringify(options.compute),
fix: JSON.stringify(options.fix),
start_time: options.start_time
},
{
silenceErrors: options.silenceErrors,
successCallback: response => {
if (response.status === 0) {
deferred.resolve(response.details);
} else {
deferred.reject();
}
},
errorCallback: deferred.reject
}
);
return new CancellableJqPromise(deferred, request);
}
fetchQueryExecutionStatistics(options) {
const url = '/impala/api/query/alanize/metrics';
const deferred = $.Deferred();
const request = simplePost(
url,
{
cluster: JSON.stringify(options.cluster),
query_id: '"' + options.queryId + '"'
},
{
silenceErrors: options.silenceErrors,
successCallback: response => {
if (response.status === 0) {
deferred.resolve(response.data);
} else {
deferred.reject();
}
},
errorCallback: deferred.reject
}
);
return new CancellableJqPromise(deferred, request);
}
async fetchHueConfigAsync(options) {
return new Promise((resolve, reject) => {
$.get(URLS.GET_HUE_CONFIG_API)
.done(response => {
if (!response && response.status === -1) {
reject(response.message);
} else {
resolve(response);
}
})
.fail(reject);
});
}
fetchHueDocsInteractive(query) {
const deferred = $.Deferred();
const request = $.post(URLS.INTERACTIVE_SEARCH_API, {
query_s: ko.mapping.toJSON(query),
limit: 50,
sources: '["documents"]'
})
.done(data => {
if (data.status === 0) {
deferred.resolve(data);
} else {
deferred.reject(data);
}
})
.fail(deferred.reject);
return new CancellableJqPromise(deferred, request);
}
fetchNavEntitiesInteractive(options) {
const deferred = $.Deferred();
const request = $.post(URLS.INTERACTIVE_SEARCH_API, {
query_s: ko.mapping.toJSON(options.query),
field_facets: ko.mapping.toJSON(options.facets || []),
limit: 50,
sources: '["sql", "hdfs", "s3"]'
})
.done(data => {
if (data.status === 0) {
deferred.resolve(data);
} else {
deferred.reject(data);
}
})
.fail(deferred.reject);
return new CancellableJqPromise(deferred, request);
}
/**
*
* @param {Object} options
* @param {string} options.statements
* @param {boolean} [options.silenceErrors]
*/
formatSql(options) {
const deferred = $.Deferred();
const request = simplePost(
URLS.FORMAT_SQL_API,
{
statements: options.statements
},
{
silenceErrors: options.silenceErrors,
successCallback: deferred.resolve,
errorCallback: deferred.reject
}
);
return new CancellableJqPromise(deferred, request);
}
/**
*
* @param {Object} options
* @param {string} options.statement
* @param {string} options.doc_type
* @param {string} options.name
* @param {string} options.description
* @param {boolean} [options.silenceErrors]
*/
createGist(options) {
const deferred = $.Deferred();
const request = simplePost(
URLS.GIST_API + 'create',
{
statement: options.statement,
doc_type: options.doc_type,
name: options.name,
description: options.description
},
{
silenceErrors: options.silenceErrors,
successCallback: deferred.resolve,
errorCallback: deferred.reject
}
);
return new CancellableJqPromise(deferred, request);
}
}
const apiHelper = new ApiHelper();
export default apiHelper;