snowflake-sdk
Version:
Node.js driver for Snowflake
692 lines • 24.6 kB
JavaScript
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.url = exports.string = exports.number = exports.userAgent = exports.driverVersion = exports.driverName = void 0;
exports.inherits = inherits;
exports.format = format;
exports.isFunction = isFunction;
exports.isObject = isObject;
exports.isDate = isDate;
exports.isArray = isArray;
exports.isString = isString;
exports.isBoolean = isBoolean;
exports.isNumber = isNumber;
exports.isPrivateKey = isPrivateKey;
exports.exists = exists;
exports.apply = apply;
exports.isBrowser = isBrowser;
exports.isNode = isNode;
exports.nextSleepTime = nextSleepTime;
exports.getJitteredSleepTime = getJitteredSleepTime;
exports.chooseRandom = chooseRandom;
exports.getNextSleepTime = getNextSleepTime;
exports.getJitter = getJitter;
exports.isLoginRequest = isLoginRequest;
exports.isRetryableHttpError = isRetryableHttpError;
exports.validateClientSessionKeepAliveHeartbeatFrequency = validateClientSessionKeepAliveHeartbeatFrequency;
exports.constructHostname = constructHostname;
exports.isPrivateLink = isPrivateLink;
exports.createOcspResponseCacheServerUrl = createOcspResponseCacheServerUrl;
exports.isPutCommand = isPutCommand;
exports.isGetCommand = isGetCommand;
exports.convertSmkIdToString = convertSmkIdToString;
exports.getCircularReplacer = getCircularReplacer;
exports.isCorrectSubdomain = isCorrectSubdomain;
exports.buildCredentialCacheKey = buildCredentialCacheKey;
exports.checkValidCustomCredentialManager = checkValidCustomCredentialManager;
exports.checkParametersDefined = checkParametersDefined;
exports.isFileModeCorrect = isFileModeCorrect;
exports.isFileNotWritableByGroupOrOthers = isFileNotWritableByGroupOrOthers;
exports.shouldRetryOktaAuth = shouldRetryOktaAuth;
exports.getDriverDirectory = getDriverDirectory;
exports.validatePath = validatePath;
exports.getEnvVar = getEnvVar;
exports.validateEmptyString = validateEmptyString;
exports.isNotEmptyAsString = isNotEmptyAsString;
exports.isNotEmptyString = isNotEmptyString;
exports.isEmptyObject = isEmptyObject;
exports.isWindows = isWindows;
exports.getFreePort = getFreePort;
exports.isPortOpen = isPortOpen;
exports.lstrip = lstrip;
exports.escapeHTML = escapeHTML;
exports.dynamicImportESMInTypescriptWithCommonJS = dynamicImportESMInTypescriptWithCommonJS;
const util_1 = __importDefault(require("util"));
const url_1 = __importDefault(require("url"));
const os_1 = __importDefault(require("os"));
const Logger = __importStar(require("./logger"));
const fs_1 = __importDefault(require("fs"));
// NOTE: keeping require as it's a circular dependency so * as Errors doesn't work
const Errors = require('./errors');
const net_1 = __importDefault(require("net"));
const package_json_1 = require("../package.json");
Object.defineProperty(exports, "driverName", { enumerable: true, get: function () { return package_json_1.name; } });
Object.defineProperty(exports, "driverVersion", { enumerable: true, get: function () { return package_json_1.version; } });
const nodeJSVersion = process.version?.startsWith('v')
? process.version.substring(1)
: process.version;
exports.userAgent = `JavaScript/${package_json_1.version} (${process.platform}-${process.arch}) NodeJS/${nodeJSVersion}`;
/**
* Note: A simple wrapper around util.inherits() for now, but this might change
* in the future.
*
* Inherits the prototype methods from one constructor into another. The
* prototype of constructor will be set to a new object created from
* superConstructor.
*
* @param constructor
* @param superConstructor
*
* @returns {Object}
*/
function inherits(constructor, superConstructor) {
return util_1.default.inherits.apply(util_1.default, [constructor, superConstructor]);
}
/**
* Note: A simple wrapper around util.format() for now, but this will likely
* change in the future.
*
* Returns a formatted string using the first argument as a printf-like format.
*
* The first argument is a string that contains zero or more placeholders.
* Each placeholder is replaced with the converted value from its corresponding
* argument. Supported placeholders are:
* %s - String.
* %d - Number (both integer and float).
* %j - JSON. Replaced with the string '[Circular]' if the argument contains
* circular references.
* %% - single percent sign ('%'). This does not consume an argument.
*
* If the placeholder does not have a corresponding argument, the placeholder is
* not replaced.
*
* If there are more arguments than placeholders, the extra arguments are
* coerced to strings (for objects and symbols, util.inspect() is used) and then
* concatenated, delimited by a space.
*
* If the first argument is not a format string then util.format() returns a
* string that is the concatenation of all its arguments separated by spaces.
* Each argument is converted to a string with util.inspect().
*/
function format(format, ...params) {
return util_1.default.format.apply(util_1.default, [format, ...params]);
}
/**
* Determines if a given value is a function.
*/
function isFunction(value) {
return !!value && typeof value === 'function';
}
const toString = Object.prototype.toString;
/**
* Determines if a given value is an object.
*/
function isObject(value) {
return toString.call(value) === '[object Object]';
}
/**
* Determines if a given value is a Date.
*/
function isDate(value) {
return toString.call(value) === '[object Date]';
}
/**
* Determines if a given value is an array.
*/
function isArray(value) {
return toString.call(value) === '[object Array]';
}
/**
* Determines if a given value is a string.
*/
function isString(value) {
return typeof value === 'string';
}
/**
* Determines if a given value is a boolean.
*/
function isBoolean(value) {
return typeof value === 'boolean';
}
/**
* Determines if a given value is a number.
*/
function isNumber(value) {
return typeof value === 'number' && isFinite(value);
}
/**
* Determines if a given value is a private key string in pem format of type pkcs8.
*/
function isPrivateKey(value) {
const trimmedValue = value.trim();
// The private key is expected to be decrypted when set in the connection string
// secret scanner complains about first check since it looks like private key, but it's only check
// pragma: allowlist nextline secret
return (trimmedValue.startsWith('-----BEGIN PRIVATE KEY-----') &&
trimmedValue.endsWith('\n-----END PRIVATE KEY-----'));
}
/**
* A collection of number-related utility functions.
*/
exports.number = {
/**
* Determines if a given value is a positive number.
*/
isPositive: function (value) {
return isNumber(value) && value > 0;
},
/**
* Determines if a given value is a non-negative number.
*/
isNonNegative: function (value) {
return isNumber(value) && value >= 0;
},
/**
* Determines if a given value is an integer.
*/
isInteger: function (value) {
return isNumber(value) && Math.floor(value) === value;
},
/**
* Determines if a given value is a positive integer.
*/
isPositiveInteger: function (value) {
return this.isInteger(value) && value > 0;
},
/**
* Determines if a given value is a non-negative integer.
*/
isNonNegativeInteger: function (value) {
return this.isInteger(value) && value >= 0;
},
};
/**
* A collection of string-related utility functions.
*/
exports.string = {
/**
* Determines if a given string is not null or empty.
*/
isNotNullOrEmpty: function (value) {
return isString(value) && value;
},
/**
* Compares two version numbers of the form 'a.b.c' where a, b and c are
* numbers (e.g. '1.0.12'). If one or both inputs are invalid versions, the
* function will return NaN, otherwise, it will return -1 if the first
* version is smaller, 1 if the first version is bigger, and 0 if the two
* versions are equal.
*/
compareVersions: function (version1, version2) {
// if one or both inputs are valid, return NaN
if (!isString(version1) || !isString(version2)) {
return NaN;
}
// split on dot
const version1Parts = version1.split('.');
const version2Parts = version2.split('.');
// add trailing zeros to make the parts arrays the same length
while (version1Parts.length < version2Parts.length) {
version1Parts.push('0');
}
while (version2Parts.length < version1Parts.length) {
version2Parts.push('0');
}
// compare elements in the two arrays one by one
let result = 0;
let version1Part, version2Part;
for (let index = 0, length = version1Parts.length; index < length; index++) {
// convert to number before doing any arithmetic
version1Part = Number(version1Parts[index]);
version2Part = Number(version2Parts[index]);
// if one or both values are not numerical, consider the input invalid
if (!isNumber(version1Part) || !isNumber(version2Part)) {
result = NaN;
break;
}
// if the two values are different, pick the
// correct result based on which value is smaller
if (version1Part !== version2Part) {
result = version1Part < version2Part ? -1 : 1;
break;
}
}
return result;
},
};
/**
* Determines if a given value is not null or undefined.
*
* @deprecated Just use if (!value) instead
*/
function exists(value) {
return value !== null && value !== undefined;
}
/**
* A collection of url-related utility functions.
*/
exports.url = {
/**
* Appends a query parameter to a url. If an invalid url is specified, an
* exception is thrown.
*
* @param url
* @param paramName the name of the query parameter.
* @param paramValue the value of the query parameter.
*/
appendParam: function (url, paramName, paramValue) {
// if the specified url is valid
const urlAsObject = url_1.default.parse(url);
if (urlAsObject) {
// if the url already has query parameters, use '&' as the separator
// when appending the additional query parameter, otherwise use '?'
url += (urlAsObject.search ? '&' : '?') + paramName + '=' + paramValue;
}
return url;
},
appendRetryParam: function (option) {
let retryUrl = this.appendParam(option.url, 'retryCount', option.retryCount);
if (option.includeRetryReason) {
retryUrl = this.appendParam(retryUrl, 'retryReason', option.retryReason);
}
return retryUrl;
},
};
/**
* Shallow-copies everything from a source object into a destination object.
*
* @param {Object} dst the object to copy properties to.
* @param {Object} src the object to copy properties from.
*/
function apply(dst, src) {
// if both dst and src are objects, copy everything from src to dst
if (isObject(dst) && isObject(src)) {
for (const key in src) {
if (Object.prototype.hasOwnProperty.call(src, key)) {
dst[key] = src[key];
}
}
}
return dst;
}
/**
* Returns true if the code is currently being run in the browser, false
* otherwise.
*/
function isBrowser() {
// @ts-ignore TS2339: Property 'browser' does not exist on type 'Process'
return !!(process && process.browser);
}
/**
* Returns true if the code is currently being run in node, false otherwise.
*/
function isNode() {
return !isBrowser();
}
/**
* Returns the next sleep time calculated by exponential backoff with
* decorrelated jitter.
* sleep = min(cap, random_between(base, sleep * 3))
* for more details, check out:
* http://www.awsarchitectureblog.com/2015/03/backoff.html
* @param base minimum seconds
* @param cap maximum seconds
* @param previousSleep previous sleep time
*/
function nextSleepTime(base, cap, previousSleep) {
return Math.min(cap, Math.abs(previousSleep * 3 - base) * Math.random() + Math.min(base, previousSleep * 3));
}
/**
* Return next sleep time calculated by the jitter rule.
*/
function getJitteredSleepTime(numofRetries, currentSleepTime, totalElapsedTime, maxRetryTimeout) {
const nextsleep = getNextSleepTime(numofRetries, currentSleepTime);
const sleep = maxRetryTimeout !== 0 ? Math.min(maxRetryTimeout - totalElapsedTime, nextsleep) : nextsleep;
totalElapsedTime += sleep;
return { sleep, totalElapsedTime };
}
/**
* Choose one of the number between two numbers.
*/
function chooseRandom(firstNumber, secondNumber) {
return Math.random() * (firstNumber - secondNumber) + secondNumber;
}
/**
* return the next sleep Time.
*/
function getNextSleepTime(numofRetries, currentSleepTime) {
const nextSleep = 2 ** numofRetries;
return chooseRandom(currentSleepTime + getJitter(currentSleepTime), nextSleep + getJitter(currentSleepTime));
}
/**
* return the jitter value.
*/
function getJitter(currentSleepTime) {
const multiplicationFactor = chooseRandom(1, -1);
return 0.5 * currentSleepTime * multiplicationFactor;
}
/**
* Check whether the request is the login-request or not.
*/
function isLoginRequest(loginUrl) {
const endPoints = ['/v1/login-request', '/authenticator-request'];
return endPoints.some((endPoint) => loginUrl.includes(endPoint));
}
/**
* Checks if the HTTP response code is retryable
*
* @param response HTTP response object
* @param retry403 will retry HTTP 403?
*/
function isRetryableHttpError(response, retry403) {
return (response &&
((response.statusCode >= 500 && response.statusCode < 600) ||
(retry403 && response.statusCode === 403) ||
response.statusCode === 408 ||
response.statusCode === 429));
}
function validateClientSessionKeepAliveHeartbeatFrequency(input, masterValidity) {
let heartbeatFrequency = input;
const realMax = Math.floor(masterValidity / 4);
const realMin = Math.floor(realMax / 4);
if (input > realMax) {
heartbeatFrequency = realMax;
}
else if (input < realMin) {
heartbeatFrequency = realMin;
}
heartbeatFrequency = Math.floor(heartbeatFrequency);
return heartbeatFrequency;
}
/**
* Constructs host name using region and account
*
* @param region where the account is located
* @param account which account to connect to
*/
function constructHostname(region, account) {
let host;
if (region === 'us-west-2') {
host = account + '.snowflakecomputing.com';
}
else if (region != null) {
if (account.indexOf('.') > 0) {
account = account.substring(0, account.indexOf('.'));
}
if (region.startsWith('cn-') || region.startsWith('CN-')) {
host = account + '.' + region + '.snowflakecomputing.cn';
}
else {
host = account + '.' + region + '.snowflakecomputing.com';
}
}
else {
host = account + '.snowflakecomputing.com';
}
return host;
}
/**
* Returns true if host indicates private link
*/
function isPrivateLink(host) {
Errors.checkArgumentExists(exists(host), Errors.codes.ERR_CONN_CREATE_MISSING_HOST);
return host.toLowerCase().includes('privatelink.snowflakecomputing.');
}
function createOcspResponseCacheServerUrl(host) {
return `http://ocsp.${host}/ocsp_response_cache.json`;
}
/**
* Returns if command is a PUT command
*/
function isPutCommand(sqlText) {
return sqlText.trim().substring(0, 3).toUpperCase() === 'PUT';
}
/**
* Returns if command is a GET command
*/
function isGetCommand(sqlText) {
return sqlText.trim().substring(0, 3).toUpperCase() === 'GET';
}
/**
* Add double quotes to smkId's value to parse it as a string instead of integer
* to preserve precision of numbers exceeding JavaScript's max safe integer
* e.g (inputting 32621973126123526 outputs 32621973126123530)
*
* @param body the data in JSON
*/
function convertSmkIdToString(body) {
return body.replace(/"smkId"(\s*):(\s*)([0-9]+)/g, '"smkId"$1:$2"$3"');
}
/**
* Under some circumstances the object passed to JSON.stringify in exception handling
* can contain circular reference, on which JSON.stringify bails out
* MDN way of handling such error
*/
function getCircularReplacer() {
const ancestors = [];
return function (key, value) {
if (typeof value !== 'object' || value === null) {
return value;
}
// `this` is the object that value is contained in,
// i.e., its direct parent.
// @ts-ignore TS2683: 'this' implicitly has type 'any' because it does not have a type annotation.
while (ancestors.length > 0 && ancestors[ancestors.length - 1] !== this) {
ancestors.pop();
}
if (ancestors.includes(value)) {
return '[Circular]';
}
ancestors.push(value);
return value;
};
}
/**
* Returns if the provided string is a valid subdomain.
*/
function isCorrectSubdomain(value) {
const subdomainRegex = RegExp(/^\w+([.-]\w+)*$/i);
return subdomainRegex.test(value);
}
function buildCredentialCacheKey(host, username, credType) {
if (!host || !username || !credType) {
Logger.getInstance().debug('Cannot build the credential cache key because one of host, username, and credType is null');
return null;
}
return `{${host.toUpperCase()}}:{${username.toUpperCase()}}:{${credType.toUpperCase()}}`;
}
function checkValidCustomCredentialManager(customCredentialManager) {
if (typeof customCredentialManager !== 'object') {
return false;
}
const requireMethods = ['write', 'read', 'remove'];
for (const method of requireMethods) {
if (!Object.hasOwnProperty.call(customCredentialManager, method) ||
typeof customCredentialManager[method] !== 'function') {
return false;
}
}
return true;
}
function checkParametersDefined(...parameters) {
return parameters.every((element) => element !== undefined && element !== null);
}
/**
* Checks if the provided file or directory permissions are correct.
* @param filePath
* @param expectedMode
* @param fsPromises
* @returns {Promise<boolean>} resolves always to true for Windows
*/
async function isFileModeCorrect(filePath, expectedMode, fsPromises) {
if (os_1.default.platform() === 'win32') {
return true;
}
return await fsPromises.stat(filePath).then((stats) => {
// we have to limit the number of LSB bits to 9 with the mask, as the stats.mode starts with the file type,
// e.g. the directory with permissions 755 will have stats.mask of 40755.
const mask = (1 << 9) - 1;
return (stats.mode & mask) === expectedMode;
});
}
/**
* Checks if the provided file or directory is writable only by the user.
* @returns {Promise<boolean>} resolves always to true for Windows
*/
async function isFileNotWritableByGroupOrOthers(configFilePath, fsPromises) {
if (os_1.default.platform() === 'win32') {
return true;
}
const stats = await fsPromises.stat(configFilePath);
return (stats.mode & (1 << 4)) === 0 && (stats.mode & (1 << 1)) === 0;
}
function shouldRetryOktaAuth({ maxRetryTimeout, maxRetryCount, numRetries, startTime, remainingTimeout, }) {
return ((maxRetryTimeout === 0 || Date.now() < startTime + remainingTimeout) &&
numRetries <= maxRetryCount);
}
function getDriverDirectory() {
return __dirname;
}
function validatePath(dir) {
try {
const stat = fs_1.default.statSync(dir);
return stat.isDirectory();
}
catch {
Logger.getInstance().error('The location is invalid. Please check this location is accessible or existing');
return false;
}
}
function getEnvVar(variable) {
return process.env[variable.toLowerCase()] || process.env[variable.toUpperCase()];
}
function validateEmptyString(value) {
return value !== '' ? value : undefined;
}
function isNotEmptyAsString(variable) {
if (typeof variable === 'string') {
return variable;
}
return exists(variable);
}
function isNotEmptyString(variable) {
return exists(variable) && variable !== '';
}
/**
* Checks Whether the object is empty (can be null or undefined) or not.
*/
function isEmptyObject(object) {
if (!exists(object)) {
return true;
}
if (typeof object !== 'object') {
return false;
}
return Object.keys(object).length === 0;
}
function isWindows() {
return os_1.default.platform() === 'win32';
}
async function getFreePort() {
return new Promise((res) => {
const srv = net_1.default.createServer();
srv.listen(0, () => {
// @ts-ignore TS2339: Property 'port' does not exist on type 'string | AddressInfo'
const port = srv.address().port;
srv.close(() => res(port));
});
});
}
async function isPortOpen(port) {
return new Promise((resolve, reject) => {
const s = net_1.default.createServer();
s.once('error', (err) => {
s.close();
if (err['code'] === 'EADDRINUSE') {
Logger.getInstance().trace(`Port: ${port} is not available. Verification failed`);
reject('Port not available.');
}
else {
Logger.getInstance().trace(`There is unexpected error during verification of port availability. Port: ${port}. Error: ${JSON.stringify(err)}`);
}
});
s.once('listening', () => {
s.close();
Logger.getInstance().trace(`Closing server run for verification whether the port is available. Port: ${port}`);
resolve('Listening');
});
s.listen(port);
});
}
/**
* Left strip the specified character from a string.
*/
function lstrip(str, remove) {
while (str.length > 0 && remove.indexOf(str.charAt(0)) !== -1) {
str = str.substr(1);
}
return str;
}
/**
* This method transforms HTML special characters into their corresponding entity representations.
*/
function escapeHTML(value) {
if (!exists(value)) {
return value;
}
return value
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
/**
* Typescript with "module": "commonjs" will transform every import() to a require() statement.
*
* This will break ESM dynamic imports resulting in a runtime error:
* -require() of ES Module... from ... not supported.
*
* A hacky solution - https://github.com/microsoft/TypeScript/issues/43329
*
* This could be removed once we drop node 18 support as Node 20+ support esm in require()
*/
async function dynamicImportESMInTypescriptWithCommonJS(moduleName) {
return Function(`return import("${moduleName}")`)();
}
//# sourceMappingURL=util.js.map
;