diff --git a/examples/utils.ts b/examples/utils.ts index 37cb6083..2bfa9de2 100644 --- a/examples/utils.ts +++ b/examples/utils.ts @@ -1,7 +1,6 @@ -import {getLogger, Logger, parseConnectionString} from 'ydb-sdk'; +import {getLogger, Logger} from 'ydb-sdk'; import yargs from 'yargs'; - export interface Runner { (logger: Logger, endpoint: string, database: string, cliParams?: any): Promise; } @@ -16,23 +15,25 @@ export interface Option { } export function getCliParams(options?: Option[]) { - const optionsUsage = options && options.length > 0 ? options.map((option) => ` --${option.name}`).join('') : ''; + const optionsUsage = + options && options.length > 0 ? options.map((option) => ` --${option.name}`).join('') : ''; - const argsBuilder = yargs - .usage(`Usage: $0 (--db --endpoint or --connection-string )${optionsUsage}`); + const argsBuilder = yargs.usage( + `Usage: $0 (--db --endpoint or --connection-string )${optionsUsage}`, + ); argsBuilder.options({ - 'db': { describe: 'YDB database name', type: 'string'}, - 'endpoint': {describe: 'YDB database endpoint', type: 'string'}, - 'connection-string': {describe: 'YDB connection string', type: 'string'}, + db: {describe: 'YDB database name', type: 'string'}, + endpoint: {describe: 'YDB database endpoint', type: 'string'}, + 'connection-string': {describe: 'YDB connection string', type: 'string'}, }); options?.forEach((option) => { - argsBuilder.option(option.name, { - describe: option.description, - type: 'string', - demandOption: true, - }); + argsBuilder.option(option.name, { + describe: option.description, + type: 'string', + demandOption: true, + }); }); const args = argsBuilder.argv; @@ -44,26 +45,30 @@ export function getCliParams(options?: Option[]) { let endpoint; let db; if (connectionStringParam) { - const parsedConnectionString = parseConnectionString(connectionStringParam); - endpoint = parsedConnectionString.endpoint; - db = parsedConnectionString.database; + let cs = new URL(connectionStringParam); + endpoint = cs.origin; + db = cs.pathname || cs.searchParams.get('database') || ''; } else if (endpointParam && dbParam) { endpoint = endpointParam; db = dbParam; } else { - throw new Error('Either --connection-string or --db --endpoint arguments are required'); + throw new Error( + 'Either --connection-string or --db --endpoint arguments are required', + ); } const cliParams = {} as any; options?.forEach((option) => { cliParams[option.key] = args[option.key] as string; }); - return {endpoint, db, cliParams} + return {endpoint, db, cliParams}; } export async function mainWithoutLogger(runner: RunnerWithoutLogger, options?: Option[]) { - const {db, endpoint, cliParams} = getCliParams(options) - console.log(`Running basic-example script against endpoint '${endpoint}' and database '${db}'.`) + const {db, endpoint, cliParams} = getCliParams(options); + console.log( + `Running basic-example script against endpoint '${endpoint}' and database '${db}'.`, + ); try { await runner(endpoint, db, cliParams); } catch (error) { @@ -72,16 +77,18 @@ export async function mainWithoutLogger(runner: RunnerWithoutLogger, options?: O } export async function main(runner: Runner, options?: Option[]) { - const {db, endpoint, cliParams} = getCliParams(options) + const {db, endpoint, cliParams} = getCliParams(options); - const logger = getLogger(); - logger.info(`Running basic-example script against endpoint '${endpoint}' and database '${db}'.`); + const logger = getLogger(); + logger.info( + `Running basic-example script against endpoint '${endpoint}' and database '${db}'.`, + ); - try { - await runner(logger, endpoint, db, cliParams); - } catch (error) { - logger.error(error as object); - } + try { + await runner(logger, endpoint, db, cliParams); + } catch (error) { + logger.error(error as object); + } } export const SYNTAX_V1 = '--!syntax_v1'; diff --git a/src/__tests__/unit/parse-connection-string.test.ts b/src/__tests__/unit/parse-connection-string.test.ts deleted file mode 100644 index 78684a49..00000000 --- a/src/__tests__/unit/parse-connection-string.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import {parseConnectionString} from "../../utils/parse-connection-string"; - -describe('Parse connection string', () => { - it('test parseConnectionString', () => { - const tests = [ - { - connectionString: 'ydb-ru.yandex.net:2135/?database=/ru/home/service/db', - endpoint: 'grpcs://ydb-ru.yandex.net:2135', - database: '/ru/home/service/db', - }, - { - connectionString: 'grpc://ydb-ru.yandex.net:2135/?database=/ru/home/service/db', - endpoint: 'grpc://ydb-ru.yandex.net:2135', - database: '/ru/home/service/db', - }, - { - connectionString: 'grpcs://ydb-ru.yandex.net:2135/?database=/ru/home/service/db', - endpoint: 'grpcs://ydb-ru.yandex.net:2135', - database: '/ru/home/service/db', - }, - { - connectionString: "grpcs://ydb.serverless.yandexcloud.net:2135/?database=/ru-central1/b1g8skpblkos03malf3s/etn03nohfn502cpa0cfe", - endpoint: "grpcs://ydb.serverless.yandexcloud.net:2135", - database: "/ru-central1/b1g8skpblkos03malf3s/etn03nohfn502cpa0cfe" - } - ]; - - tests.forEach((test) => { - const parsedString = parseConnectionString(test.connectionString); - expect(parsedString).toEqual({endpoint: test.endpoint, database: test.database}); - }); - }); -}); diff --git a/src/driver.ts b/src/driver.ts index b5e9b8b0..c5e6a753 100644 --- a/src/driver.ts +++ b/src/driver.ts @@ -1,20 +1,19 @@ import {ENDPOINT_DISCOVERY_PERIOD} from './constants'; import {TimeoutExpired} from './errors'; -import {ISslCredentials, makeSslCredentials} from './utils/ssl-credentials'; -import DiscoveryService from "./discovery/discovery-service"; -import {TableClient} from "./table"; -import {ClientOptions} from "./utils"; -import {IAuthService} from "./credentials/i-auth-service"; -import SchemeService from "./schema/scheme-client"; -import SchemeClient from "./schema/scheme-client"; -import {parseConnectionString} from "./utils/parse-connection-string"; -import {QueryClient} from "./query"; -import {Logger} from "./logger/simple-logger"; -import {getDefaultLogger} from "./logger/get-default-logger"; -import {TopicClient} from "./topic"; -import {RetryStrategy} from "./retries/retryStrategy"; -import {RetryParameters} from "./retries/retryParameters"; -import {IClientSettings} from "./client/settings"; +import {ISslCredentials, makeDefaultSslCredentials} from './utils/ssl-credentials'; +import DiscoveryService from './discovery/discovery-service'; +import {TableClient} from './table'; +import {ClientOptions} from './utils'; +import {IAuthService} from './credentials/i-auth-service'; +import SchemeService from './schema/scheme-client'; +import SchemeClient from './schema/scheme-client'; +import {QueryClient} from './query'; +import {Logger} from './logger/simple-logger'; +import {getDefaultLogger} from './logger/get-default-logger'; +import {TopicClient} from './topic'; +import {RetryStrategy} from './retries/retryStrategy'; +import {RetryParameters} from './retries/retryParameters'; +import {IClientSettings} from './client/settings'; export interface IPoolSettings { minLimit?: number; @@ -23,11 +22,18 @@ export interface IPoolSettings { } export interface IDriverSettings { + /** + * @deprecated Use connectionString instead + */ endpoint?: string; + + /** + * @deprecated Use connectionString instead + */ database?: string; connectionString?: string; authService: IAuthService; - sslCredentials?: ISslCredentials, + sslCredentials?: ISslCredentials; poolSettings?: IPoolSettings; clientOptions?: ClientOptions; retrier?: RetryStrategy; @@ -52,20 +58,46 @@ export default class Driver { } constructor(settings: IDriverSettings) { + let secure: boolean = false, + endpoint: string = '', + database: string = ''; + this.logger = settings.logger || getDefaultLogger(); - let endpoint: string, database: string; - if (settings.connectionString) { - ({endpoint, database} = parseConnectionString(settings.connectionString)); - } else if (!settings.endpoint) { - throw new Error('The "endpoint" is a required field in driver settings'); - } else if (!settings.database) { - throw new Error('The "database" is a required field in driver settings'); - } else { + + if (settings.endpoint && settings.database) { + settings.logger?.warn( + 'The "endpoint" and "database" fields are deprecated. Use "connectionString" instead', + ); + endpoint = settings.endpoint; database = settings.database; + + secure = endpoint.startsWith('grpcs://') || endpoint.startsWith('https://'); + } + + if (settings.connectionString) { + let cs = new URL(settings.connectionString); + endpoint = cs.origin; + database = cs.pathname || cs.searchParams.get('database') || ''; + + if (!database) { + throw new Error( + 'The "database" field is required in the connection string. It should be specified either in the path or as a `database` query parameter.', + ); + } + + secure = cs.protocol === 'grpcs:' || cs.protocol === 'https:'; + } + + if (!endpoint || !database) { + throw new Error( + 'Either "endpoint" and "database" or "connectionString" must be specified', + ); } - const sslCredentials = makeSslCredentials(endpoint, this.logger, settings.sslCredentials); + const sslCredentials = secure + ? settings.sslCredentials ?? makeDefaultSslCredentials() + : undefined; const retrier = settings.retrier || new RetryStrategy(new RetryParameters(), this.logger); diff --git a/src/index.ts b/src/index.ts index 1112b559..9ab69d41 100644 --- a/src/index.ts +++ b/src/index.ts @@ -8,10 +8,7 @@ export { getFallbackLogFunction, } from './logger/deprecated'; -export { - Logger, - LogFn, -} from './logger/simple-logger'; +export {Logger, LogFn} from './logger/simple-logger'; export {default as Driver, IDriverSettings, IPoolSettings} from './driver'; @@ -40,53 +37,52 @@ export {Context} from './context'; export {YdbError, StatusCode} from './errors'; -export {TableSessionPool} from "./table/table-session-pool"; +export {TableSessionPool} from './table/table-session-pool'; -export {AlterTableDescription} from "./table/table-session"; -export {TableDescription} from "./table/table-session"; -export {TableIndex} from "./table/table-session"; -export {TableProfile} from "./table/table-session"; -export {CachingPolicy} from "./table/table-session"; -export {ExecutionPolicy} from "./table/table-session"; -export {CompactionPolicy} from "./table/table-session"; -export {ReplicationPolicy} from "./table/table-session"; -export {PartitioningPolicy} from "./table/table-session"; -export {ExplicitPartitions} from "./table/table-session"; -export {StoragePolicy} from "./table/table-session"; -export {ColumnFamilyPolicy} from "./table/table-session"; -export {StorageSettings} from "./table/table-session"; -export {Column} from "./table/table-session"; -export {TableSession, TableSession as Session} from "./table/table-session"; -export {ExecuteScanQuerySettings} from "./table/table-session"; -export {ReadTableSettings} from "./table/table-session"; -export {BulkUpsertSettings} from "./table/table-session"; -export {ExecuteQuerySettings} from "./table/table-session"; -export {PrepareQuerySettings} from "./table/table-session"; -export {RollbackTransactionSettings} from "./table/table-session"; -export {CommitTransactionSettings} from "./table/table-session"; -export {BeginTransactionSettings} from "./table/table-session"; -export {DescribeTableSettings} from "./table/table-session"; -export {DropTableSettings} from "./table/table-session"; -export {AlterTableSettings} from "./table/table-session"; -export {CreateTableSettings} from "./table/table-session"; -export {OperationParams} from "./table/table-session"; -export {AUTO_TX} from "./table/table-session"; +export {AlterTableDescription} from './table/table-session'; +export {TableDescription} from './table/table-session'; +export {TableIndex} from './table/table-session'; +export {TableProfile} from './table/table-session'; +export {CachingPolicy} from './table/table-session'; +export {ExecutionPolicy} from './table/table-session'; +export {CompactionPolicy} from './table/table-session'; +export {ReplicationPolicy} from './table/table-session'; +export {PartitioningPolicy} from './table/table-session'; +export {ExplicitPartitions} from './table/table-session'; +export {StoragePolicy} from './table/table-session'; +export {ColumnFamilyPolicy} from './table/table-session'; +export {StorageSettings} from './table/table-session'; +export {Column} from './table/table-session'; +export {TableSession, TableSession as Session} from './table/table-session'; +export {ExecuteScanQuerySettings} from './table/table-session'; +export {ReadTableSettings} from './table/table-session'; +export {BulkUpsertSettings} from './table/table-session'; +export {ExecuteQuerySettings} from './table/table-session'; +export {PrepareQuerySettings} from './table/table-session'; +export {RollbackTransactionSettings} from './table/table-session'; +export {CommitTransactionSettings} from './table/table-session'; +export {BeginTransactionSettings} from './table/table-session'; +export {DescribeTableSettings} from './table/table-session'; +export {DropTableSettings} from './table/table-session'; +export {AlterTableSettings} from './table/table-session'; +export {CreateTableSettings} from './table/table-session'; +export {OperationParams} from './table/table-session'; +export {AUTO_TX} from './table/table-session'; -export {StaticCredentialsAuthService} from "./credentials/static-credentials-auth-service"; -export {IamAuthService} from "./credentials/iam-auth-service"; -export {MetadataAuthService} from "./credentials/metadata-auth-service"; -export {TokenAuthService} from "./credentials/token-auth-service"; -export {AnonymousAuthService} from "./credentials/anonymous-auth-service"; -export {ITokenService} from "./credentials/i-token-service"; -export {IAuthService} from "./credentials/i-auth-service"; -export {ModifyPermissionsSettings} from "./schema/scheme-service"; -export {DescribePathSettings} from "./schema/scheme-service"; -export {ListDirectorySettings} from "./schema/scheme-service"; -export {RemoveDirectorySettings} from "./schema/scheme-service"; -export {MakeDirectorySettings} from "./schema/scheme-service"; +export {StaticCredentialsAuthService} from './credentials/static-credentials-auth-service'; +export {IamAuthService} from './credentials/iam-auth-service'; +export {MetadataAuthService} from './credentials/metadata-auth-service'; +export {TokenAuthService} from './credentials/token-auth-service'; +export {AnonymousAuthService} from './credentials/anonymous-auth-service'; +export {ITokenService} from './credentials/i-token-service'; +export {IAuthService} from './credentials/i-auth-service'; +export {ModifyPermissionsSettings} from './schema/scheme-service'; +export {DescribePathSettings} from './schema/scheme-service'; +export {ListDirectorySettings} from './schema/scheme-service'; +export {RemoveDirectorySettings} from './schema/scheme-service'; +export {MakeDirectorySettings} from './schema/scheme-service'; -export {ParsedConnectionString, parseConnectionString} from "./utils/parse-connection-string"; -export {QueryClient, ResultSet, RowType} from "./query"; +export {QueryClient, ResultSet, RowType} from './query'; -export {SimpleLogger} from "./logger/simple-logger"; -export {getDefaultLogger} from "./logger/get-default-logger"; +export {SimpleLogger} from './logger/simple-logger'; +export {getDefaultLogger} from './logger/get-default-logger'; diff --git a/src/utils/parse-connection-string.ts b/src/utils/parse-connection-string.ts deleted file mode 100644 index d5de8bbc..00000000 --- a/src/utils/parse-connection-string.ts +++ /dev/null @@ -1,35 +0,0 @@ -import url from 'url'; - -export interface ParsedConnectionString { - endpoint: string; - database: string; -} - -export function parseConnectionString(connectionString: string): ParsedConnectionString { - let cs = connectionString; - if (!cs.startsWith('grpc://') && !cs.startsWith('grpcs://') ){ - cs = 'grpcs://' + cs; - } - - let parsedUrl = url.parse(cs, true); - let databaseParam = parsedUrl.query['database']; - - let database; - if (databaseParam === undefined) { - throw new Error('unknown database'); - } else if (Array.isArray(databaseParam)) { - if (databaseParam.length === 0) { - throw new Error('unknown database'); - } - database = databaseParam[0]; - } else { - database = databaseParam; - } - - const host = parsedUrl.host || 'localhost'; - - return { - endpoint: `${parsedUrl.protocol}//${host}`, - database, - }; -} diff --git a/src/utils/ssl-credentials.ts b/src/utils/ssl-credentials.ts index e1768667..2c0dc56f 100644 --- a/src/utils/ssl-credentials.ts +++ b/src/utils/ssl-credentials.ts @@ -3,11 +3,10 @@ import * as tls from 'tls'; // noinspection ES6PreferShortImport import certs from '../certs/certs.json'; -import {Logger} from "../logger/simple-logger"; function makeInternalRootCertificates() { - const internalRootCertificates = Buffer.from(certs.internal, 'utf8') - const fallbackSystemRootCertificates = Buffer.from(certs.system, 'utf8') + const internalRootCertificates = Buffer.from(certs.internal, 'utf8'); + const fallbackSystemRootCertificates = Buffer.from(certs.system, 'utf8'); let systemRootCertificates: Buffer; const nodeRootCertificates = tls.rootCertificates as string[] | undefined; @@ -28,25 +27,8 @@ export function makeDefaultSslCredentials() { return {rootCertificates: makeInternalRootCertificates()}; } -export function makeSslCredentials(endpoint: string, logger: Logger, sslCredentials: ISslCredentials | undefined): ISslCredentials | undefined { - if (endpoint.startsWith('grpc://')) { - logger.debug('Protocol grpc specified in endpoint, using insecure connection.'); - return undefined; - } - if (endpoint.startsWith('grpcs://')) { - logger.debug('Protocol grpcs specified in endpoint, using SSL connection.'); - } else { - logger.debug('No protocol specified in endpoint, using SSL connection.') - } - - if (sslCredentials) { - return sslCredentials; - } - return makeDefaultSslCredentials(); -} - export interface ISslCredentials { - rootCertificates?: Buffer, - clientPrivateKey?: Buffer, - clientCertChain?: Buffer + rootCertificates?: Buffer; + clientPrivateKey?: Buffer; + clientCertChain?: Buffer; }