Skip to content

Commit 9eb3807

Browse files
Implement client context (#191)
* Introduce client context Signed-off-by: Levko Kravets <levko.ne@gmail.com> * Pass context to all relevant classes Signed-off-by: Levko Kravets <levko.ne@gmail.com> * Make driver a part of context Signed-off-by: Levko Kravets <levko.ne@gmail.com> * Fix tests Signed-off-by: Levko Kravets <levko.ne@gmail.com> --------- Signed-off-by: Levko Kravets <levko.ne@gmail.com>
1 parent 46c3586 commit 9eb3807

26 files changed

Lines changed: 829 additions & 537 deletions

lib/DBSQLClient.ts

Lines changed: 38 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { EventEmitter } from 'events';
44
import TCLIService from '../thrift/TCLIService';
55
import { TProtocolVersion } from '../thrift/TCLIService_types';
66
import IDBSQLClient, { ClientOptions, ConnectionOptions, OpenSessionRequest } from './contracts/IDBSQLClient';
7+
import IDriver from './contracts/IDriver';
8+
import IClientContext from './contracts/IClientContext';
79
import HiveDriver from './hive/HiveDriver';
810
import { Int64 } from './hive/Types';
911
import DBSQLSession from './DBSQLSession';
@@ -41,13 +43,17 @@ function getInitialNamespaceOptions(catalogName?: string, schemaName?: string) {
4143
};
4244
}
4345

44-
export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
46+
export default class DBSQLClient extends EventEmitter implements IDBSQLClient, IClientContext {
4547
private connectionProvider?: IConnectionProvider;
4648

4749
private authProvider?: IAuthentication;
4850

4951
private client?: TCLIService.Client;
5052

53+
private readonly driver = new HiveDriver({
54+
context: this,
55+
});
56+
5157
private readonly logger: IDBSQLLogger;
5258

5359
private readonly thrift = thrift;
@@ -73,7 +79,7 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
7379
};
7480
}
7581

