Skip to content

Commit 3c29fe2

Browse files
Convert unit tests to Typescript (#258)
* Convert unit tests to Typescript Signed-off-by: Levko Kravets <levko.ne@gmail.com> * Polish & cleanup Signed-off-by: Levko Kravets <levko.ne@gmail.com> * Polish & cleanup Signed-off-by: Levko Kravets <levko.ne@gmail.com> --------- Signed-off-by: Levko Kravets <levko.ne@gmail.com>
1 parent 3eed509 commit 3c29fe2

File tree

142 files changed

+6089
-5851
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

142 files changed

+6089
-5851
lines changed

lib/DBSQLClient.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { TProtocolVersion } from '../thrift/TCLIService_types';
77
import IDBSQLClient, { ClientOptions, ConnectionOptions, OpenSessionRequest } from './contracts/IDBSQLClient';
88
import IDriver from './contracts/IDriver';
99
import IClientContext, { ClientConfig } from './contracts/IClientContext';
10+
import IThriftClient from './contracts/IThriftClient';
1011
import HiveDriver from './hive/HiveDriver';
1112
import DBSQLSession from './DBSQLSession';
1213
import IDBSQLSession from './contracts/IDBSQLSession';
@@ -43,6 +44,8 @@ function getInitialNamespaceOptions(catalogName?: string, schemaName?: string) {
4344
};
4445
}
4546

