Skip to content

Commit c351530

Browse files
authored
SDK: Add Repos API (#60)
1 parent 51173e1 commit c351530

File tree

12 files changed

+393
-16
lines changed

12 files changed

+393
-16
lines changed

packages/databricks-sdk-js/.vscode/launch.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,7 @@
1010
"request": "launch",
1111
"runtimeArgs": ["run", "test:unit"],
1212
"runtimeExecutable": "npm",
13-
"cwd": "${workspaceFolder}/packages/databricks-sdk-js",
14-
"outFiles": [
15-
"${workspaceFolder}/packages/databricks-sdk-js/**/*.js"
16-
],
13+
"outFiles": ["${workspaceFolder}/**/*.js", "!**/node_modules/**"],
1714
"type": "node"
1815
}
1916
]

packages/databricks-sdk-js/package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,10 @@
2222
"clean": "rm -rf dist node_modules",
2323
"fix": "prettier . --write",
2424
"test:lint": "eslint src --ext ts && prettier . -c",
25-
"test:run": "ts-mocha --type-check 'src/**/*.test.ts'",
26-
"test": "yarn run test:lint && yarn run test:run",
25+
"test:unit": "ts-mocha --type-check 'src/**/*.test.ts'",
26+
"test": "yarn run test:lint && yarn run test:unit",
2727
"test:integ": "ts-mocha --type-check 'src/**/*.integ.ts'",
28-
"test:cov": "nyc --report-dir ./coverage/unit yarn run test:run",
28+
"test:cov": "nyc --report-dir ./coverage/unit yarn run test:unit",
2929
"test:integ:cov": "nyc --report-dir ./coverage/integration yarn run test:integ"
3030
},
3131
"dependencies": {
@@ -70,4 +70,4 @@
7070
],
7171
"report-dir": "coverage"
7272
}
73-
}
73+
}

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
/* eslint-disable @typescript-eslint/naming-convention */
22
import fetch from "node-fetch";
3+
import * as https from "node:https";
34
import {TextDecoder} from "node:util";
45
import {fromDefaultChain} from "./auth/fromChain";
56

67
const sdkVersion = require("../package.json").version;
78

8-
type HttpMethod = "POST" | "GET";
9+
type HttpMethod = "POST" | "GET" | "DELETE" | "PATCH";
910

