Skip to content

Commit 21e3087

Browse files
samikshya-dbclaude
andcommitted
Add authentication support for REST API calls
Implements getAuthHeaders() method for authenticated REST API requests: - Added getAuthHeaders() to IClientContext interface - Implemented in DBSQLClient using authProvider.authenticate() - Updated FeatureFlagCache to fetch from connector-service API with auth - Added driver version support for version-specific feature flags - Replaced placeholder implementation with actual REST API calls Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 5885c86 commit 21e3087

File tree

3 files changed

+91
-11
lines changed

3 files changed

+91
-11
lines changed

lib/DBSQLClient.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import thrift from 'thrift';
22
import Int64 from 'node-int64';
33

44
import { EventEmitter } from 'events';
5+
import { HeadersInit } from 'node-fetch';
56
import TCLIService from '../thrift/TCLIService';
67
import { TProtocolVersion } from '../thrift/TCLIService_types';
78
import IDBSQLClient, { ClientOptions, ConnectionOptions, OpenSessionRequest } from './contracts/IDBSQLClient';
@@ -291,4 +292,16 @@ export default class DBSQLClient extends EventEmitter implements IDBSQLClient, I
291292
public async getDriver(): Promise<IDriver> {
292293
return this.driver;
293294
}
295+
296+
public async getAuthHeaders(): Promise<HeadersInit> {
297+
if (this.authProvider) {
298+
try {
299+
return await this.authProvider.authenticate();
300+
} catch (error) {
301+
this.logger.log(LogLevel.debug, `Error getting auth headers: ${error}`);
302+
return {};
303+
}
304+
}
305+
return {};
306+
}
294307
}

lib/contracts/IClientContext.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { HeadersInit } from 'node-fetch';
12
import IDBSQLLogger from './IDBSQLLogger';
23
import IDriver from './IDriver';
34
import IConnectionProvider from '../connection/contracts/IConnectionProvider';
@@ -43,4 +44,11 @@ export default interface IClientContext {
4344
getClient(): Promise<IThriftClient>;
4445

4546
getDriver(): Promise<IDriver>;
47+
48+
/**
49+
* Gets authentication headers for HTTP requests.
50+
* Used by telemetry and feature flag fetching to authenticate REST API calls.
51+
* @returns Promise resolving to headers object with authentication, or empty object if no auth
52+
*/
53+
getAuthHeaders(): Promise<HeadersInit>;
4654
}

lib/telemetry/FeatureFlagCache.ts

Lines changed: 70 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import fetch from 'node-fetch';
1718
import IClientContext from '../contracts/IClientContext';
1819
import { LogLevel } from '../contracts/IDBSQLLogger';
1920

@@ -104,17 +105,75 @@ export default class FeatureFlagCache {
104105
}
105106

106107
/**
107-
* Fetches feature flag from server.
108-
* This is a placeholder implementation that returns false.
109-
* Real implementation would fetch from server using connection provider.
110-
* @param _host The host to fetch feature flag for (unused in placeholder implementation)
108+
* Gets the driver version from package.json.
109+
* Used for version-specific feature flag requests.
111110
*/
112-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
113-
private async fetchFeatureFlag(_host: string): Promise<boolean> {
114-
// Placeholder implementation
115-
// Real implementation would use:
116-
// const connectionProvider = await this.context.getConnectionProvider();
117-
// and make an API call to fetch the feature flag
118-
return false;
111+
private getDriverVersion(): string {
112+
try {
113+
// eslint-disable-next-line @typescript-eslint/no-var-requires
114+
const packageJson = require('../../package.json');
115+
return packageJson.version || 'unknown';
116+
} catch {
117+
return 'unknown';
118+
}
119+
}
120+
121+
/**
122+
* Fetches feature flag from server REST API.
123+
* Makes authenticated call to connector-service endpoint.
124+
* @param host The host to fetch feature flag for
125+
*/
126+
private async fetchFeatureFlag(host: string): Promise<boolean> {
127+
const logger = this.context.getLogger();
128+
try {
129+
const driverVersion = this.getDriverVersion();
130+
const endpoint = `https://${host}/api/2.0/connector-service/feature-flags/OSS_NODEJS/${driverVersion}`;
131+
132+
// Get authentication headers
133+
const authHeaders = await this.context.getAuthHeaders();
134+
135+
logger.log(LogLevel.debug, `Fetching feature flag from ${endpoint}`);
136+
137+
const response = await fetch(endpoint, {
138+
method: 'GET',
139+
headers: {
140+
...authHeaders,
141+
'Content-Type': 'application/json',
142+
'User-Agent': `databricks-sql-nodejs/${driverVersion}`,
143+
},
144+
});
145+
146+
if (!response.ok) {
147+
logger.log(LogLevel.debug, `Feature flag fetch returned status ${response.status}`);
148+
return false;
149+
}
150+
151+
const data: any = await response.json();
152+
153+
// Update cache duration from ttl_seconds if provided
154+
if (data && data.ttl_seconds) {
155+
const ctx = this.contexts.get(host);
156+
if (ctx) {
157+
ctx.cacheDuration = data.ttl_seconds * 1000;
158+
logger.log(LogLevel.debug, `Updated cache duration to ${data.ttl_seconds} seconds`);
159+
}
160+
}
161+
162+
// Find the telemetry flag
163+
if (data && data.flags && Array.isArray(data.flags)) {
164+
const flag = data.flags.find((f: any) => f.name === this.FEATURE_FLAG_NAME);
165+
if (flag) {
166+
const enabled = String(flag.value).toLowerCase() === 'true';
167+
logger.log(LogLevel.debug, `Feature flag ${this.FEATURE_FLAG_NAME} = ${enabled}`);
168+
return enabled;
169+
}
170+
}
171+
172+
logger.log(LogLevel.debug, `Feature flag ${this.FEATURE_FLAG_NAME} not found in response`);
173+
return false;
174+
} catch (error: any) {
175+
logger.log(LogLevel.debug, `Error fetching feature flag from ${host}: ${error.message}`);
176+
return false;
177+
}
119178
}
120179
}

0 commit comments

Comments
 (0)