blob: 9d1cadcb8a4df03b43e9206046f989340be95016 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF 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.
*/
var validators = require('utils/validator');
var stringUtils = require('utils/string_utils');
/**
* @typedef {parsedJDBCUrl}
* @type {object}
* @property {string} dbType alias name by type of database @see utils/configs/database.DB_UI_TYPE_ALIAS
* @property {string} location parsed host name
*/
/**
* Helper methods to process database values and properties
* @module utils/configs/database
*/
module.exports = {
/**
* Database type properties with <code>options</code> attribute usually displayed as radio-button.
* <code>options</code> attribute contains mapping of database type to special properties name such as
* database host and database name.
* @type {object}
*/
dbServicePropertyMap: {
HIVE: {
dbType: 'hive_database',
databaseName: 'ambari.hive.db.schema.name',
connectionUrl: 'javax.jdo.option.ConnectionURL'
},
OOZIE: {
dbType: 'oozie_database',
connectionUrl: 'oozie.service.JPAService.jdbc.url',
databaseName: 'oozie.db.schema.name'
},
RANGER: {
dbType: 'DB_FLAVOR',
connectionUrl: 'ranger.jpa.jdbc.url',
databaseName: 'db_name',
fallbackHostName: 'db_host'
}
},
/**
* Map of jdbc url patterns related to supported database types.
*
* @type {object}
*/
DB_JDBC_PATTERNS: {
mysql: 'jdbc:mysql://{0}/{1}',
mssql: 'jdbc:sqlserver://{0};databaseName={1}',
postgres: 'jdbc:postgresql://{0}:5432/{1}',
derby: 'jdbc:derby:{0}/{1}',
oracle: 'jdbc:oracle:thin:@(?:\/?\/?){0}:1521(\:|\/){1}',
sqla: 'jdbc:sqlanywhere:host={0};database={1}'
},
DB_UI_TYPE_ALIAS: {
mysql: 'mysql',
sqlserver: 'mssql',
postgresql: 'postgres',
derby: 'derby',
oracle: 'oracle',
sqlanywhere: 'sqla'
},
/**
* Setup database related properties.
*
* @method bootstrapDatabaseProperties
* @param {App.ServiceConfigProperty[]} serviceConfigs
* @param {string} [serviceName=false]
*/
bootstrapDatabaseProperties: function(serviceConfigs, serviceName) {
var self = this;
var supportedServices = Em.keys(this.dbServicePropertyMap);
if (serviceName && !supportedServices.contains(serviceName)) return;
var serviceNames = serviceName ? [serviceName] : serviceConfigs.mapProperty('serviceName').uniq();
serviceNames.forEach(function(serviceName) {
if (!supportedServices.contains(serviceName)) return;
var configs = serviceConfigs.filterProperty('serviceName', serviceName) || [];
var connectionConfigs = self.dbServicePropertyMap[serviceName];
var databaseTypeProperty = configs.findProperty('name', connectionConfigs.dbType);
if (!databaseTypeProperty) return;
var databaseTypePropertyIndex = configs.indexOf(databaseTypeProperty);
var generatedProperties = self.getPropsByOptions(databaseTypeProperty, configs);
var jdbcObject = self.parseJdbcUrl(Em.get(configs.findProperty('name', connectionConfigs.connectionUrl), 'value'));
generatedProperties.forEach(function(property) {
if (Em.get(property, 'name').endsWith('_host')) {
// set UI host names for each database type with value parsed from jdbc connection url
// if value is not ip or hostname (in case of New Derby Database) for Oozie set <code>fallbackUrl</code>
// from <code>dbServicePropertyMap</code>
Em.setProperties(property, {
value: jdbcObject.location || ''
});
self.addPropertyToServiceConfigs(property, serviceConfigs, databaseTypePropertyIndex);
}
});
});
},
/**
* Add UI specific property to serviceConfigObject if it does not exist, and update value for existed property.
* This code affects properties related to `_host` and `_database` which are hardcoded on UI.
*
* @param {object} property - property to append/update
* @param {App.ServiceConfigProperty[]} configs - loaded and processed service configs
* @param {integer} index of first occurrence of database type in config
*/
addPropertyToServiceConfigs: function(property, configs, index) {
var configProperty = configs.findProperty('name', Em.get(property, 'name'));
if (configProperty) {
Em.set(configProperty, 'value', Em.get(property, 'value'));
} else {
if (index) {
configs.insertAt(index, App.ServiceConfigProperty.create(property));
} else {
configs.pushObject(App.ServiceConfigProperty.create(property));
}
}
},
/**
* Get hardcoded properties from site_properties.js which UI use to display host name and database name.
*
* @method getPropsByOptions
* @param {object} databaseTypeProperty - hardcoded property from site_properties.js usualy used as radiobutton
* @param {App.ServiceConfigProperty[]} configs - loaded and processed configs
* @returns {object[]} - hardcoded properties from site_properties.js related to database name and location
*/
getPropsByOptions: function(databaseTypeProperty) {
Em.assert('Property related to database type should contains `options` attribute', databaseTypeProperty.get('options'));
return databaseTypeProperty.options.mapProperty('foreignKeys').reduce(function(p,c) {
return p.concat(c);
}).uniq().map(function(name) {
return App.config.get('preDefinedSiteProperties').findProperty('name', name) || null;
}).compact();
},
/**
* Get database location from jdbc url value
*
* @method getDBLocationFromJDBC
* @param {string} jdbcUrl - url to parse
* @returns {string|null}
*/
getDBLocationFromJDBC: function(jdbcUrl) {
var dbProvider = this.getJDBCProviderName(jdbcUrl),
protocol = this._makeProtocol(dbProvider),
pattern = /^\/\//,
url;
if (!this.isSupportedProvider(dbProvider)) {
return '';
}
if (dbProvider === 'derby') {
return this.getDerbyPath(jdbcUrl);
}
url = "http://" + jdbcUrl.replace(protocol, '').replace(pattern, '');
if (dbProvider === 'sqlserver') {
url = url.split(';')[0];
}
if (dbProvider === 'oracle') {
var matches = jdbcUrl.replace(protocol, '').match(/@(?:\/?\/?)(.+)/);
if (matches.length) {
var result = Em.getWithDefault(matches, '1', '').split(':')[0];
return result === '{0}' ? '' : result;
}
return '';
}
if (dbProvider === 'sqlanywhere') {
url = url.split(';').map(function(i) {
return /host=/.test(i) ? i.replace('host=', '').replace('http://', '') : null;
}).compact()[0];
return this.getHostNameByUrl('http://' + url);
}
url = url.split(':').slice(0, 2).join(':');
return this.getHostNameByUrl(url);
},
/**
* Return derby database path by jdbcUrl
*
* @param {string} jdbcUrl
* @return {string} database path
*/
getDerbyPath: function(jdbcUrl) {
var matches = jdbcUrl.match(new RegExp(this.DB_JDBC_PATTERNS['derby'].format('(.*)', '(.*)')));
if (matches.length) {
var dbLocation = Em.getWithDefault(matches, '1', '');
if (dbLocation.startsWith('${')) {
return Em.getWithDefault(matches, '0', '').match(/\${[^}]+}/)[0];
}
return dbLocation != '{0}' ? dbLocation : null;
} else {
return null;
}
},
/**
* Returns host name by url input
*
* @param {string} url
* @returns {string} host name
*/
getHostNameByUrl: function(url) {
var link = document.createElement('a');
link.href = url;
var hostName = link.hostname;
link = null;
return hostName;
},
_makeProtocol: function(dbProvider) {
var protocol = 'jdbc:' + dbProvider + ':';
if (dbProvider === 'oracle') {
return protocol + 'thin:';
}
return protocol;
},
/**
* Returns provider name from jdbcUrl
*
* @param {string} jdbcUrl
* @returns {string} provider name e.g. `jdbc:some_provider:another-opt//additional` -> `some_provider`
*/
getJDBCProviderName: function(jdbcUrl) {
return jdbcUrl.split(':')[1];
},
/**
* Returns true when provider supported by UI.
*
* @returns {boolean}
*/
isSupportedProvider: function(dbProvider) {
return !!(this.DB_UI_TYPE_ALIAS[dbProvider]);
},
/**
* Determines alias value (DB_UI_TYPE_ALIAS attribute value) by provider name
*
* @param {string} dbProvider provider name parsed from jdbcUrl e,g. from `jdbc:mysql://` -> `mysql`
* @returns {string} alias value used on UI.
*/
getJDBCAlias: function(dbProvider) {
return this.DB_UI_TYPE_ALIAS[dbProvider];
},
/**
* Returns parsed info from jdbcUrl connection string
*
* @param {string} jdbcUrl
* @returns {parsedJDBCUrl}
*/
parseJdbcUrl: function(jdbcUrl) {
var result = {
dbType: null,
location: null
};
if (jdbcUrl === '') {
return result;
}
result.dbType = this.getJDBCAlias(this.getJDBCProviderName(jdbcUrl)) || null;
result.location = this.getDBLocationFromJDBC(jdbcUrl);
return result;
}
};