Skip to content

Airflow DAGs

databaas_utils.airflow mirrors the user helpers, but authenticates through the pipeline's Airflow connection (a Zitadel service account) instead of a person. Setting up that connection is covered in the Databaas documentation: Creating an Airflow service account.

from databaas_utils.airflow import get_catalog, get_index, get_store, scoped, target_for

t = target_for(__file__)

with DAG(t.dag_id("daily_summary"), tags=t.tags("weather"), is_paused_upon_creation=t.paused):
    ...

def load():
    catalog = get_catalog(namespace=scoped("open_meteo"))
    store = get_store()
    index = get_index()

Helpers

Each takes conn_id= (default databaas_lakehouse) and namespace=.

Function Returns
get_catalog() A pyiceberg catalog
get_store() A blob store for generic tables
get_index() The Iceberg file index
get_zarr_group() A zarr.Group
get_settings() LakehouseSettings

You can also use the hook, LakehouseHook, which has the same get_* methods.

A missing or misspelt connection fails the task. It never falls back to local storage. Pass local_root="/tmp/x" to get_store() or get_zarr_group() if you want the filesystem explicitly, for example when running a DAG on your laptop.

Dev deployments and branch-safe data

CI deploys a branch of a pipeline next to production with databaas dag deploy, with dev_<key>_ prefixed DAG ids. Without care, that branch would still write to the production tables. Two helpers make names deployment-aware:

target_for(__file__) : Reads the _databaas_target.json that CI wrote into the bundle. Gives you dag_id(name), tags(...), paused, is_production and doc_suffix().

scoped(name) : Returns name in production and name_dev_<key> on a branch.

table = scoped("daily_summary")   # "daily_summary" or "daily_summary_dev_add_login"

Call scoped() directly from the DAG file

It finds the deployment from the file that called it. Called from a helper module, it raises. Pass the DAG's path explicitly instead: scoped("daily_summary", caller_file=__file__).

from databaas_target import target still works for older DAGs, but is deprecated and removed in 2.0.

Testing a DAG locally

Set the connection in the environment, with the service account's JSON key. No Airflow database is needed for this route:

export AIRFLOW_CONN_DATABAAS_LAKEHOUSE="$(python -c '
import json, sys
print(json.dumps({
    "host": "https://catalog.<your-databaas-domain>",
    "extra": {
        "service_account_key": json.load(open(sys.argv[1])),
        "token_url": "https://auth.<your-databaas-domain>/oauth/v2/token",
        "scope": "openid urn:zitadel:iam:org:project:id:<project id>:aud",
    },
}))' key.json)"

The warehouse defaults to databaas-lakehouse; add "schema" for another one. Set scope rather than only project_id: a service account's token carries the project as audience only when the scope asks for it with :aud, and Trino rejects it otherwise.