1011
export class HttpError extends Error {
1112
constructor(
@@ -18,11 +19,18 @@ export class HttpError extends Error {
1819
}
1920

2021
export class ApiClient {
22+
private agent: https.Agent;
23+
2124
constructor(
2225
private readonly product: string,
2326
private readonly productVersion: string,
2427
private credentialProvider = fromDefaultChain
25-
) {}
28+
) {
29+
this.agent = new https.Agent({
30+
keepAlive: true,
31+
keepAliveMsecs: 15_000,
32+
});
33+
}
2634

2735
userAgent(): string {
2836
let pairs = [
@@ -54,6 +62,7 @@ export class ApiClient {
5462
let options: any = {
5563
method,
5664
headers,
65+
agent: this.agent,
5766
};
5867

5968
if (payload) {
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/* eslint-disable @typescript-eslint/naming-convention */
2+
import {ApiClient} from "../api-client";
3+
4+
export interface GetRepoResponse {
5+
/**
6+
* ID of the repo object in the workspace.
7+
*/
8+
id: number;
9+
10+
/**
11+
* URL of the Git repository to be linked.
12+
*/
13+
url?: string;
14+
15+
/**
16+
* Git provider. This field is case-insensitive. The available Git providers are gitHub, bitbucketCloud, gitLab, azureDevOpsServices, gitHubEnterprise, bitbucketServer, gitLabEnterpriseEdition and awsCodeCommit.
17+
*/
18+
provider?: string;
19+
20+
/**
21+
* Desired path for the repo in the workspace. Must be in the format /Repos/{folder}/{repo-name}.
22+
*/
23+
path: string;
24+
25+
/**
26+
* Branch that the local version of the repo is checked out to.
27+
*/
28+
branch?: string;
29+
30+
/**
31+
* SHA-1 hash representing the commit ID of the current HEAD of the repo.
32+
*/
33+
head_commit_id?: string;
34+
}
35+
36+
export interface GetReposRequest {
37+
path_prefix?: string;
38+
next_page_token?: string;
39+
}
40+
41+
export interface GetReposResponse {
42+
repos: Array<GetRepoResponse>;
43+
next_page_token?: string;
44+
}
45+
46+
export interface CreateRepoRequest {
47+
/** URL of the Git repository to be linked. */
48+
url: string;
49+
provider: string;
50+
path?: string;
51+
}
52+
53+
export interface DeleteRepoRequest {
54+
id: number;
55+
}
56+
57+
export interface DeleteRepoResponse {}
58+
59+
export interface GetRepoRequest {
60+
id: number;
61+
}
62+
63+
export interface UpdateRepoRequest {
64+
id: number;
65+
66+
/** Branch that the local version of the repo is checked out to. */
67+
branch?: string;
68+
69+
/**
70+
* Tag that the local version of the repo is checked out to. Updating the
71+
* repo to a tag puts the repo in a detached HEAD state. Before committing
72+
* new changes, you must update the repo to a branch instead of the
73+
* detached HEAD.
74+
*/
75+
tag?: string;
76+
}
77+
78+
/**
79+
* The repos API allows users to manage their
80+
* [repos](https://docs.databricks.com/repos.html). Users can use the API to
81+
* access all repos that they have manage permissions on.
82+
*/
83+
export class ReposService {
84+
constructor(readonly client: ApiClient) {}
85+
86+
/**
87+
* Returns repos that the calling user has Manage permissions on. Results
88+
* are paginated with each page containing twenty repos.
89+
*/
90+
async getRepos(req: GetReposRequest): Promise<GetReposResponse> {
91+
return (await this.client.request(
92+
"/api/2.0/repos",
93+
"GET",
94+
req
95+
)) as GetReposResponse;
96+
}
97+
98+
/**
99+
* Creates a repo in the workspace and links it to the remote Git repo
100+
* specified. Note that repos created programmatically must be linked to a
101+
* remote Git repo, unlike repos created in the browser.
102+
*/
103+
async createRepo(req: CreateRepoRequest): Promise<GetRepoResponse> {
104+
return (await this.client.request(
105+
"/api/2.0/repos",
106+
"POST",
107+
req
108+
)) as GetRepoResponse;
109+
}
110+
111+
/**
112+
* Returns the repo with the given repo ID.
113+
*/
114+
async getRepo(req: GetRepoRequest): Promise<GetRepoResponse> {
115+
return (await this.client.request(
116+
`/api/2.0/repos/${req.id}`,
117+
"GET",
118+
req
119+
)) as GetRepoResponse;
120+
}
121+
122+
/**
123+
* Updates the repo to a different branch or tag, or updates the repo to
124+
* the latest commit on the same branch.
125+
*/
126+
async updateRepo(req: UpdateRepoRequest): Promise<GetRepoResponse> {
127+
return (await this.client.request(
128+
`/api/2.0/repos/${req.id}`,
129+
"PATCH",
130+
req
131+
)) as GetRepoResponse;
132+
}
133+
134+
/**
135+
* Deletes the specified repo
136+
*/
137+
async deleteRepo(req: DeleteRepoRequest): Promise<DeleteRepoResponse> {
138+
return (await this.client.request(
139+
`/api/2.0/repos/${req.id}`,
140+
"DELETE",
141+
req
142+
)) as DeleteRepoResponse;
143+
}
144+
}
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/* eslint-disable @typescript-eslint/naming-convention */
2+
3+
import assert from "node:assert";
4+
import {GetReposRequest, GetReposResponse} from "./apis/repos";
5+
import {paginated} from "./decorators";
6+
import {CancellationToken} from "./types";
7+
8+
describe(__filename, () => {
9+
it("should paginate", async () => {
10+
class Test {
11+
public count = 0;
12+
13+
@paginated("next_page_token", "repos")
14+
async getRepos(req: GetReposRequest): Promise<GetReposResponse> {
15+
this.count += 1;
16+
if (this.count === 3) {
17+
return {
18+
repos: [
19+
{
20+
id: this.count,
21+
path: "/",
22+
},
23+
],
24+
};
25+
}
26+
return {
27+
repos: [
28+
{
29+
id: this.count,
30+
path: "/",
31+
},
32+
],
33+
next_page_token: "next_page_token",
34+
};
35+
}
36+
}
37+
38+
const t = new Test();
39+
const response = await t.getRepos({});
40+
assert.equal(t.count, 3);
41+
assert.equal(response.repos.length, 3);
42+
});
43+
44+
it("should cancel", async () => {
45+
const token = {
46+
isCancellationRequested: false,
47+
};
48+
49+
class Test {
50+
public count = 0;
51+
52+
@paginated("next_page_token", "repos")
53+
async getRepos(
54+
req: GetReposRequest,
55+
_token: CancellationToken
56+
): Promise<GetReposResponse> {
57+
this.count += 1;
58+
if (this.count === 3) {
59+
token.isCancellationRequested = true;
60+
}
61+
return {
62+
repos: [
63+
{
64+
id: this.count,
65+
path: "/",
66+
},
67+
],
68+
next_page_token: "next_page_token",
69+
};
70+
}
71+
}
72+
73+
const t = new Test();
74+
const response = await t.getRepos({}, token);
75+
assert.equal(t.count, 3);
76+
});
77+
});
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import {CancellationToken} from "./types";
2+
3+
/**
4+
* Wraps an API client function that uses pagination and calles it iteratively to
5+
* fetch all data.
6+
*/
7+
export function paginated<REQ, RES>(
8+
paginationKey: keyof (REQ | RES),
9+
itemsKey: keyof RES
10+
) {
11+
return function (
12+
_target: any,
13+
_propertyKey: string,
14+
descriptor: PropertyDescriptor
15+
): PropertyDescriptor {
16+
const childFunction = descriptor.value as (
17+
req: REQ,
18+
token?: CancellationToken
19+
) => Promise<RES>;
20+
21+
descriptor.value = async function (
22+
req: REQ,
23+
token?: CancellationToken
24+
): Promise<RES> {
25+
let results = [];
26+
let response: RES;
27+
let paginationToken: any;
28+
do {
29+
if (paginationToken) {
30+
req[paginationKey] = paginationToken;
31+
} else {
32+
delete req[paginationKey];
33+
}
34+
response = await childFunction.call(this, req, token);
35+
36+
if (token && token.isCancellationRequested) {
37+
return response;
38+
}
39+
40+
if (response[itemsKey]) {
41+
results.push(...(response[itemsKey] as any));
42+
}
43+
paginationToken = response[paginationKey];
44+
} while (paginationToken);
45+
46+
(response[itemsKey] as any) = results;
47+
return response;
48+
};
49+
return descriptor;
50+
};
51+
}

packages/databricks-sdk-js/src/index.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,22 @@ export {JobsService} from "./apis/jobs";
1414
export * as libraries from "./apis/libraries";
1515
export {ManagedLibraryService} from "./apis/libraries";
1616
export * as notebook from "./apis/notebook";
17-
export * as scim from "./apis/scim";
17+
export * as repos from "./apis/repos";
18+
export {ReposService} from "./apis/repos";
1819
export {ScimService} from "./apis/scim";
1920
export * as workspace from "./apis/workspace";
2021
export {WorkspaceService} from "./apis/workspace";
2122

2223
export * from "./services/Cluster";
2324
export * from "./services/Command";
2425
export * from "./services/ExecutionContext";
26+
export * from "./services/Repos";
2527
export * from "./services/WorkflowRun";
2628

2729
export * from "./auth/types";
2830
export * from "./auth/fromEnv";
2931
export * from "./auth/fromChain";
3032
export * from "./auth/fromConfigFile";
3133
export * from "./auth/configFile";
34+
35+
export * from "./types";

packages/databricks-sdk-js/src/services/Cluster.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,10 @@ import {
1313
RunLifeCycleState,
1414
SubmitRunRequest,
1515
} from "../apis/jobs";
16+
import {CancellationToken} from "../types";
1617
import {ExecutionContext} from "./ExecutionContext";
1718
import {WorkflowRun} from "./WorkflowRun";
1819

19-
interface CancellationToken {
20-
isCancellationRequested: boolean;
21-
}
22-
2320
export class Cluster {
2421
private clusterApi: ClusterService;
2522

0 commit comments

Comments
 (0)