@superblocksteam/sabs-client 0.0.1-demo-databricks-deploy

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/sabs.ts ADDED
@@ -0,0 +1,502 @@
1
+ import {
2
+ ApplicationMetadata,
3
+ BuildRequest,
4
+ BuildResponse,
5
+ BuildStatus,
6
+ DeployDatabricksRequest,
7
+ DeployDatabricksResponse,
8
+ ListRequest,
9
+ ListResponse,
10
+ StatusResponse,
11
+ TerminateRequest,
12
+ TerminateResponse,
13
+ BulkStatusRequest,
14
+ BulkStatusResponse,
15
+ CreateLiveEditRequest,
16
+ CreateLiveEditResponse,
17
+ TerminateLiveEditResponse,
18
+ TerminateLiveEditRequest
19
+ } from '@superblocksteam/sabs-types';
20
+ import axios, { AxiosRequestConfig, RawAxiosRequestHeaders } from 'axios';
21
+
22
+ import { createErrorFromStatusCode, createClientError, createNetworkError } from './errors';
23
+
24
+ /**
25
+ * SABS (Superblocks Application Build System) TypeScript Client
26
+ *
27
+ * Provides methods to interact with the SABS API for building and managing applications.
28
+ *
29
+ * All error types inherit from the standard `HttpError` class that extends the standard `Error` class,
30
+ * ensuring backward compatibility with existing error handling code that catches generic `Error` instances
31
+ * used in earlier versions of the client library.
32
+ */
33
+ export class SabsClient {
34
+ private readonly baseUrl: string;
35
+
36
+ public constructor(baseUrl: string) {
37
+ this.baseUrl = baseUrl;
38
+ }
39
+
40
+ /**
41
+ * Start a new build for an application
42
+ *
43
+ * @param params - Build parameters
44
+ * @param params.directoryHash - Hash of the application directory to build
45
+ * @param params.meta - Application metadata (ID and organization ID)
46
+ * @param params.buildKey - Secret build key for authentication
47
+ * @param params.accessToken - JWT access token for authorization
48
+ * @returns Promise resolving to build information including build ID
49
+ * @throws BadRequestError if the directory hash, application metadata, application ID, organization ID, build key, or access token are empty or invalid
50
+ * @throws ForbiddenError if the access token is invalid or missing the required scopes
51
+ * @throws InternalServerError if the service has an unexpected error while performing this request
52
+ * @throws HttpError if the request fails for an unknown reason
53
+ */
54
+ public async build({
55
+ directoryHash,
56
+ meta,
57
+ buildKey,
58
+ accessToken
59
+ }: {
60
+ directoryHash: string;
61
+ meta: ApplicationMetadata;
62
+ buildKey: string;
63
+ accessToken?: string;
64
+ }): Promise<BuildResponse> {
65
+ if (!directoryHash || directoryHash.length === 0) {
66
+ throw createClientError('Directory hash is required');
67
+ }
68
+ if (!meta) {
69
+ throw createClientError('Application metadata is required');
70
+ }
71
+ if (!meta.id || meta.id.length === 0) {
72
+ throw createClientError('Application ID is required');
73
+ }
74
+ if (!meta.organizationId || meta.organizationId.length === 0) {
75
+ throw createClientError('Organization ID is required');
76
+ }
77
+ if (!buildKey || buildKey.length === 0) {
78
+ throw createClientError('Build key is required');
79
+ }
80
+ if (!accessToken || accessToken.length === 0) {
81
+ throw createClientError('Access token is required');
82
+ }
83
+
84
+ const data = new BuildRequest({
85
+ directoryHash: directoryHash,
86
+ applicationMetadata: meta,
87
+ buildKey
88
+ });
89
+
90
+ return this.executeRequest<BuildResponse>(
91
+ {
92
+ method: 'POST',
93
+ url: `${this.baseUrl}/v1/builds`,
94
+ data
95
+ },
96
+ accessToken
97
+ );
98
+ }
99
+
100
+ /**
101
+ * Start a new Databricks build for an application and deploy it to Databricks
102
+ *
103
+ * @param params - Build parameters
104
+ * @param params.directoryHash - Hash of the application directory to build
105
+ * @param params.meta - Application metadata (ID and organization ID)
106
+ * @param params.buildKey - Secret build key for authentication
107
+ * @param params.accessToken - JWT access token for authorization
108
+ * @returns Promise resolving to build information including build ID
109
+ * @throws BadRequestError if the directory hash, application metadata, application ID, organization ID, build key, or access token are empty or invalid
110
+ * @throws ForbiddenError if the access token is invalid or missing the required scopes
111
+ * @throws InternalServerError if the service has an unexpected error while performing this request
112
+ * @throws HttpError if the request fails for an unknown reason
113
+ */
114
+ public async deployDatabricks({
115
+ directoryHash,
116
+ meta,
117
+ buildKey,
118
+ accessToken
119
+ }: {
120
+ directoryHash: string;
121
+ meta: ApplicationMetadata;
122
+ buildKey: string;
123
+ accessToken?: string;
124
+ }): Promise<DeployDatabricksResponse> {
125
+ if (!directoryHash || directoryHash.length === 0) {
126
+ throw createClientError('Directory hash is required');
127
+ }
128
+ if (!meta) {
129
+ throw createClientError('Application metadata is required');
130
+ }
131
+ if (!meta.id || meta.id.length === 0) {
132
+ throw createClientError('Application ID is required');
133
+ }
134
+ if (!meta.organizationId || meta.organizationId.length === 0) {
135
+ throw createClientError('Organization ID is required');
136
+ }
137
+ if (!buildKey || buildKey.length === 0) {
138
+ throw createClientError('Build key is required');
139
+ }
140
+ if (!accessToken || accessToken.length === 0) {
141
+ throw createClientError('Access token is required');
142
+ }
143
+
144
+ const data = new DeployDatabricksRequest({
145
+ directoryHash: directoryHash,
146
+ applicationMetadata: meta,
147
+ buildKey
148
+ });
149
+
150
+ return this.executeRequest<DeployDatabricksResponse>(
151
+ {
152
+ method: 'POST',
153
+ url: `${this.baseUrl}/v1/builds/deploy-databricks`,
154
+ data
155
+ },
156
+ accessToken
157
+ );
158
+ }
159
+
160
+ /**
161
+ * Get the status of a build
162
+ *
163
+ * @param params - Status query parameters
164
+ * @param params.buildId - ID of the build to check
165
+ * @param params.accessToken - JWT access token for authorization
166
+ * @returns Promise resolving to build status information
167
+ * @throws BadRequestError if the build ID or access token is empty or invalid
168
+ * @throws UnauthorizedError if the access token is invalid or missing the required scopes
169
+ * @throws NotFoundError if the build ID is invalid
170
+ * @throws InternalServerError if the service has an unexpected error while performing this request
171
+ * @throws HttpError if the request fails for an unknown reason
172
+ */
173
+ public async status({ buildId, accessToken }: { buildId: string; accessToken?: string }): Promise<StatusResponse> {
174
+ if (!buildId || buildId.length === 0) {
175
+ throw createClientError('Build ID is required');
176
+ }
177
+ if (!accessToken || accessToken.length === 0) {
178
+ throw createClientError('Access token is required');
179
+ }
180
+
181
+ return this.executeRequest<StatusResponse>(
182
+ {
183
+ method: 'GET',
184
+ url: `${this.baseUrl}/v1/builds/${buildId}`
185
+ },
186
+ accessToken
187
+ );
188
+ }
189
+
190
+ /**
191
+ * Get the status of multiple builds at once
192
+ *
193
+ * @param params - Bulk status query parameters
194
+ * @param params.organizationId - Organization ID
195
+ * @param params.applicationId - Application ID
196
+ * @param params.directoryHashes - Array of directory hashes to check
197
+ * @param params.accessToken - JWT access token for authorization
198
+ * @returns Promise resolving to multiple build status information
199
+ * @throws BadRequestError if the organization ID, application ID, directory hashes, or access token are empty or invalid
200
+ * @throws UnauthorizedError if the access token is invalid or missing the required scopes
201
+ * @throws NotFoundError if the organization ID or application ID is invalid
202
+ * @throws InternalServerError if the service has an unexpected error while performing this request
203
+ * @throws HttpError if the request fails for an unknown reason
204
+ */
205
+ public async bulkStatus({
206
+ organizationId,
207
+ applicationId,
208
+ directoryHashes,
209
+ accessToken
210
+ }: {
211
+ organizationId: string;
212
+ applicationId: string;
213
+ directoryHashes: string[];
214
+ accessToken?: string;
215
+ }): Promise<BulkStatusResponse> {
216
+ if (!organizationId || organizationId.length === 0) {
217
+ throw createClientError('Organization ID is required');
218
+ }
219
+ if (!applicationId || applicationId.length === 0) {
220
+ throw createClientError('Application ID is required');
221
+ }
222
+ if (!directoryHashes || directoryHashes.length === 0) {
223
+ throw createClientError('Directory hashes are required');
224
+ }
225
+ if (!accessToken || accessToken.length === 0) {
226
+ throw createClientError('Access token is required');
227
+ }
228
+
229
+ const data = new BulkStatusRequest({
230
+ organizationId,
231
+ applicationId,
232
+ directoryHashes
233
+ });
234
+
235
+ return this.executeRequest<BulkStatusResponse>(
236
+ {
237
+ method: 'POST',
238
+ url: `${this.baseUrl}/v1/builds/${organizationId}/${applicationId}/bulk-status`,
239
+ data
240
+ },
241
+ accessToken
242
+ );
243
+ }
244
+
245
+ /**
246
+ * List all builds for a specific application and directory
247
+ *
248
+ * @param params - List query parameters
249
+ * @param params.organizationId - Organization ID
250
+ * @param params.applicationId - Application ID
251
+ * @param params.directoryHash - Hash of the application directory
252
+ * @param params.accessToken - JWT access token for authorization
253
+ * @returns Promise resolving to list of builds
254
+ * @throws BadRequestError if the organization ID, application ID, directory hash, or access token are empty or invalid
255
+ * @throws UnauthorizedError if the access token is invalid or missing the required scopes
256
+ * @throws NotFoundError if the organization ID or application ID is invalid
257
+ * @throws InternalServerError if the service has an unexpected error while performing this request
258
+ * @throws HttpError if the request fails for an unknown reason
259
+ */
260
+ public async list({
261
+ organizationId,
262
+ applicationId,
263
+ directoryHash,
264
+ accessToken
265
+ }: {
266
+ organizationId: string;
267
+ applicationId: string;
268
+ directoryHash: string;
269
+ accessToken?: string;
270
+ }): Promise<ListResponse> {
271
+ if (!organizationId || organizationId.length === 0) {
272
+ throw createClientError('Organization ID is required');
273
+ }
274
+ if (!applicationId || applicationId.length === 0) {
275
+ throw createClientError('Application ID is required');
276
+ }
277
+ if (!directoryHash || directoryHash.length === 0) {
278
+ throw createClientError('Directory hash is required');
279
+ }
280
+ if (!accessToken || accessToken.length === 0) {
281
+ throw createClientError('Access token is required');
282
+ }
283
+
284
+ const data = new ListRequest({
285
+ organizationId,
286
+ applicationId,
287
+ directoryHash
288
+ });
289
+
290
+ return this.executeRequest<ListResponse>(
291
+ {
292
+ method: 'GET',
293
+ url: `${this.baseUrl}/v1/build`,
294
+ params: data
295
+ },
296
+ accessToken
297
+ );
298
+ }
299
+
300
+ /**
301
+ * Terminate a running build with a final status
302
+ *
303
+ * @param params - Termination parameters
304
+ * @param params.buildId - ID of the build to terminate
305
+ * @param params.status - Final status of the build
306
+ * @param params.buildKey - Secret build key for authentication
307
+ * @param params.error - Optional error message if build failed
308
+ * @param params.accessToken - JWT access token for authorization
309
+ * @returns Promise resolving to termination confirmation
310
+ * @throws BadRequestError if the build ID, build status, build key, or access token are empty or invalid
311
+ * @throws UnauthorizedError if the access token is invalid or missing the required scopes
312
+ * @throws NotFoundError if the build ID is invalid
313
+ * @throws InternalServerError if the service has an unexpected error while performing this request
314
+ * @throws HttpError if the request fails for an unknown reason
315
+ */
316
+ public async terminate({
317
+ buildId,
318
+ status,
319
+ buildKey,
320
+ error,
321
+ accessToken
322
+ }: {
323
+ buildId: string;
324
+ status: BuildStatus;
325
+ buildKey?: string;
326
+ error?: string;
327
+ accessToken?: string;
328
+ }): Promise<TerminateResponse> {
329
+ if (!buildId || buildId.length === 0) {
330
+ throw createClientError('Build ID is required');
331
+ }
332
+ if (!status) {
333
+ throw createClientError('Build status is required');
334
+ }
335
+ if (!buildKey || buildKey.length === 0) {
336
+ throw createClientError('Build key is required');
337
+ }
338
+ if (!accessToken || accessToken.length === 0) {
339
+ throw createClientError('Access token is required');
340
+ }
341
+
342
+ const data = new TerminateRequest({
343
+ buildId,
344
+ status,
345
+ error,
346
+ buildKey
347
+ });
348
+
349
+ return this.executeRequest<TerminateResponse>(
350
+ {
351
+ method: 'POST',
352
+ url: `${this.baseUrl}/v1/builds/${buildId}/terminate`,
353
+ data
354
+ },
355
+ accessToken
356
+ );
357
+ }
358
+
359
+ /**
360
+ * Create a new live edit session for real-time development
361
+ *
362
+ * @param params - Live edit creation parameters
363
+ * @param params.applicationId - Application ID
364
+ * @param params.organizationId - Organization ID
365
+ * @param params.branch - Git branch name
366
+ * @param params.expiresIn - Session duration in seconds
367
+ * @param params.accessToken - JWT access token for authorization
368
+ * @returns Promise resolving to live edit session information
369
+ * @throws BadRequestError if the application ID, organization ID, branch, or access token are empty or invalid, or expiresIn is not greater than 0
370
+ * @throws UnauthorizedError if the access token is invalid or missing the required scopes
371
+ * @throws InternalServerError if the service has an unexpected error while performing this request
372
+ * @throws HttpError if the request fails for an unknown reason
373
+ */
374
+ public async createLiveEdit({
375
+ applicationId,
376
+ organizationId,
377
+ branch,
378
+ expiresIn,
379
+ accessToken
380
+ }: {
381
+ applicationId: string;
382
+ organizationId: string;
383
+ branch: string;
384
+ expiresIn: number;
385
+ accessToken: string;
386
+ }): Promise<CreateLiveEditResponse> {
387
+ if (!applicationId || applicationId.length === 0) {
388
+ throw createClientError('Application ID is required');
389
+ }
390
+ if (!organizationId || organizationId.length === 0) {
391
+ throw createClientError('Organization ID is required');
392
+ }
393
+ if (!branch || branch.length === 0) {
394
+ throw createClientError('Branch is required');
395
+ }
396
+ if (!accessToken || accessToken.length === 0) {
397
+ throw createClientError('Access token is required');
398
+ }
399
+ if (!expiresIn || expiresIn <= 0) {
400
+ throw createClientError('Expires in is required and must be greater than 0');
401
+ }
402
+
403
+ const data = new CreateLiveEditRequest({
404
+ application: {
405
+ applicationId,
406
+ organizationId: organizationId,
407
+ branch: branch
408
+ },
409
+ sessionJwt: accessToken,
410
+ expiresIn: BigInt(expiresIn)
411
+ });
412
+
413
+ return this.executeRequest<CreateLiveEditResponse>(
414
+ {
415
+ method: 'POST',
416
+ url: `${this.baseUrl}/v1/live-edit`,
417
+ data
418
+ },
419
+ accessToken
420
+ );
421
+ }
422
+
423
+ /**
424
+ * Terminate an active live edit session
425
+ *
426
+ * @param params - Live edit termination parameters
427
+ * @param params.liveEditId - ID of the live edit session to terminate
428
+ * @param params.accessToken - JWT access token for authorization
429
+ * @returns Promise resolving to termination confirmation
430
+ * @throws BadRequestError if the live edit ID or access token are empty or invalid
431
+ * @throws UnauthorizedError if the access token is invalid or missing the required scopes
432
+ * @throws NotFoundError if the live edit ID is invalid
433
+ * @throws InternalServerError if the service has an unexpected error while performing this request
434
+ * @throws HttpError if the request fails for an unknown reason
435
+ */
436
+ public async terminateLiveEdit({
437
+ liveEditId,
438
+ accessToken
439
+ }: {
440
+ liveEditId: string;
441
+ accessToken: string;
442
+ }): Promise<TerminateLiveEditResponse> {
443
+ if (!liveEditId || liveEditId.length === 0) {
444
+ throw createClientError('Live edit ID is required');
445
+ }
446
+ if (!accessToken || accessToken.length === 0) {
447
+ throw createClientError('Access token is required');
448
+ }
449
+
450
+ const data = new TerminateLiveEditRequest({
451
+ liveEditId
452
+ });
453
+
454
+ return this.executeRequest<TerminateLiveEditResponse>(
455
+ {
456
+ method: 'POST',
457
+ url: `${this.baseUrl}/v1/live-edit/${liveEditId}/terminate`,
458
+ data
459
+ },
460
+ accessToken
461
+ );
462
+ }
463
+
464
+ private async executeRequest<T>(config: AxiosRequestConfig, accessToken?: string): Promise<T> {
465
+ let headers: RawAxiosRequestHeaders | undefined;
466
+ if (accessToken || config.headers) {
467
+ headers = {
468
+ ...config.headers,
469
+ Authorization: accessToken ? `Bearer ${accessToken}` : undefined
470
+ };
471
+ }
472
+
473
+ try {
474
+ const response = await axios.request<T>({
475
+ ...config,
476
+ headers
477
+ });
478
+ return response.data;
479
+ } catch (error) {
480
+ if (axios.isAxiosError(error)) {
481
+ const statusCode = error.response?.status ?? 500;
482
+ const statusText = error.response?.statusText ?? 'Unknown Error';
483
+ const responseData = error.response?.data;
484
+
485
+ let message: string;
486
+ if (responseData && typeof responseData === 'object' && responseData.message) {
487
+ message = responseData.message;
488
+ } else if (responseData && typeof responseData === 'string') {
489
+ message = responseData;
490
+ } else {
491
+ message = `${statusText} (${statusCode})`;
492
+ }
493
+
494
+ throw createErrorFromStatusCode(statusCode, message);
495
+ } else {
496
+ // Network error or other non-HTTP error
497
+ const message = error instanceof Error ? error.message : 'Unknown error occurred';
498
+ throw createNetworkError(message, error instanceof Error ? error : undefined);
499
+ }
500
+ }
501
+ }
502
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,23 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "esnext",
4
+ "module": "commonjs",
5
+ "composite": true,
6
+ "incremental": true,
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "sourceMap": true,
10
+ "moduleResolution": "node",
11
+ "esModuleInterop": true,
12
+ "resolveJsonModule": true,
13
+ "rootDir": "./src",
14
+ "outDir": "./dist",
15
+ "allowSyntheticDefaultImports": true,
16
+ "forceConsistentCasingInFileNames": true,
17
+ "strictNullChecks": true,
18
+ "allowJs": false,
19
+ "skipLibCheck": true
20
+ },
21
+ "exclude": ["./dist", "./node_modules"],
22
+ "include": ["./**/*.ts"]
23
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "./tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": ".",
5
+ "noEmit": true
6
+ },
7
+ "include": ["./test/**/*.ts", "./src/**/*.ts", "./*.config.js", "./src/**/*.json"],
8
+ "exclude": ["node_modules"]
9
+ }