@sap/hdbext
Version:
Hana-client extension library and utility functions for using SAP HANA in node.js
190 lines (162 loc) • 6.63 kB
JavaScript
;
var assert = require('assert');
var async = require('async');
var _ = require('lodash');
var hdb = require('@sap/hana-client');
var hdbPromise = require('@sap/hana-client/extension/Promise.js');
var safeSql = require('./safe-sql');
var debug = require('debug')('hdbext:client-factory');
module.exports.createConnection = createConnection;
module.exports.createConnectionPromise = createConnectionPromise;
function createConnection(options, callback) {
options = normalizeOptions(options);
let serverNode = getServerNode(options);
debug(`createConnection() start for createConnection ${serverNode}`);
var client = hdb.createConnection(options);
debug(`HANA client created for createConnection ${serverNode}`);
async.series([
function (cb) {
debug(`Connect to HANA ${serverNode}`);
client.connect(function(err){
if (err) {
debug(`Failed to connect to HANA ${serverNode}:`, err);
return cb(err);
}
debug(`Successfully connected to HANA ${serverNode}`);
if (Object.prototype.hasOwnProperty.call(options, 'autoCommit')) {
debug(`Set AutoCommit to ${options.autoCommit}`);
try {
client.setAutoCommit(options.autoCommit);
debug(`AutoCommit applied for ${serverNode}`);
} catch (err2) {
debug(`Failed to set AutoCommit for ${serverNode}:`, err2);
return cb(err2);
}
} else {
debug(`No AutoCommit option provided for ${serverNode}`);
}
debug(`Connection setup step completed for createConnection ${serverNode}`);
cb(null);
});
},
function (cb) {
var xsAppUser = options['sessionVariable:XS_APPLICATIONUSER'];
if (!xsAppUser) {
debug(`No XS_APPLICATIONUSER provided for createConnection ${serverNode}; skipping session variable setup`);
return cb(null);
}
client.exec("SET 'XS_APPLICATIONUSER' = " + safeSql.stringLiteral(xsAppUser), function (err) {
if (err) {
debug(`Failed to set XS_APPLICATIONUSER for ${serverNode} createConnection:`, err);
return cb(err);
}
debug(`XS_APPLICATIONUSER session variable set for ${serverNode} createConnection`);
cb(null);
});
}
], function (err) {
if (err) {
debug('Error in createConnection', err);
debug(`Disconnecting client for ${serverNode} because createConnection failed`);
client.disconnect(function (e) {
if (e) { debug('Error while closing connection.', e); }
else { debug(`Client disconnected for ${serverNode}`); }
});
debug(`createConnection() returning error for ${serverNode}`);
return callback(err);
}
debug(`createConnection() succeeded for ${serverNode}`);
callback(null, client);
});
}
async function createConnectionPromise(options) {
options = normalizeOptions(options);
let serverNode = getServerNode(options);
var conn = hdb.createConnection();
debug(`Connect to HANA ${serverNode}`);
try {
await hdbPromise.connect(conn, options);
debug(`Successfully connected to HANA ${serverNode}`);
} catch (err) {
debug(`Failed to connect to HANA ${serverNode}:`, err);
throw err;
}
if (Object.prototype.hasOwnProperty.call(options, 'autoCommit')) {
debug(`Set AutoCommit to ${options.autoCommit}`);
try {
conn.setAutoCommit(options.autoCommit);
} catch (err) {
debug(`Failed to set AutoCommit for ${serverNode}:`, err);
tryCloseConnection(conn);
throw err;
}
}
var xsAppUser = options['sessionVariable:XS_APPLICATIONUSER'];
if (xsAppUser) {
try {
await hdbPromise.exec(conn, "SET 'XS_APPLICATIONUSER' = " + safeSql.stringLiteral(xsAppUser));
debug('Successfully set XS_APPLICATIONUSER session variable');
} catch (err) {
debug('Failed to set XS_APPLICATIONUSER:', err);
tryCloseConnection(conn);
throw err;
}
}
return conn;
}
async function tryCloseConnection(conn) {
try {
await hdbPromise.close(conn);
} catch (err) {
debug('Failed to close connection:', err);
}
}
function getServerNode(hanaOptions){
if (hanaOptions.serverNode){
return hanaOptions.serverNode;
}
return `${hanaOptions.host}:${hanaOptions.port}`;
}
function normalizeOptions(options) {
assert(_.isObject(options), 'options should be an object');
options = _.clone(options);
assert(!Object.prototype.hasOwnProperty.call(options, 'autoCommit') || _.isBoolean(options.autoCommit), 'options.autoCommit should be a boolean');
if (options.schema) {
options.currentSchema = safeSql.identifier(options.schema);
delete options.schema;
}
if (options.locale) {
options.locale = options.locale.replace(/-/g, '_');
}
if (!options.hosts && options.db_hosts) {
assert(Array.isArray(options.db_hosts), 'options.db_hosts should be an array');
options.hosts = options.db_hosts;
delete options.db_hosts;
}
if (!options.ca && options.certificate) {
options.ca = [options.certificate];
delete options.certificate;
}
if (!Object.prototype.hasOwnProperty.call(options, 'sslValidateCertificate') && Object.prototype.hasOwnProperty.call(options, 'validate_certificate')) {
assert(_.isBoolean(options.validate_certificate), 'options.validate_certificate should be a boolean');
options.sslValidateCertificate = options.validate_certificate;
delete options.validate_certificate;
}
assert(!Object.prototype.hasOwnProperty.call(options, 'encrypt') || _.isBoolean(options.encrypt), 'options.encrypt should be a boolean');
if (!options.sslHostNameInCertificate && options.hostname_in_certificate) {
assert(_.isString(options.hostname_in_certificate), 'options.hostname_in_certificate should be a string');
options.sslHostNameInCertificate = options.hostname_in_certificate;
delete options.hostname_in_certificate;
}
if (!options.cert && options.client_authentication_certificate) {
assert(_.isString(options.client_authentication_certificate), 'options.client_authentication_certificate should be a string');
options.cert = options.client_authentication_certificate;
delete options.client_authentication_certificate;
}
if (!options.key && options.client_authentication_private_key) {
assert(_.isString(options.client_authentication_private_key), 'options.client_authentication_private_key should be a string');
options.key = options.client_authentication_private_key;
delete options.client_authentication_private_key;
}
return options;
}