diff --git a/knip.json b/knip.json index 3cec36a06..7af9ba72f 100644 --- a/knip.json +++ b/knip.json @@ -16,6 +16,7 @@ "@databricks/sdk-files", "@databricks/sdk-genie", "@databricks/sdk-jobs", + "@databricks/sdk-postgres", "@databricks/sdk-uc-tables", "@databricks/sdk-vectorsearch", "@databricks/sdk-options", diff --git a/packages/appkit/package.json b/packages/appkit/package.json index b80fef165..445adfaa8 100644 --- a/packages/appkit/package.json +++ b/packages/appkit/package.json @@ -80,6 +80,7 @@ "@databricks/sdk-uc-tables": "0.52.0", "@databricks/sdk-vectorsearch": "0.51.0", "@databricks/sdk-options": "0.51.0", + "@databricks/sdk-postgres": "0.52.0", "@databricks/sdk-scim": "0.51.0", "@databricks/sdk-statementexecution": "0.52.0", "@databricks/sdk-warehouses": "0.53.0", diff --git a/packages/appkit/src/testing/mock-workspace-client.ts b/packages/appkit/src/testing/mock-workspace-client.ts index 3b21896a7..24da241fe 100644 --- a/packages/appkit/src/testing/mock-workspace-client.ts +++ b/packages/appkit/src/testing/mock-workspace-client.ts @@ -71,6 +71,7 @@ const FACADE_SERVICES = [ "jobs", "vectorSearch", "tables", + "postgres", "statementExecution", "servingEndpoints", "currentUser", diff --git a/packages/shared/package.json b/packages/shared/package.json index 33df9896a..de317aecf 100644 --- a/packages/shared/package.json +++ b/packages/shared/package.json @@ -57,6 +57,7 @@ "@databricks/sdk-uc-tables": "0.52.0", "@databricks/sdk-vectorsearch": "0.51.0", "@databricks/sdk-options": "0.51.0", + "@databricks/sdk-postgres": "0.52.0", "@databricks/sdk-scim": "0.51.0", "@databricks/sdk-statementexecution": "0.52.0", "@databricks/sdk-warehouses": "0.53.0", diff --git a/packages/shared/src/workspace-client/client.ts b/packages/shared/src/workspace-client/client.ts index ec1dbe624..c704f95a4 100644 --- a/packages/shared/src/workspace-client/client.ts +++ b/packages/shared/src/workspace-client/client.ts @@ -26,6 +26,8 @@ import { type FilesClient, buildTablesClient, type TablesClient, + buildPostgresClient, + type PostgresClient, buildVectorSearchClient, type VectorSearchClient, type StatementExecutionClient, @@ -47,6 +49,7 @@ export class AppKitWorkspaceClient implements WorkspaceClient { #files?: FilesClient; #vectorSearch?: VectorSearchClient; #tables?: TablesClient; + #postgres?: PostgresClient; constructor(opts: WorkspaceClientOptions) { this.#opts = opts; @@ -100,6 +103,14 @@ export class AppKitWorkspaceClient implements WorkspaceClient { return this.#tables; } + // Migrated to the modular SDK — built lazily, independent of the legacy client. + get postgres(): PostgresClient { + if (!this.#postgres) { + this.#postgres = buildPostgresClient(this.#opts); + } + return this.#postgres; + } + // Migrated to the modular SDK — built lazily, independent of the legacy client. get statementExecution(): StatementExecutionClient { if (!this.#statementExecution) { diff --git a/packages/shared/src/workspace-client/modular.ts b/packages/shared/src/workspace-client/modular.ts index 4c645ab85..3fe97d6be 100644 --- a/packages/shared/src/workspace-client/modular.ts +++ b/packages/shared/src/workspace-client/modular.ts @@ -56,6 +56,7 @@ import type { FilesClient } from "@databricks/sdk-files/v2"; import type { GenieClient } from "@databricks/sdk-genie/v1"; import type { JobsClient } from "@databricks/sdk-jobs/v2"; import type { ClientOptions } from "@databricks/sdk-options/client"; +import type { PostgresClient } from "@databricks/sdk-postgres/v1"; import { ScimClient } from "@databricks/sdk-scim/v1"; import { StatementExecutionClient } from "@databricks/sdk-statementexecution/v1"; import type { TablesClient } from "@databricks/sdk-uc-tables/v1"; @@ -623,6 +624,20 @@ export function buildTablesClient(opts: WorkspaceClientOptions): TablesClient { ); } +/** + * Build a modular Lakebase Postgres (autoscaling: projects, branches, + * endpoints) client from wrapper options. Distinct from the provisioned + * `/api/2.0/database` instance API. + */ +export function buildPostgresClient( + opts: WorkspaceClientOptions, +): PostgresClient { + return lazyClient( + () => import("@databricks/sdk-postgres/v1").then((m) => m.PostgresClient), + mapToClientOptions(opts), + ); +} + // ── Client type re-exports (for the facade accessor types) ─────────────── export type { FilesClient } from "@databricks/sdk-files/v2"; export type { @@ -631,6 +646,7 @@ export type { } from "@databricks/sdk-files/v2"; export type { GenieClient } from "@databricks/sdk-genie/v1"; export type { JobsClient } from "@databricks/sdk-jobs/v2"; +export type { PostgresClient } from "@databricks/sdk-postgres/v1"; export type { ScimClient } from "@databricks/sdk-scim/v1"; export type { StatementExecutionClient } from "@databricks/sdk-statementexecution/v1"; export type { TablesClient } from "@databricks/sdk-uc-tables/v1"; diff --git a/packages/shared/src/workspace-client/tests/modular.test.ts b/packages/shared/src/workspace-client/tests/modular.test.ts index 4b9928fd1..383dacf36 100644 --- a/packages/shared/src/workspace-client/tests/modular.test.ts +++ b/packages/shared/src/workspace-client/tests/modular.test.ts @@ -55,6 +55,12 @@ vi.mock("@databricks/sdk-genie/v1", () => ({ }; }), })); +vi.mock("@databricks/sdk-postgres/v1", () => ({ + PostgresClient: vi.fn().mockImplementation((opts) => { + ctorOpts.push(opts); + return { opts, getEndpoint: async () => ({}) }; + }), +})); vi.mock("@databricks/sdk-auth/credentials", () => ({ newPatCredentials: vi.fn((token: string) => { patTokens.push(token); @@ -120,6 +126,7 @@ import { ApiError } from "../errors"; import { buildGenieClient, buildWarehousesClient, + buildPostgresClient, buildWorkspaceAuth, } from "../modular"; @@ -345,6 +352,33 @@ describe("modular mapToClientOptions (via buildWarehousesClient)", () => { expect(Reflect.get(client, "then")).toBeUndefined(); }); + test("postgres normalizes the host and passes SP M2M credentials", async () => { + process.env.DATABRICKS_HOST = "envhost.cloud.databricks.com"; + process.env.DATABRICKS_CLIENT_ID = "sp-client-id"; + process.env.DATABRICKS_CLIENT_SECRET = "sp-secret"; + await buildPostgresClient({}).getEndpoint({ name: "e" }); + expect(ctorOpts[0].host).toBe("https://envhost.cloud.databricks.com"); + expect((ctorOpts[0].credentials as { name: () => string }).name()).toBe( + "oauth-m2m", + ); + }); + + test("postgres (asUser) uses the OBO token as PAT, not the SP env creds", async () => { + process.env.DATABRICKS_CLIENT_ID = "sp-client-id"; + process.env.DATABRICKS_CLIENT_SECRET = "sp-secret"; + await buildPostgresClient({ + token: "user-token", + host: "https://x", + }).getEndpoint({ + name: "e", + }); + expect(ctorOpts[0].credentials).toMatchObject({ + kind: "pat", + token: "user-token", + }); + expect(m2mOpts).toEqual([]); + }); + test("no product configured (build-time) → transport adds no AppKit User-Agent", async () => { buildWarehousesClient({ host: "https://x" }); expect(await sentUserAgent(ctorOpts[0].httpClient)).toBeNull(); diff --git a/packages/shared/src/workspace-client/types.ts b/packages/shared/src/workspace-client/types.ts index 4444e5fbe..7a1e0abba 100644 --- a/packages/shared/src/workspace-client/types.ts +++ b/packages/shared/src/workspace-client/types.ts @@ -24,6 +24,7 @@ import type { FilesClient, TablesClient, VectorSearchClient, + PostgresClient, } from "./modular"; // Legacy SDK type namespaces for un-migrated services, re-exported so AppKit @@ -64,6 +65,8 @@ export interface WorkspaceClient extends WorkspaceAuth { /** Unity Catalog Tables (modular SDK). */ readonly tables: TablesClient; + /** Lakebase Postgres: autoscaling projects, branches, endpoints (modular SDK). */ + readonly postgres: PostgresClient; /** Statement Execution (modular SDK). */ readonly statementExecution: StatementExecutionClient; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 87927c2ad..d09f608b1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -302,6 +302,9 @@ importers: '@databricks/sdk-options': specifier: 0.51.0 version: 0.51.0 + '@databricks/sdk-postgres': + specifier: 0.52.0 + version: 0.52.0 '@databricks/sdk-scim': specifier: 0.51.0 version: 0.51.0 @@ -650,6 +653,9 @@ importers: '@databricks/sdk-options': specifier: 0.51.0 version: 0.51.0 + '@databricks/sdk-postgres': + specifier: 0.52.0 + version: 0.52.0 '@databricks/sdk-scim': specifier: 0.51.0 version: 0.51.0 @@ -2062,6 +2068,10 @@ packages: resolution: {integrity: sha512-p5uBh64Y1onnvwlEwfRsmKmGRI23Y4Ly8fZiCgNUwVDe4Z9d0ctI0mo1v4gnDqlmBaBml3TZfqn5BJBROCXsEw==} engines: {node: '>=22.0.0'} + '@databricks/sdk-postgres@0.52.0': + resolution: {integrity: sha512-QW+H5Oc3ehfwuYJRcV424lly+msjdir5RqK/4/a7BbDpKtugMgAxLMJl2ojw48B3e4xAqh3JsUT21vmHeVPtGQ==} + engines: {node: '>=22.0.0'} + '@databricks/sdk-scim@0.51.0': resolution: {integrity: sha512-WSTpuID4gRoTxVApyxujW+MsI5ANvmn8/DqPK6fXqxCkSINLEbtR+GTk/th8xnADtJekKNIWzeORFblXd3idcg==} engines: {node: '>=22.0.0'} @@ -14410,6 +14420,15 @@ snapshots: '@databricks/sdk-auth': 0.51.0 '@databricks/sdk-core': 0.51.0 + '@databricks/sdk-postgres@0.52.0': + dependencies: + '@databricks/sdk-auth': 0.51.0 + '@databricks/sdk-core': 0.51.0 + '@databricks/sdk-options': 0.51.0 + '@js-temporal/polyfill': 0.5.1 + json-bigint: 1.0.0 + zod: 4.3.6 + '@databricks/sdk-scim@0.51.0': dependencies: '@databricks/sdk-auth': 0.51.0