1- import thrift , { HttpHeaders } from 'thrift' ;
1+ import thrift from 'thrift' ;
22
33import { EventEmitter } from 'events' ;
44import TCLIService from '../thrift/TCLIService' ;
@@ -13,12 +13,13 @@ import HttpConnection from './connection/connections/HttpConnection';
1313import IConnectionOptions from './connection/contracts/IConnectionOptions' ;
1414import Status from './dto/Status' ;
1515import HiveDriverError from './errors/HiveDriverError' ;
16- import { areHeadersEqual , buildUserAgentString , definedOrError } from './utils' ;
16+ import { buildUserAgentString , definedOrError } from './utils' ;
1717import PlainHttpAuthentication from './connection/auth/PlainHttpAuthentication' ;
1818import DatabricksOAuth from './connection/auth/DatabricksOAuth' ;
1919import IDBSQLLogger , { LogLevel } from './contracts/IDBSQLLogger' ;
2020import DBSQLLogger from './DBSQLLogger' ;
2121import CloseableCollection from './utils/CloseableCollection' ;
22+ import IConnectionProvider from './connection/contracts/IConnectionProvider' ;
2223
2324function prependSlash ( str : string ) : string {
2425 if ( str . length > 0 && str . charAt ( 0 ) !== '/' ) {
@@ -41,13 +42,11 @@ function getInitialNamespaceOptions(catalogName?: string, schemaName?: string) {
4142}
4243
4344export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
44- private client : TCLIService . Client | null = null ;
45+ private connectionProvider ?: IConnectionProvider ;
4546
46- private authProvider : IAuthentication | null = null ;
47+ private authProvider ? : IAuthentication ;
4748
48- private connectionOptions : ConnectionOptions | null = null ;
49-
50- private additionalHeaders : HttpHeaders = { } ;
49+ private client ?: TCLIService . Client ;
5150
5251 private readonly logger : IDBSQLLogger ;
5352
@@ -61,30 +60,14 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
6160 this . logger . log ( LogLevel . info , 'Created DBSQLClient' ) ;
6261 }
6362
64- private getConnectionOptions ( options : ConnectionOptions , headers : HttpHeaders ) : IConnectionOptions {
65- const {
66- host,
67- port,
68- path,
69- clientId,
70- authType,
71- // @ts -expect-error TS2339: Property 'token' does not exist on type 'ConnectionOptions'
72- token,
73- // @ts -expect-error TS2339: Property 'persistence' does not exist on type 'ConnectionOptions'
74- persistence,
75- // @ts -expect-error TS2339: Property 'provider' does not exist on type 'ConnectionOptions'
76- provider,
77- ...otherOptions
78- } = options ;
79-
63+ private getConnectionOptions ( options : ConnectionOptions ) : IConnectionOptions {
8064 return {
81- host,
82- port : port || 443 ,
83- path : prependSlash ( path ) ,
65+ host : options . host ,
66+ port : options . port || 443 ,
67+ path : prependSlash ( options . path ) ,
8468 https : true ,
85- ... otherOptions ,
69+ socketTimeout : options . socketTimeout ,
8670 headers : {
87- ...headers ,
8871 'User-Agent' : buildUserAgentString ( options . clientId ) ,
8972 } ,
9073 } ;
@@ -128,7 +111,38 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
128111 */
129112 public async connect ( options : ConnectionOptions , authProvider ?: IAuthentication ) : Promise < IDBSQLClient > {
130113 this . authProvider = this . getAuthProvider ( options , authProvider ) ;
131- this . connectionOptions = options ;
114+
115+ this . connectionProvider = new HttpConnection ( this . getConnectionOptions ( options ) ) ;
116+
117+ const thriftConnection = await this . connectionProvider . getThriftConnection ( ) ;
118+
119+ thriftConnection . on ( 'error' , ( error : Error ) => {
120+ // Error.stack already contains error type and message, so log stack if available,
121+ // otherwise fall back to just error type + message
122+ this . logger . log ( LogLevel . error , error . stack || `${ error . name } : ${ error . message } ` ) ;
123+ try {
124+ this . emit ( 'error' , error ) ;
125+ } catch ( e ) {
126+ // EventEmitter will throw unhandled error when emitting 'error' event.
127+ // Since we already logged it few lines above, just suppress this behaviour
128+ }
129+ } ) ;
130+
131+ thriftConnection . on ( 'reconnecting' , ( params : { delay : number ; attempt : number } ) => {
132+ this . logger . log ( LogLevel . debug , `Reconnecting, params: ${ JSON . stringify ( params ) } ` ) ;
133+ this . emit ( 'reconnecting' , params ) ;
134+ } ) ;
135+
136+ thriftConnection . on ( 'close' , ( ) => {
137+ this . logger . log ( LogLevel . debug , 'Closing connection.' ) ;
138+ this . emit ( 'close' ) ;
139+ } ) ;
140+
141+ thriftConnection . on ( 'timeout' , ( ) => {
142+ this . logger . log ( LogLevel . debug , 'Connection timed out.' ) ;
143+ this . emit ( 'timeout' ) ;
144+ } ) ;
145+
132146 return this ;
133147 }
134148
@@ -158,65 +172,28 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
158172 }
159173
160174 private async getClient ( ) {
161- if ( ! this . connectionOptions || ! this . authProvider ) {
175+ if ( ! this . connectionProvider ) {
162176 throw new HiveDriverError ( 'DBSQLClient: not connected' ) ;
163177 }
164178
165- const authHeaders = await this . authProvider . authenticate ( ) ;
166- // When auth headers change - recreate client. Thrift library does not provide API for updating
167- // changed options, therefore we have to recreate both connection and client to apply new headers
168- if ( ! this . client || ! areHeadersEqual ( this . additionalHeaders , authHeaders ) ) {
179+ if ( ! this . client ) {
169180 this . logger . log ( LogLevel . info , 'DBSQLClient: initializing thrift client' ) ;
170- this . additionalHeaders = authHeaders ;
171- const connectionOptions = this . getConnectionOptions ( this . connectionOptions , this . additionalHeaders ) ;
181+ this . client = this . thrift . createClient ( TCLIService , await this . connectionProvider . getThriftConnection ( ) ) ;
182+ }
172183
173- const connection = await this . createConnection ( connectionOptions ) ;
174- this . client = this . thrift . createClient ( TCLIService , connection . getConnection ( ) ) ;
184+ if ( this . authProvider ) {
185+ const authHeaders = await this . authProvider . authenticate ( ) ;
186+ this . connectionProvider . setHeaders ( authHeaders ) ;
175187 }
176188
177189 return this . client ;
178190 }
179191
180- private async createConnection ( options : IConnectionOptions ) {
181- const connectionProvider = new HttpConnection ( ) ;
182- const connection = await connectionProvider . connect ( options ) ;
183- const thriftConnection = connection . getConnection ( ) ;
184-
185- thriftConnection . on ( 'error' , ( error : Error ) => {
186- // Error.stack already contains error type and message, so log stack if available,
187- // otherwise fall back to just error type + message
188- this . logger . log ( LogLevel . error , error . stack || `${ error . name } : ${ error . message } ` ) ;
189- try {
190- this . emit ( 'error' , error ) ;
191- } catch ( e ) {
192- // EventEmitter will throw unhandled error when emitting 'error' event.
193- // Since we already logged it few lines above, just suppress this behaviour
194- }
195- } ) ;
196-
197- thriftConnection . on ( 'reconnecting' , ( params : { delay : number ; attempt : number } ) => {
198- this . logger . log ( LogLevel . debug , `Reconnecting, params: ${ JSON . stringify ( params ) } ` ) ;
199- this . emit ( 'reconnecting' , params ) ;
200- } ) ;
201-
202- thriftConnection . on ( 'close' , ( ) => {
203- this . logger . log ( LogLevel . debug , 'Closing connection.' ) ;
204- this . emit ( 'close' ) ;
205- } ) ;
206-
207- thriftConnection . on ( 'timeout' , ( ) => {
208- this . logger . log ( LogLevel . debug , 'Connection timed out.' ) ;
209- this . emit ( 'timeout' ) ;
210- } ) ;
211-
212- return connection ;
213- }
214-
215192 public async close ( ) : Promise < void > {
216193 await this . sessions . closeAll ( ) ;
217194
218- this . client = null ;
219- this . authProvider = null ;
220- this . connectionOptions = null ;
195+ this . client = undefined ;
196+ this . connectionProvider = undefined ;
197+ this . authProvider = undefined ;
221198 }
222199}
0 commit comments