47+
export type ThriftLibrary = Pick<typeof thrift, 'createClient'>;
48+
4649
export default class DBSQLClient extends EventEmitter implements IDBSQLClient, IClientContext {
4750
private static defaultLogger?: IDBSQLLogger;
4851

@@ -52,17 +55,17 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I
5255

5356
private authProvider?: IAuthentication;
5457

55-
private client?: TCLIService.Client;
58+
private client?: IThriftClient;
5659

5760
private readonly driver = new HiveDriver({
5861
context: this,
5962
});
6063

6164
private readonly logger: IDBSQLLogger;
6265

63-
private readonly thrift = thrift;
66+
private thrift: ThriftLibrary = thrift;
6467

65-
private sessions = new CloseableCollection<DBSQLSession>();
68+
private readonly sessions = new CloseableCollection<DBSQLSession>();
6669

6770
private static getDefaultLogger(): IDBSQLLogger {
6871
if (!this.defaultLogger) {
@@ -113,7 +116,7 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I
113116
};
114117
}
115118

116-
private initAuthProvider(options: ConnectionOptions, authProvider?: IAuthentication): IAuthentication {
119+
private createAuthProvider(options: ConnectionOptions, authProvider?: IAuthentication): IAuthentication {
117120
if (authProvider) {
118121
return authProvider;
119122
}
@@ -143,6 +146,10 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I
143146
}
144147
}
145148

149+
private createConnectionProvider(options: ConnectionOptions): IConnectionProvider {
150+
return new HttpConnection(this.getConnectionOptions(options), this);
151+
}
152+
146153
/**
147154
* Connects DBSQLClient to endpoint
148155
* @public
@@ -153,9 +160,9 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I
153160
* const session = client.connect({host, path, token});
154161
*/
155162
public async connect(options: ConnectionOptions, authProvider?: IAuthentication): Promise<IDBSQLClient> {
156-
this.authProvider = this.initAuthProvider(options, authProvider);
163+
this.authProvider = this.createAuthProvider(options, authProvider);
157164

158-
this.connectionProvider = new HttpConnection(this.getConnectionOptions(options), this);
165+
this.connectionProvider = this.createConnectionProvider(options);
159166

160167
const thriftConnection = await this.connectionProvider.getThriftConnection();
161168

@@ -238,7 +245,7 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I
238245
return this.connectionProvider;
239246
}
240247

241-
public async getClient(): Promise<TCLIService.Client> {
248+
public async getClient(): Promise<IThriftClient> {
242249
const connectionProvider = await this.getConnectionProvider();
243250

244251
if (!this.client) {

lib/DBSQLOperation.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export default class DBSQLOperation implements IOperation {
6464

6565
private metadata?: TGetResultSetMetadataResp;
6666

67-
private state: number = TOperationState.INITIALIZED_STATE;
67+
private state: TOperationState = TOperationState.INITIALIZED_STATE;
6868

6969
// Once operation is finished or fails - cache status response, because subsequent calls
7070
// to `getOperationStatus()` may fail with irrelevant errors, e.g. HTTP 404

lib/connection/auth/DatabricksOAuth/AuthorizationCode.ts

Lines changed: 60 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -6,50 +6,19 @@ import { OAuthScopes, scopeDelimiter } from './OAuthScope';
66
import IClientContext from '../../../contracts/IClientContext';
77
import AuthenticationError from '../../../errors/AuthenticationError';
88

9+
export type DefaultOpenAuthUrlCallback = (authUrl: string) => Promise<void>;
10+
11+
export type OpenAuthUrlCallback = (authUrl: string, defaultOpenAuthUrl: DefaultOpenAuthUrlCallback) => Promise<void>;
12+
913
export interface AuthorizationCodeOptions {
1014
client: BaseClient;
1115
ports: Array<number>;
1216
context: IClientContext;
17+
openAuthUrl?: OpenAuthUrlCallback;
1318
}
1419

15-
async function startServer(
16-
host: string,
17-
port: number,
18-
requestHandler: (req: IncomingMessage, res: ServerResponse) => void,
19-
): Promise<Server> {
20-
const server = http.createServer(requestHandler);
21-
22-
return new Promise((resolve, reject) => {
23-
const errorListener = (error: Error) => {
24-
server.off('error', errorListener);
25-
reject(error);
26-
};
27-
28-
server.on('error', errorListener);
29-
server.listen(port, host, () => {
30-
server.off('error', errorListener);
31-
resolve(server);
32-
});
33-
});
34-
}
35-
36-
async function stopServer(server: Server): Promise<void> {
37-
if (!server.listening) {
38-
return;
39-
}
40-
41-
return new Promise((resolve, reject) => {
42-
const errorListener = (error: Error) => {
43-
server.off('error', errorListener);
44-
reject(error);
45-
};
46-
47-
server.on('error', errorListener);
48-
server.close(() => {
49-
server.off('error', errorListener);
50-
resolve();
51-
});
52-
});
20+
async function defaultOpenAuthUrl(authUrl: string): Promise<void> {
21+
await open(authUrl);
5322
}
5423

5524
export interface AuthorizationCodeFetchResult {
@@ -65,16 +34,12 @@ export default class AuthorizationCode {
6534

6635
private readonly host: string = 'localhost';
6736

68-
private readonly ports: Array<number>;
37+
private readonly options: AuthorizationCodeOptions;
6938

7039
constructor(options: AuthorizationCodeOptions) {
7140
this.client = options.client;
72-
this.ports = options.ports;
7341
this.context = options.context;
74-
}
75-
76-
private async openUrl(url: string) {
77-
return open(url);
42+
this.options = options;
7843
}
7944

8045
public async fetch(scopes: OAuthScopes): Promise<AuthorizationCodeFetchResult> {
@@ -84,7 +49,7 @@ export default class AuthorizationCode {
8449

8550
let receivedParams: CallbackParamsType | undefined;
8651

87-
const server = await this.startServer((req, res) => {
52+
const server = await this.createServer((req, res) => {
8853
const params = this.client.callbackParams(req);
8954
if (params.state === state) {
9055
receivedParams = params;
@@ -108,7 +73,8 @@ export default class AuthorizationCode {
10873
redirect_uri: redirectUri,
10974
});
11075

111-
await this.openUrl(authUrl);
76+
const openAuthUrl = this.options.openAuthUrl ?? defaultOpenAuthUrl;
77+
await openAuthUrl(authUrl, defaultOpenAuthUrl);
11278
await server.stopped();
11379

11480
if (!receivedParams || !receivedParams.code) {
@@ -122,11 +88,11 @@ export default class AuthorizationCode {
12288
return { code: receivedParams.code, verifier: verifierString, redirectUri };
12389
}
12490

125-
private async startServer(requestHandler: (req: IncomingMessage, res: ServerResponse) => void) {
126-
for (const port of this.ports) {
91+
private async createServer(requestHandler: (req: IncomingMessage, res: ServerResponse) => void) {
92+
for (const port of this.options.ports) {
12793
const host = this.host; // eslint-disable-line prefer-destructuring
12894
try {
129-
const server = await startServer(host, port, requestHandler); // eslint-disable-line no-await-in-loop
95+
const server = await this.startServer(host, port, requestHandler); // eslint-disable-line no-await-in-loop
13096
this.context.getLogger().log(LogLevel.info, `Listening for OAuth authorization callback at ${host}:${port}`);
13197

13298
let resolveStopped: () => void;
@@ -140,7 +106,7 @@ export default class AuthorizationCode {
140106
host,
141107
port,
142108
server,
143-
stop: () => stopServer(server).then(resolveStopped).catch(rejectStopped),
109+
stop: () => this.stopServer(server).then(resolveStopped).catch(rejectStopped),
144110
stopped: () => stoppedPromise,
145111
};
146112
} catch (error) {
@@ -156,6 +122,50 @@ export default class AuthorizationCode {
156122
throw new AuthenticationError('Failed to start server: all ports are in use');
157123
}
158124

125+
private createHttpServer(requestHandler: (req: IncomingMessage, res: ServerResponse) => void) {
126+
return http.createServer(requestHandler);
127+
}
128+
129+
private async startServer(
130+
host: string,
131+
port: number,
132+
requestHandler: (req: IncomingMessage, res: ServerResponse) => void,
133+
): Promise<Server> {
134+
const server = this.createHttpServer(requestHandler);
135+
136+
return new Promise((resolve, reject) => {
137+
const errorListener = (error: Error) => {
138+
server.off('error', errorListener);
139+
reject(error);
140+
};
141+
142+
server.on('error', errorListener);
143+
server.listen(port, host, () => {
144+
server.off('error', errorListener);
145+
resolve(server);
146+
});
147+
});
148+
}
149+
150+
private async stopServer(server: Server): Promise<void> {
151+
if (!server.listening) {
152+
return;
153+
}
154+
155+
return new Promise((resolve, reject) => {
156+
const errorListener = (error: Error) => {
157+
server.off('error', errorListener);
158+
reject(error);
159+
};
160+
161+
server.on('error', errorListener);
162+
server.close(() => {
163+
server.off('error', errorListener);
164+
resolve();
165+
});
166+
});
167+
}
168+
159169
private renderCallbackResponse(): string {
160170
const applicationName = 'Databricks Sql Connector';
161171

lib/connection/auth/DatabricksOAuth/index.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import IClientContext from '../../../contracts/IClientContext';
77

88
export { OAuthFlow };
99

10-
interface DatabricksOAuthOptions extends OAuthManagerOptions {
10+
export interface DatabricksOAuthOptions extends OAuthManagerOptions {
1111
scopes?: OAuthScopes;
1212
persistence?: OAuthPersistence;
1313
headers?: HeadersInit;
@@ -18,14 +18,13 @@ export default class DatabricksOAuth implements IAuthentication {
1818

1919
private readonly options: DatabricksOAuthOptions;
2020

21-
private readonly manager: OAuthManager;
21+
private manager?: OAuthManager;
2222

2323
private readonly defaultPersistence = new OAuthPersistenceCache();
2424

2525
constructor(options: DatabricksOAuthOptions) {
2626
this.context = options.context;
2727
this.options = options;
28-
this.manager = OAuthManager.getManager(this.options);
2928
}
3029

3130
public async authenticate(): Promise<HeadersInit> {
@@ -35,15 +34,22 @@ export default class DatabricksOAuth implements IAuthentication {
3534

3635
let token = await persistence.read(host);
3736
if (!token) {
38-
token = await this.manager.getToken(scopes ?? defaultOAuthScopes);
37+
token = await this.getManager().getToken(scopes ?? defaultOAuthScopes);
3938
}
4039

41-
token = await this.manager.refreshAccessToken(token);
40+
token = await this.getManager().refreshAccessToken(token);
4241
await persistence.persist(host, token);
4342

4443
return {
4544
...headers,
4645
Authorization: `Bearer ${token.accessToken}`,
4746
};
4847
}
48+
49+
private getManager(): OAuthManager {
50+
if (!this.manager) {
51+
this.manager = OAuthManager.getManager(this.options);
52+
}
53+
return this.manager;
54+
}
4955
}

lib/connection/connections/HttpConnection.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ export default class HttpConnection implements IConnectionProvider {
3636
});
3737
}
3838

39-
public async getAgent(): Promise<http.Agent> {
39+
public async getAgent(): Promise<http.Agent | undefined> {
4040
if (!this.agent) {
4141
if (this.options.proxy !== undefined) {
4242
this.agent = this.createProxyAgent(this.options.proxy);

lib/connection/connections/HttpRetryPolicy.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ function delay(milliseconds: number): Promise<void> {
1212
export default class HttpRetryPolicy implements IRetryPolicy<HttpTransactionDetails> {
1313
private context: IClientContext;
1414

15-
private readonly startTime: number; // in milliseconds
15+
private startTime: number; // in milliseconds
1616

1717
private attempt: number;
1818

lib/connection/contracts/IConnectionProvider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export interface HttpTransactionDetails {
1010
export default interface IConnectionProvider {
1111
getThriftConnection(): Promise<any>;
1212

13-
getAgent(): Promise<http.Agent>;
13+
getAgent(): Promise<http.Agent | undefined>;
1414

1515
setHeaders(headers: HeadersInit): void;
1616

lib/contracts/IClientContext.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import IDBSQLLogger from './IDBSQLLogger';
22
import IDriver from './IDriver';
33
import IConnectionProvider from '../connection/contracts/IConnectionProvider';
4-
import TCLIService from '../../thrift/TCLIService';
4+
import IThriftClient from './IThriftClient';
55

66
export interface ClientConfig {
77
directResultsDefaultMaxRows: number;
@@ -29,7 +29,7 @@ export default interface IClientContext {
2929

3030
getConnectionProvider(): Promise<IConnectionProvider>;
3131

32-
getClient(): Promise<TCLIService.Client>;
32+
getClient(): Promise<IThriftClient>;
3333

3434
getDriver(): Promise<IDriver>;
3535
}

lib/contracts/IThriftClient.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import TCLIService from '../../thrift/TCLIService';
2+
3+
type ThriftClient = TCLIService.Client;
4+
5+
type ThriftClientMethods = {
6+
[K in keyof ThriftClient]: ThriftClient[K];
7+
};
8+
9+
export default interface IThriftClient extends ThriftClientMethods {}

lib/hive/Commands/BaseCommand.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,14 @@
11
import { Response } from 'node-fetch';
2-
import TCLIService from '../../../thrift/TCLIService';
32
import HiveDriverError from '../../errors/HiveDriverError';
43
import RetryError, { RetryErrorCode } from '../../errors/RetryError';
54
import IClientContext from '../../contracts/IClientContext';
65

7-
export default abstract class BaseCommand {
8-
protected client: TCLIService.Client;
6+
export default abstract class BaseCommand<ClientType> {
7+
protected client: ClientType;
98

109
protected context: IClientContext;
1110

12-
constructor(client: TCLIService.Client, context: IClientContext) {
11+
constructor(client: ClientType, context: IClientContext) {
1312
this.client = client;
1413
this.context = context;
1514
}

lib/hive/Commands/CancelDelegationTokenCommand.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import BaseCommand from './BaseCommand';
22
import { TCancelDelegationTokenReq, TCancelDelegationTokenResp } from '../../../thrift/TCLIService_types';
3+
import IThriftClient from '../../contracts/IThriftClient';
34

4-
export default class CancelDelegationTokenCommand extends BaseCommand {
5+
type Client = Pick<IThriftClient, 'CancelDelegationToken'>;
6+
7+
export default class CancelDelegationTokenCommand extends BaseCommand<Client> {
58
execute(data: TCancelDelegationTokenReq): Promise<TCancelDelegationTokenResp> {
69
const request = new TCancelDelegationTokenReq(data);
710

0 commit comments

Comments
 (0)