diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 29ecfc5..2a655d4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -77,6 +77,34 @@ jobs: - name: Run Tests run: pnpm test + playground-integration-test: + name: Playground Integration Tests + needs: detect-changes + if: needs.detect-changes.outputs.appkit == 'true' + runs-on: + group: databricks-protected-runner-group + labels: linux-ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - uses: pnpm/action-setup@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + - name: Install dependencies + run: pnpm install --frozen-lockfile + - name: Install Playwright Browsers + run: pnpm --filter=dev-playground exec playwright install --with-deps chromium + - name: Build packages + run: pnpm build + - name: Run Integration Tests + run: pnpm --filter=dev-playground test:integration + env: + APPKIT_E2E_TEST: 'true' + DATABRICKS_WAREHOUSE_ID: e2e-mock + DATABRICKS_WORKSPACE_ID: e2e-mock + docs-build: name: Docs Build needs: detect-changes diff --git a/apps/dev-playground/server/index.ts b/apps/dev-playground/server/index.ts index ed3fdac..a56ba4a 100644 --- a/apps/dev-playground/server/index.ts +++ b/apps/dev-playground/server/index.ts @@ -1,7 +1,18 @@ import { analytics, createApp, server } from "@databricks/appkit"; +import { WorkspaceClient } from "@databricks/sdk-experimental"; import { reconnect } from "./reconnect-plugin"; import { telemetryExamples } from "./telemetry-example-plugin"; +function createMockClient() { + const client = new WorkspaceClient({ + host: "http://localhost", + token: "e2e", + authType: "pat", + }); + client.currentUser.me = async () => ({ id: "e2e-test-user" }); + return client; +} + createApp({ plugins: [ server({ autoStart: false }), @@ -9,6 +20,7 @@ createApp({ telemetryExamples(), analytics({}), ], + ...(process.env.APPKIT_E2E_TEST && { client: createMockClient() }), }).then((appkit) => { appkit.server .extend((app) => { diff --git a/docs/docs/api/appkit/Function.createApp.md b/docs/docs/api/appkit/Function.createApp.md index 35128e0..85e7fa2 100644 --- a/docs/docs/api/appkit/Function.createApp.md +++ b/docs/docs/api/appkit/Function.createApp.md @@ -3,6 +3,7 @@ ```ts function createApp(config: { cache?: CacheConfig; + client?: WorkspaceClient; plugins?: T; telemetry?: TelemetryConfig; }): Promise>; @@ -20,8 +21,9 @@ Bootstraps AppKit with the provided configuration. | Parameter | Type | | ------ | ------ | -| `config` | \{ `cache?`: [`CacheConfig`](Interface.CacheConfig.md); `plugins?`: `T`; `telemetry?`: [`TelemetryConfig`](Interface.TelemetryConfig.md); \} | +| `config` | \{ `cache?`: [`CacheConfig`](Interface.CacheConfig.md); `client?`: `WorkspaceClient`; `plugins?`: `T`; `telemetry?`: [`TelemetryConfig`](Interface.TelemetryConfig.md); \} | | `config.cache?` | [`CacheConfig`](Interface.CacheConfig.md) | +| `config.client?` | `WorkspaceClient` | | `config.plugins?` | `T` | | `config.telemetry?` | [`TelemetryConfig`](Interface.TelemetryConfig.md) | diff --git a/packages/appkit/src/context/service-context.ts b/packages/appkit/src/context/service-context.ts index 0304196..ee4b245 100644 --- a/packages/appkit/src/context/service-context.ts +++ b/packages/appkit/src/context/service-context.ts @@ -57,8 +57,13 @@ export class ServiceContext { /** * Initialize the service context. Should be called once at app startup. * Safe to call multiple times - will return the same instance. + * + * @param client - Optional pre-configured WorkspaceClient to use instead + * of creating one from environment credentials. */ - static async initialize(): Promise { + static async initialize( + client?: WorkspaceClient, + ): Promise { if (ServiceContext.instance) { return ServiceContext.instance; } @@ -67,7 +72,7 @@ export class ServiceContext { return ServiceContext.initPromise; } - ServiceContext.initPromise = ServiceContext.createContext(); + ServiceContext.initPromise = ServiceContext.createContext(client); ServiceContext.instance = await ServiceContext.initPromise; return ServiceContext.instance; } @@ -147,19 +152,21 @@ export class ServiceContext { return getClientOptions(); } - private static async createContext(): Promise { - const client = new WorkspaceClient({}, getClientOptions()); + private static async createContext( + client?: WorkspaceClient, + ): Promise { + const wsClient = client ?? new WorkspaceClient({}, getClientOptions()); - const warehouseId = ServiceContext.getWarehouseId(client); - const workspaceId = ServiceContext.getWorkspaceId(client); - const currentUser = await client.currentUser.me(); + const warehouseId = ServiceContext.getWarehouseId(wsClient); + const workspaceId = ServiceContext.getWorkspaceId(wsClient); + const currentUser = await wsClient.currentUser.me(); if (!currentUser.id) { throw ConfigurationError.resourceNotFound("Service user ID"); } return { - client, + client: wsClient, serviceUserId: currentUser.id, warehouseId, workspaceId, diff --git a/packages/appkit/src/core/appkit.ts b/packages/appkit/src/core/appkit.ts index c34588e..ed226b3 100644 --- a/packages/appkit/src/core/appkit.ts +++ b/packages/appkit/src/core/appkit.ts @@ -1,3 +1,4 @@ +import type { WorkspaceClient } from "@databricks/sdk-experimental"; import type { BasePlugin, CacheConfig, @@ -141,6 +142,7 @@ export class AppKit { plugins?: T; telemetry?: TelemetryConfig; cache?: CacheConfig; + client?: WorkspaceClient; } = {}, ): Promise> { // Initialize core services @@ -149,7 +151,7 @@ export class AppKit { // Initialize ServiceContext for Databricks client management // This provides the service principal client and shared resources - await ServiceContext.initialize(); + await ServiceContext.initialize(config?.client); const rawPlugins = config.plugins as T; const preparedPlugins = AppKit.preparePlugins(rawPlugins); @@ -188,6 +190,7 @@ export async function createApp< plugins?: T; telemetry?: TelemetryConfig; cache?: CacheConfig; + client?: WorkspaceClient; } = {}, ): Promise> { return AppKit._createApp(config); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72bcd8e..587a1d2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18088,7 +18088,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 csstype: 3.1.3 dom-serializer@1.4.1: @@ -19268,7 +19268,7 @@ snapshots: history@4.10.1: dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 loose-envify: 1.4.0 resolve-pathname: 3.0.0 tiny-invariant: 1.3.3 @@ -21986,7 +21986,7 @@ snapshots: react-transition-group@4.4.5(react-dom@19.2.0(react@19.2.0))(react@19.2.0): dependencies: - '@babel/runtime': 7.28.4 + '@babel/runtime': 7.28.6 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1