Skip to content

Commit 9e75eff

Browse files
authored
Added unified User-Agent HTTP header (#52)
* Implements concepts of _product name_ and _product version_. * Dynamically fetches VSCode extension version. * Dynamically tracks SDK version. * Dynamically tracks NodeJS version. * Dynamically tracks OS name.
1 parent 730584c commit 9e75eff

4 files changed

Lines changed: 52 additions & 5 deletions

File tree

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/* eslint-disable @typescript-eslint/naming-convention */
2+
3+
import assert from "node:assert";
4+
import {ApiClient} from "./api-client";
5+
6+
const sdkVersion = require("../package.json").version;
7+
8+
describe(__filename, () => {
9+
beforeEach(() => {
10+
delete process.env.DATABRICKS_CONFIG_FILE;
11+
});
12+
13+
it("should create proper user agent", () => {
14+
let ua = new ApiClient("unit", "3.4.5").userAgent();
15+
assert.equal(
16+
ua,
17+
`unit/3.4.5 databricks-sdk-js/${sdkVersion} nodejs/${process.version.slice(
18+
1
19+
)} os/${process.platform}`
20+
);
21+
});
22+
});

packages/databricks-sdk-js/src/api-client.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import fetch from "node-fetch";
33
import {TextDecoder} from "node:util";
44
import {fromDefaultChain} from "./auth/fromChain";
55

6+
const sdkVersion = require("../package.json").version;
7+
68
type HttpMethod = "POST" | "GET";
79

810
export class HttpError extends Error {
@@ -16,7 +18,23 @@ export class HttpError extends Error {
1618
}
1719

1820
export class ApiClient {
19-
constructor(private credentialProvider = fromDefaultChain) {}
21+
constructor(
22+
private readonly product: string,
23+
private readonly productVersion: string,
24+
private credentialProvider = fromDefaultChain
25+
) {}
26+
27+
userAgent(): string {
28+
let pairs = [
29+
`${this.product}/${this.productVersion}`,
30+
`databricks-sdk-js/${sdkVersion}`,
31+
`nodejs/${process.version.slice(1)}`,
32+
`os/${process.platform}`,
33+
];
34+
// TODO: add ability of per-request extra-information,
35+
// so that we can track sub-functionality, like in Terraform
36+
return pairs.join(" ");
37+
}
2038

2139
async request(
2240
path: string,
@@ -26,7 +44,7 @@ export class ApiClient {
2644
const credentials = await this.credentialProvider();
2745
const headers = {
2846
"Authorization": `Bearer ${credentials.token}`,
29-
"User-Agent": `vscode-notebook`,
47+
"User-Agent": this.userAgent(),
3048
"Content-Type": "text/json",
3149
};
3250

packages/databricks-sdk-js/src/test/IntegrationTestSetup.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ export class IntegrationTestSetup {
1515
private static _instance: IntegrationTestSetup;
1616
static async getInstance(): Promise<IntegrationTestSetup> {
1717
if (!this._instance) {
18-
let client = new ApiClient();
18+
let client = new ApiClient("integration-tests", "0.0.1");
1919

2020
if (!process.env["TEST_DEFAULT_CLUSTER_ID"]) {
2121
throw new Error(

packages/databricks-vscode/src/configuration/ConnectionManager.ts

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import {
33
Cluster,
44
fromConfigFile,
55
ScimService,
6+
CredentialProvider,
67
} from "@databricks/databricks-sdk";
78
import {
89
commands,
@@ -16,6 +17,8 @@ import {PathMapper} from "./PathMapper";
1617
import {ProjectConfigFile} from "./ProjectConfigFile";
1718
import {selectProfile} from "./selectProfileWizard";
1819

20+
const extensionVersion = require("../../package.json").version;
21+
1922
export type ConnectionState = "CONNECTED" | "CONNECTING" | "DISCONNECTED";
2023

2124
/**
@@ -68,6 +71,10 @@ export class ConnectionManager {
6871
return this._apiClient;
6972
}
7073

74+
private apiClientFrom(creds: CredentialProvider): ApiClient {
75+
return new ApiClient("vscode-extension", extensionVersion, creds);
76+
}
77+
7178
async login(interactive: boolean = false): Promise<void> {
7279
await this.logout();
7380

@@ -97,7 +104,7 @@ export class ConnectionManager {
97104

98105
await credentialProvider();
99106

100-
apiClient = new ApiClient(credentialProvider);
107+
apiClient = this.apiClientFrom(credentialProvider);
101108
this._me = await this.getMe(apiClient);
102109
} catch (e: any) {
103110
const message = `Can't login to Databricks: ${e.message}`;
@@ -149,7 +156,7 @@ export class ConnectionManager {
149156
}
150157

151158
try {
152-
await this.getMe(new ApiClient(fromConfigFile(profile)));
159+
await this.getMe(this.apiClientFrom(fromConfigFile(profile)));
153160
} catch (e: any) {
154161
console.error(e);
155162
const response = await window.showWarningMessage(

0 commit comments

Comments
 (0)