Skip to content

feat: Lakeflow Jobs Plugin#265

Open
atilafassina wants to merge 27 commits intomainfrom
ekniazev/jobs-plugin-core
Open

feat: Lakeflow Jobs Plugin#265
atilafassina wants to merge 27 commits intomainfrom
ekniazev/jobs-plugin-core

Conversation

@atilafassina
Copy link
Copy Markdown
Contributor

Summary

Resource-scoped jobs plugin following the files plugin pattern. Jobs are configured as named resources discovered from environment variables at startup.

Design

  • Resource-scoped: Only configured jobs are accessible — this is not an open SDK wrapper
  • Env-var discovery: Jobs are discovered from DATABRICKS_JOB_<KEY> env vars (e.g. DATABRICKS_JOB_ETL=123)
  • Single-job shorthand: DATABRICKS_JOB_ID maps to the "default" key
  • Manifest declares job resources with CAN_MANAGE_RUN permission
  • Works with databricks apps init --features jobs

API

// Trigger a configured job
const { run_id } = await appkit.jobs("etl").runNow();

// Trigger and wait for completion
const run = await appkit.jobs("etl").runNowAndWait();

// OBO access
await appkit.jobs("etl").asUser(req).runNow();

// List recent runs
const runs = await appkit.jobs("etl").listRuns({ limit: 10 });

// Single-job shorthand
await appkit.jobs("default").runNow();

Files changed

  • plugins/jobs/manifest.json — declares job resource with CAN_MANAGE_RUN permission
  • plugins/jobs/types.tsJobAPI, JobHandle, JobsExport, IJobsConfig types
  • plugins/jobs/plugin.tsJobsPlugin with discoverJobs(), getResourceRequirements(), resource-scoped createJobAPI()
  • plugins/jobs/index.ts — barrel exports
  • connectors/jobs/client.tslistRuns now respects limit parameter
  • plugins/jobs/tests/plugin.test.ts — 32 tests covering discovery, resource requirements, exports, OBO, multi-job, and auto-fill

Documentation safety checklist

  • Examples use least-privilege permissions
  • Sensitive values are obfuscated
  • No insecure patterns introduced

Reopened from #223 — moved head branch from fork to upstream to fix CI secrets access.

keugenek and others added 24 commits April 2, 2026 17:04
Jobs are configured as named resources (DATABRICKS_JOB_<KEY> env vars)
and discovered at startup, following the files plugin pattern.

API is scoped to configured jobs:
  appkit.jobs('etl').runNow()
  appkit.jobs('etl').runNowAndWait()
  appkit.jobs('etl').lastRun()
  appkit.jobs('etl').listRuns()
  appkit.jobs('etl').asUser(req).runNow()

Single-job shorthand via DATABRICKS_JOB_ID env var.
Supports OBO access via asUser(req).

Co-authored-by: Isaac
Signed-off-by: Evgenii Kniazev <evgenii.kniazev@databricks.com>
Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…nfig

Phase 1 of jobs plugin rework: establishes config foundation with
TaskType union, three interceptor tiers (read/write/stream), and
pure parameter mapping for all 6 task types.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…reaming

Wave 2 of jobs plugin rework:
- Fix connector protobuf types (Waiter access pattern)
- Remove waitForRun from connector, add runAndWait async generator in plugin
- Wrap reads in execute() with JOBS_READ_DEFAULTS, writes with JOBS_WRITE_DEFAULTS
- Add Zod param validation and mapParams integration to runNow
- Enrich clientConfig() with JSON Schema per job
- Update JobAPI types and adapt existing tests

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
Phase 5 of jobs plugin rework: adds injectRoutes() with 4 endpoints:
- POST /:jobKey/run (with ?stream=true SSE support)
- GET /:jobKey/runs (paginated)
- GET /:jobKey/runs/:runId (detail)
- GET /:jobKey/status (latest run)
All routes use OBO via asUser(req) and validate job keys.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
- Fix ERR_HTTP_HEADERS_SENT crash when streaming errors occur after
  headers are flushed (check res.headersSent before setting status)
