Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,20 @@ export DATABRICKS_APP_NAME=your-app-name # The name of the app to deploy. If not
export DATABRICKS_WORKSPACE_DIR=your-workspace-dir # The source workspace directory to deploy the app from. It will be used to construct the absolute path: /Workspace/Users/{your-username}/{workspace-dir}
```

## Generating App templates

To generate app templates, run the following command:

```bash
pnpm generate:app-templates
```

By default, the command will generate app templates in the `../app-templates` directory, assuming that you have the [`app-templates`](https://github.com/databricks/app-templates) repository cloned in the same parent directory as this one.

You can override the output directory by setting the `APP_TEMPLATES_OUTPUT_DIR` environment variable.

By default, the command will use the `databricks` CLI to generate the app templates. You can override the CLI by setting the `DATABRICKS_CLI` environment variable to provide a different binary name or path.

## Contributing to AppKit documentation

The `docs/` directory contains the AppKit documentation site, built with Docusaurus.
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"build:watch": "pnpm -r --filter=!dev-playground --filter=!docs build:watch",
"check:fix": "biome check --write .",
"check": "biome check .",
"generate:app-templates": "tsx tools/generate-app-templates.ts",
"check:licenses": "tsx tools/check-licenses.ts",
"build:notice": "tsx tools/build-notice.ts > NOTICE.md",
"deploy:playground": "pnpm pack:sdk && tsx tools/playground/deploy-playground.ts",
Expand Down
43 changes: 34 additions & 9 deletions template/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
# Minimal Databricks App
# {{.projectName}}

A minimal Databricks App powered by Databricks AppKit, featuring React, TypeScript, and Tailwind CSS.
A Databricks App powered by [AppKit](https://databricks.github.io/appkit/), featuring React, TypeScript, and Tailwind CSS.

**Enabled plugins:**
{{- if .plugins.analytics}}
- **Analytics** -- SQL query execution against Databricks SQL Warehouses
{{- end}}
{{- if .plugins.lakebase}}
- **Lakebase** -- Fully managed Postgres database for transactional (OLTP) workloads on Databricks
{{- end}}
- **Server** -- Express HTTP server with static file serving and Vite dev mode

## Prerequisites

Expand All @@ -15,7 +24,7 @@ A minimal Databricks App powered by Databricks AppKit, featuring React, TypeScri
For local development, configure your environment variables by creating a `.env` file:

```bash
cp env.example .env
cp .env.example .env
```

Edit `.env` and set the environment variables you need:
Expand All @@ -25,6 +34,12 @@ DATABRICKS_HOST=https://your-workspace.cloud.databricks.com
DATABRICKS_APP_PORT=8000
# ... other environment variables, depending on the plugins you use
```
{{- if .plugins.lakebase}}

#### Lakebase Configuration