76-
private getAuthProvider(options: ConnectionOptions, authProvider?: IAuthentication): IAuthentication {
82+
private initAuthProvider(options: ConnectionOptions, authProvider?: IAuthentication): IAuthentication {
7783
if (authProvider) {
7884
return authProvider;
7985
}
@@ -84,15 +90,16 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
8490
return new PlainHttpAuthentication({
8591
username: 'token',
8692
password: options.token,
93+
context: this,
8794
});
8895
case 'databricks-oauth':
8996
return new DatabricksOAuth({
9097
host: options.host,
91-
logger: this.logger,
9298
persistence: options.persistence,
9399
azureTenantId: options.azureTenantId,
94100
clientId: options.oauthClientId,
95101
clientSecret: options.oauthClientSecret,
102+
context: this,
96103
});
97104
case 'custom':
98105
return options.provider;
@@ -110,7 +117,7 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
110117
* const session = client.connect({host, path, token});
111118
*/
112119
public async connect(options: ConnectionOptions, authProvider?: IAuthentication): Promise<IDBSQLClient> {
113-
this.authProvider = this.getAuthProvider(options, authProvider);
120+
this.authProvider = this.initAuthProvider(options, authProvider);
114121

115122
this.connectionProvider = new HttpConnection(this.getConnectionOptions(options));
116123

@@ -156,44 +163,57 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient {
156163
* const session = await client.openSession();
157164
*/
158165
public async openSession(request: OpenSessionRequest = {}): Promise<IDBSQLSession> {
159-
const driver = new HiveDriver(() => this.getClient());
160-
161-
const response = await driver.openSession({
166+
const response = await this.driver.openSession({
162167
client_protocol_i64: new Int64(TProtocolVersion.SPARK_CLI_SERVICE_PROTOCOL_V8),
163168
...getInitialNamespaceOptions(request.initialCatalog, request.initialSchema),
164169
});
165170

166171
Status.assert(response.status);
167-
const session = new DBSQLSession(driver, definedOrError(response.sessionHandle), {
168-
logger: this.logger,
172+
const session = new DBSQLSession({
173+
handle: definedOrError(response.sessionHandle),
174+
context: this,
169175
});
170176
this.sessions.add(session);
171177
return session;
172178
}
173179

174-
private async getClient() {
180+
public async close(): Promise<void> {
181+
await this.sessions.closeAll();
182+
183+
this.client = undefined;
184+
this.connectionProvider = undefined;
185+
this.authProvider = undefined;
186+
}
187+
188+
public getLogger(): IDBSQLLogger {
189+
return this.logger;
190+
}
191+
192+
public async getConnectionProvider(): Promise<IConnectionProvider> {
175193
if (!this.connectionProvider) {
176194
throw new HiveDriverError('DBSQLClient: not connected');
177195
}
178196

197+
return this.connectionProvider;
198+
}
199+
200+
public async getClient(): Promise<TCLIService.Client> {
201+
const connectionProvider = await this.getConnectionProvider();
202+
179203
if (!this.client) {
180204
this.logger.log(LogLevel.info, 'DBSQLClient: initializing thrift client');
181-
this.client = this.thrift.createClient(TCLIService, await this.connectionProvider.getThriftConnection());
205+
this.client = this.thrift.createClient(TCLIService, await connectionProvider.getThriftConnection());
182206
}
183207

184208
if (this.authProvider) {
185209
const authHeaders = await this.authProvider.authenticate();
186-
this.connectionProvider.setHeaders(authHeaders);
210+
connectionProvider.setHeaders(authHeaders);
187211
}
188212

189213
return this.client;
190214
}
191215

192-
public async close(): Promise<void> {
193-
await this.sessions.closeAll();
194-
195-
this.client = undefined;
196-
this.connectionProvider = undefined;
197-
this.authProvider = undefined;
216+
public async getDriver(): Promise<IDriver> {
217+
return this.driver;
198218
}
199219
}

lib/DBSQLOperation/FetchResultsHelper.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ import {
66
TRowSet,
77
} from '../../thrift/TCLIService_types';
88
import { ColumnCode, FetchType, Int64 } from '../hive/Types';
9-
import HiveDriver from '../hive/HiveDriver';
109
import Status from '../dto/Status';
10+
import IClientContext from '../contracts/IClientContext';
1111

1212
function checkIfOperationHasMoreRows(response: TFetchResultsResp): boolean {
1313
if (response.hasMoreRows) {
@@ -36,7 +36,7 @@ function checkIfOperationHasMoreRows(response: TFetchResultsResp): boolean {
3636
}
3737

3838
export default class FetchResultsHelper {
39-
private readonly driver: HiveDriver;
39+
private readonly context: IClientContext;
4040

4141
private readonly operationHandle: TOperationHandle;
4242

@@ -49,12 +49,12 @@ export default class FetchResultsHelper {
4949
public hasMoreRows: boolean = false;
5050

5151
constructor(
52-
driver: HiveDriver,
52+
context: IClientContext,
5353
operationHandle: TOperationHandle,
5454
prefetchedResults: Array<TFetchResultsResp | undefined>,
5555
returnOnlyPrefetchedResults: boolean,
5656
) {
57-
this.driver = driver;
57+
this.context = context;
5858
this.operationHandle = operationHandle;
5959
prefetchedResults.forEach((item) => {
6060
if (item) {
@@ -85,7 +85,8 @@ export default class FetchResultsHelper {
8585
return this.processFetchResponse(prefetchedResponse);
8686
}
8787

88-
const response = await this.driver.fetchResults({
88+
const driver = await this.context.getDriver();
89+
const response = await driver.fetchResults({
8990
operationHandle: this.operationHandle,
9091
orientation: this.fetchOrientation,
9192
maxRows: new Int64(maxRows),

lib/DBSQLOperation/index.ts

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import IOperation, {
55
GetSchemaOptions,
66
WaitUntilReadyOptions,
77
} from '../contracts/IOperation';
8-
import HiveDriver from '../hive/HiveDriver';
98
import {
109
TGetOperationStatusResp,
1110
TOperationHandle,
@@ -18,19 +17,22 @@ import {
1817
} from '../../thrift/TCLIService_types';
1918
import Status from '../dto/Status';
2019
import FetchResultsHelper from './FetchResultsHelper';
21-
import IDBSQLLogger, { LogLevel } from '../contracts/IDBSQLLogger';
20+
import { LogLevel } from '../contracts/IDBSQLLogger';
2221
import OperationStateError, { OperationStateErrorCode } from '../errors/OperationStateError';
2322
import IOperationResult from '../result/IOperationResult';
2423
import JsonResult from '../result/JsonResult';
2524
import ArrowResult from '../result/ArrowResult';
2625
import CloudFetchResult from '../result/CloudFetchResult';
2726
import { definedOrError } from '../utils';
2827
import HiveDriverError from '../errors/HiveDriverError';
28+
import IClientContext from '../contracts/IClientContext';
2929

3030
const defaultMaxRows = 100000;
3131

3232
interface DBSQLOperationConstructorOptions {
33-
logger: IDBSQLLogger;
33+
handle: TOperationHandle;
34+
directResults?: TSparkDirectResults;
35+
context: IClientContext;
3436
}
3537

3638
async function delay(ms?: number): Promise<void> {
@@ -42,12 +44,10 @@ async function delay(ms?: number): Promise<void> {
4244
}
4345

4446
export default class DBSQLOperation implements IOperation {
45-
private readonly driver: HiveDriver;
47+
private readonly context: IClientContext;
4648

4749
private readonly operationHandle: TOperationHandle;
4850

49-
private readonly logger: IDBSQLLogger;
50-
5151
public onClose?: () => void;
5252

5353
private readonly _data: FetchResultsHelper;
@@ -70,32 +70,26 @@ export default class DBSQLOperation implements IOperation {
7070

7171
private resultHandler?: IOperationResult;
7272

73-
constructor(
74-
driver: HiveDriver,
75-
operationHandle: TOperationHandle,
76-
{ logger }: DBSQLOperationConstructorOptions,
77-
directResults?: TSparkDirectResults,
78-
) {
79-
this.driver = driver;
80-
this.operationHandle = operationHandle;
81-
this.logger = logger;
73+
constructor({ handle, directResults, context }: DBSQLOperationConstructorOptions) {
74+
this.operationHandle = handle;
75+
this.context = context;
8276

8377
const useOnlyPrefetchedResults = Boolean(directResults?.closeOperation);
8478

85-
this.hasResultSet = operationHandle.hasResultSet;
79+
this.hasResultSet = this.operationHandle.hasResultSet;
8680
if (directResults?.operationStatus) {
8781
this.processOperationStatusResponse(directResults.operationStatus);
8882
}
8983

9084
this.metadata = directResults?.resultSetMetadata;
9185
this._data = new FetchResultsHelper(
92-
this.driver,
86+
this.context,
9387
this.operationHandle,
9488
[directResults?.resultSet],
9589
useOnlyPrefetchedResults,
9690
);
9791
this.closeOperation = directResults?.closeOperation;
98-
this.logger.log(LogLevel.debug, `Operation created with id: ${this.getId()}`);
92+
this.context.getLogger().log(LogLevel.debug, `Operation created with id: ${this.getId()}`);
9993
}
10094

10195
public getId() {
@@ -118,7 +112,7 @@ export default class DBSQLOperation implements IOperation {
118112
const chunk = await this.fetchChunk(options);
119113
data.push(chunk);
120114
} while (await this.hasMoreRows()); // eslint-disable-line no-await-in-loop
121-
this.logger?.log(LogLevel.debug, `Fetched all data from operation with id: ${this.getId()}`);
115+
this.context.getLogger().log(LogLevel.debug, `Fetched all data from operation with id: ${this.getId()}`);
122116

123117
return data.flat();
124118
}
@@ -149,10 +143,12 @@ export default class DBSQLOperation implements IOperation {
149143
await this.failIfClosed();
150144

151145
const result = await resultHandler.getValue(data ? [data] : []);
152-
this.logger?.log(
153-
LogLevel.debug,
154-
`Fetched chunk of size: ${options?.maxRows || defaultMaxRows} from operation with id: ${this.getId()}`,
155-
);
146+
this.context
147+
.getLogger()
148+
.log(
149+
LogLevel.debug,
150+
`Fetched chunk of size: ${options?.maxRows || defaultMaxRows} from operation with id: ${this.getId()}`,
151+
);
156152
return result;
157153
}
158154

@@ -163,13 +159,14 @@ export default class DBSQLOperation implements IOperation {
163159
*/
164160
public async status(progress: boolean = false): Promise<TGetOperationStatusResp> {
165161
await this.failIfClosed();
166-
this.logger?.log(LogLevel.debug, `Fetching status for operation with id: ${this.getId()}`);
162+
this.context.getLogger().log(LogLevel.debug, `Fetching status for operation with id: ${this.getId()}`);
167163

168164
if (this.operationStatus) {
169165
return this.operationStatus;
170166
}
171167

172-
const response = await this.driver.getOperationStatus({
168+
const driver = await this.context.getDriver();
169+
const response = await driver.getOperationStatus({
173170
operationHandle: this.operationHandle,
174171
getProgressUpdate: progress,
175172
});
@@ -186,9 +183,10 @@ export default class DBSQLOperation implements IOperation {
186183
return Status.success();
187184
}
188185

189-
this.logger?.log(LogLevel.debug, `Cancelling operation with id: ${this.getId()}`);
186+
this.context.getLogger().log(LogLevel.debug, `Cancelling operation with id: ${this.getId()}`);
190187

191-
const response = await this.driver.cancelOperation({
188+
const driver = await this.context.getDriver();
189+
const response = await driver.cancelOperation({
192190
operationHandle: this.operationHandle,
193191
});
194192
Status.assert(response.status);
@@ -209,11 +207,12 @@ export default class DBSQLOperation implements IOperation {
209207
return Status.success();
210208
}
211209

212-
this.logger?.log(LogLevel.debug, `Closing operation with id: ${this.getId()}`);
210+
this.context.getLogger().log(LogLevel.debug, `Closing operation with id: ${this.getId()}`);
213211

212+
const driver = await this.context.getDriver();
214213
const response =
215214
this.closeOperation ??
216-
(await this.driver.closeOperation({
215+
(await driver.closeOperation({
217216
operationHandle: this.operationHandle,
218217
}));
219218
Status.assert(response.status);
@@ -254,7 +253,7 @@ export default class DBSQLOperation implements IOperation {
254253

255254
await this.waitUntilReady(options);
256255

257-
this.logger?.log(LogLevel.debug, `Fetching schema for operation with id: ${this.getId()}`);
256+
this.context.getLogger().log(LogLevel.debug, `Fetching schema for operation with id: ${this.getId()}`);
258257
const metadata = await this.fetchMetadata();
259258
return metadata.schema ?? null;
260259
}
@@ -332,7 +331,8 @@ export default class DBSQLOperation implements IOperation {
332331

333332
private async fetchMetadata() {
334333
if (!this.metadata) {
335-
const metadata = await this.driver.getResultSetMetadata({
334+
const driver = await this.context.getDriver();
335+
const metadata = await driver.getResultSetMetadata({
336336
operationHandle: this.operationHandle,
337337
});
338338
Status.assert(metadata.status);
@@ -349,13 +349,13 @@ export default class DBSQLOperation implements IOperation {
349349
if (!this.resultHandler) {
350350
switch (resultFormat) {
351351
case TSparkRowSetType.COLUMN_BASED_SET:
352-
this.resultHandler = new JsonResult(metadata.schema);
352+
this.resultHandler = new JsonResult(this.context, metadata.schema);
353353
break;
354354
case TSparkRowSetType.ARROW_BASED_SET:
355-
this.resultHandler = new ArrowResult(metadata.schema, metadata.arrowSchema);
355+
this.resultHandler = new ArrowResult(this.context, metadata.schema, metadata.arrowSchema);
356356
break;
357357
case TSparkRowSetType.URL_BASED_SET:
358-
this.resultHandler = new CloudFetchResult(metadata.schema);
358+
this.resultHandler = new CloudFetchResult(this.context, metadata.schema);
359359
break;
360360
default:
361361
this.resultHandler = undefined;

0 commit comments

Comments
 (0)