- Fix spark_jar/sql param mapping to use correct SDK fields
  (jar_params, sql_params instead of parameters)
- Forward abort signal from execute() interceptors to all connector
  calls for proper timeout/cancellation support
- Use Zod result.data after validation to preserve schema
  transforms and defaults
- Validate params even when omitted if a schema is configured
- Fix lastRun return type from Run to BaseRun (matches listRuns)
- Fix getJobId error message for default job key

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
The asUser() proxy wraps createJobAPI() in user context, but the
returned JobAPI closures called this.client after the ALS scope
exited, falling back to service principal. Fix by capturing the
client and userId eagerly at creation time and passing them
explicitly to execute() and connector calls.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
Migrate streaming to executeStream(), route polling through execute(),
use ValidationError, standardize eager captures, tighten types, expose
taskType in clientConfig, remove .job() alias, update manifest docs.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…ms before streaming

- Cap listRuns to [1, 100] in both connector and HTTP route to prevent
  unbounded memory materialization
- Reject negative runId (<=0) in GET/DELETE run handlers
- Validate Zod params before entering SSE stream branch so bad params
  return 400 JSON instead of a generic SSE error event

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
Jobs are configured as named resources (DATABRICKS_JOB_<KEY> env vars)
and discovered at startup, following the files plugin pattern.

API is scoped to configured jobs:
  appkit.jobs('etl').runNow()
  appkit.jobs('etl').runNowAndWait()
  appkit.jobs('etl').lastRun()
  appkit.jobs('etl').listRuns()
  appkit.jobs('etl').asUser(req).runNow()

Single-job shorthand via DATABRICKS_JOB_ID env var.
Supports OBO access via asUser(req).

Co-authored-by: Isaac
Signed-off-by: Evgenii Kniazev <evgenii.kniazev@databricks.com>
Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…nfig

Phase 1 of jobs plugin rework: establishes config foundation with
TaskType union, three interceptor tiers (read/write/stream), and
pure parameter mapping for all 6 task types.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…reaming

Wave 2 of jobs plugin rework:
- Fix connector protobuf types (Waiter access pattern)
- Remove waitForRun from connector, add runAndWait async generator in plugin
- Wrap reads in execute() with JOBS_READ_DEFAULTS, writes with JOBS_WRITE_DEFAULTS
- Add Zod param validation and mapParams integration to runNow
- Enrich clientConfig() with JSON Schema per job
- Update JobAPI types and adapt existing tests

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
Phase 5 of jobs plugin rework: adds injectRoutes() with 4 endpoints:
- POST /:jobKey/run (with ?stream=true SSE support)
- GET /:jobKey/runs (paginated)
- GET /:jobKey/runs/:runId (detail)
- GET /:jobKey/status (latest run)
All routes use OBO via asUser(req) and validate job keys.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
- Fix ERR_HTTP_HEADERS_SENT crash when streaming errors occur after
  headers are flushed (check res.headersSent before setting status)
- Fix spark_jar/sql param mapping to use correct SDK fields
  (jar_params, sql_params instead of parameters)
- Forward abort signal from execute() interceptors to all connector
  calls for proper timeout/cancellation support
- Use Zod result.data after validation to preserve schema
  transforms and defaults
- Validate params even when omitted if a schema is configured
- Fix lastRun return type from Run to BaseRun (matches listRuns)
- Fix getJobId error message for default job key

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
The asUser() proxy wraps createJobAPI() in user context, but the
returned JobAPI closures called this.client after the ALS scope
exited, falling back to service principal. Fix by capturing the
client and userId eagerly at creation time and passing them
explicitly to execute() and connector calls.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
Migrate streaming to executeStream(), route polling through execute(),
use ValidationError, standardize eager captures, tighten types, expose
taskType in clientConfig, remove .job() alias, update manifest docs.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…ms before streaming

- Cap listRuns to [1, 100] in both connector and HTTP route to prevent
  unbounded memory materialization