The Lakebase plugin requires additional environment variables for PostgreSQL connectivity. To learn how to configure the Lakebase plugin, see the [Lakebase plugin documentation](https://databricks.github.io/appkit/docs/plugins/lakebase).
{{- end}}

### CLI Authentication

Expand Down Expand Up @@ -57,7 +72,7 @@ client_secret = prod-client-secret
Deploy using a specific profile:

```bash
databricks bundle deploy -t prod --profile production
databricks bundle deploy --profile production
```

**Note:** Personal Access Tokens (PATs) are legacy authentication. OAuth is strongly recommended for better security.
Expand Down Expand Up @@ -90,7 +105,7 @@ npm run build

This creates:

- `dist/server/` - Compiled server code
- `dist/server.js` - Compiled server bundle
- `client/dist/` - Bundled client assets

### Production
Expand Down Expand Up @@ -126,12 +141,18 @@ Update `databricks.yml` with your workspace settings:

```yaml
targets:
dev:
default:
workspace:
host: https://your-workspace.cloud.databricks.com
{{- if .plugins.analytics}}
variables:
warehouse_id: your-warehouse-id
sql_warehouse_id: your-warehouse-id
{{- end}}
```
{{- if .plugins.analytics}}

Make sure to set the `sql_warehouse_id` variable to your Databricks SQL Warehouse ID.
{{- end}}

### 2. Validate Bundle

Expand All @@ -141,10 +162,10 @@ databricks bundle validate

### 3. Deploy

Deploy to the development target:
Deploy to the default target:

```bash
databricks bundle deploy -t dev
databricks bundle deploy
```

### 4. Run
Expand Down Expand Up @@ -174,6 +195,10 @@ databricks bundle deploy -t prod
* server.ts # Server entry point
* routes/ # Routes
* shared/ # Shared types
{{- if .plugins.analytics}}
* config/ # Configuration
* queries/ # SQL query files
{{- end}}
* databricks.yml # Bundle configuration
* app.yaml # App configuration
* .env.example # Environment variables example
Expand Down
2 changes: 1 addition & 1 deletion template/client/src/pages/analytics/AnalyticsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { sql } from "@databricks/appkit-ui/js";
import { useState } from 'react';

export function AnalyticsPage() {
const { data, loading, error } = useAnalyticsQuery('hello_world', {
const { data, loading, error } = useAnalyticsQuery<{ value: string }[]>('hello_world', {
message: sql.string('hello world'),
});

Expand Down
2 changes: 1 addition & 1 deletion template/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "{{.projectName}}",
"version": "1.0.0",
"main": "build/index.js",
"main": "dist/server.js",
"type": "module",
"scripts": {
"start": "NODE_ENV=production node --env-file-if-exists=./.env ./dist/server.js",
Expand Down
4 changes: 2 additions & 2 deletions template/playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export default defineConfig({
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: `http://localhost:${process.env.PORT || 8000}`,
baseURL: `http://localhost:${process.env.DATABRICKS_APP_PORT || process.env.PORT || 8000}`,
trace: 'on-first-retry',
},
projects: [
Expand All @@ -19,7 +19,7 @@ export default defineConfig({
],
webServer: {
command: 'npm run dev',
url: `http://localhost:${process.env.PORT || 8000}`,
url: `http://localhost:${process.env.DATABRICKS_APP_PORT || process.env.PORT || 8000}`,
reuseExistingServer: !process.env.CI,
timeout: 120 * 1000,
},
Expand Down
74 changes: 60 additions & 14 deletions template/tests/smoke.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,77 @@ import { test, expect } from '@playwright/test';
import { writeFileSync, mkdirSync } from 'node:fs';
import { join } from 'node:path';

// ── Templated configuration (resolved by `databricks apps init`) ────────────
const APP_CONFIG = {
name: '{{.projectName}}',
plugins: [
{{- if .plugins.analytics}}
'analytics',
{{- end}}
{{- if .plugins.lakebase}}
'lakebase',
{{- end}}
],
} as const;

interface PluginPage {
navLabel: string;
path: string;
expectedTexts: string[];
}

const PLUGIN_PAGES: Record<string, PluginPage> = {
analytics: {
navLabel: 'Analytics',
path: '/analytics',
expectedTexts: ['SQL Query Result', 'Sales Data Filter'],
},
lakebase: {
navLabel: 'Lakebase',
path: '/lakebase',
expectedTexts: ['Todo List'],
},
};

const enabledPages = Object.entries(PLUGIN_PAGES).filter(
([key]) => APP_CONFIG.plugins.includes(key),
);

// ── Tests ───────────────────────────────────────────────────────────────────

let testArtifactsDir: string;
let consoleLogs: string[] = [];
let consoleErrors: string[] = [];
let pageErrors: string[] = [];
let failedRequests: string[] = [];

test('smoke test - app loads and displays data', async ({ page }) => {
// Navigate to the app
test('smoke test - app loads and displays home page', async ({ page }) => {
await page.goto('/');

// ⚠️ UPDATE THESE SELECTORS after customizing App.tsx:
// - Change heading name to match your app title
// - Change data selector to match your primary data display
await expect(page.getByRole('heading', { name: 'Minimal Databricks App' })).toBeVisible();
await expect(page.getByText('hello world', { exact: true })).toBeVisible({ timeout: 30000 });

// Wait for health check to complete (wait for "OK" status)
await expect(page.getByText('OK')).toBeVisible({ timeout: 30000 });
await expect(page.getByRole('heading', { name: APP_CONFIG.name })).toBeVisible();
await expect(
page.getByRole('heading', { name: 'Welcome to your Databricks App' }),
).toBeVisible();
await expect(page.getByText('Getting Started')).toBeVisible();

// Verify console logs were captured
expect(consoleLogs.length).toBeGreaterThan(0);
expect(consoleErrors.length).toBe(0);
expect(pageErrors.length).toBe(0);
await expect(page.getByRole('link', { name: 'Home' })).toBeVisible();
for (const [, plugin] of enabledPages) {
await expect(page.getByRole('link', { name: plugin.navLabel })).toBeVisible();
}
});

for (const [name, plugin] of enabledPages) {
test(`smoke test - ${name} page loads`, async ({ page }) => {
await page.goto(plugin.path);

for (const text of plugin.expectedTexts) {
await expect(page.getByText(text)).toBeVisible();
}
});
}

// ── Lifecycle hooks ─────────────────────────────────────────────────────────

test.beforeEach(async ({ page }) => {
consoleLogs = [];
consoleErrors = [];
Expand Down
Loading