- Reject negative runId (<=0) in GET/DELETE run handlers
- Validate Zod params before entering SSE stream branch so bad params
  return 400 JSON instead of a generic SSE error event

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
…s-plugin-core

# Conflicts:
#	packages/appkit/src/index.ts
execute() returns ExecutionResult<T> (a discriminated union), but the
jobs plugin was treating it as plain T. This caused 9 TypeScript errors
and 6 test failures. Each call site now checks result.ok before
accessing result.data, matching the pattern established by the serving
plugin.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
Merge remote branch and fix execute() call sites: execute() returns
ExecutionResult<T> (discriminated union), but the jobs plugin treated
results as plain T. Each call site now checks result.ok before accessing
result.data, matching the pattern from the serving plugin.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
Copilot AI review requested due to automatic review settings April 10, 2026 20:40
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new resource-scoped “jobs” plugin to @databricks/appkit, following the existing “files” plugin pattern: jobs are discovered from environment variables at startup and exposed via a keyed accessor API, with HTTP routes for triggering and monitoring runs.

Changes:

  • Introduces plugins/jobs (manifest, defaults, params mapping, types, plugin implementation, and extensive tests).
  • Adds connectors/jobs with telemetry + cancellation support and updates exports/docs to surface the new plugin and types.
  • Updates templates and generated API docs/sidebars to include the new Jobs plugin.

Reviewed changes

Copilot reviewed 24 out of 25 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
template/appkit.plugins.json Adds “jobs” to plugin template metadata + resource description.
pnpm-lock.yaml Locks new dependency (zod@4.3.6) and related lockfile updates.
packages/appkit/src/plugins/jobs/types.ts Public types for Jobs plugin API/config (JobAPI/JobHandle/IJobsConfig).
packages/appkit/src/plugins/jobs/plugin.ts Core JobsPlugin: env discovery, dynamic resource requirements, API methods, HTTP routes.
packages/appkit/src/plugins/jobs/params.ts TaskType-based param mapping into SDK request fields.
packages/appkit/src/plugins/jobs/defaults.ts Execution defaults for read/write/stream operations.
packages/appkit/src/plugins/jobs/manifest.json Plugin manifest + config schema + baseline resource definition.
packages/appkit/src/plugins/jobs/index.ts Barrel exports for Jobs plugin/types.
packages/appkit/src/plugins/jobs/tests/plugin.test.ts Comprehensive unit tests for discovery, API, routes, and validation.
packages/appkit/src/plugins/index.ts Exposes jobs from the plugins barrel.
packages/appkit/src/index.ts Exposes jobs and related public types/configs from package root.
packages/appkit/src/connectors/jobs/client.ts JobsConnector SDK wrapper with telemetry instrumentation + limit handling.
packages/appkit/src/connectors/jobs/types.ts Connector config type (timeout/telemetry).
packages/appkit/src/connectors/jobs/index.ts Connector barrel exports.
packages/appkit/src/connectors/index.ts Exposes jobs connector from connectors barrel.
packages/appkit/package.json Adds zod dependency for runtime param schemas + JSON schema generation.
docs/docs/api/appkit/typedoc-sidebar.ts Adds new Jobs docs entries to sidebar.
docs/docs/api/appkit/*.md Adds generated API docs pages for Jobs plugin types.
docs/docs/api/appkit/index.md Adds Jobs-related types to API index list.
docs/docs/api/appkit/Interface.BasePluginConfig.md Updates “Extended by” list to include IJobsConfig.
Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@atilafassina atilafassina changed the title feat: add resource-scoped jobs plugin for Databricks Lakeflow Jobs feat: Lakeflow Jobs Plugin Apr 10, 2026
The StreamManager's close handlers cleaned up heartbeats and client
tracking but never aborted the stream's AbortController. This left
polling loops (e.g. jobs runAndWait) running in the background after
SSE connections closed, wasting resources until timeout.

Co-authored-by: Isaac
Signed-off-by: Atila Fassina <atila@fassina.eu>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants