|
| 1 | +# Dagster Dataform Integration |
| 2 | + |
| 3 | +A Dagster integration for Google Cloud Dataform that provides asset definitions and workflow monitoring capabilities. |
| 4 | + |
| 5 | +## Core Features |
| 6 | + |
| 7 | +### Observability |
| 8 | +- **Asset Discovery**: Automatically creates Dagster assets from Dataform compilation results |
| 9 | +- **Workflow Monitoring**: Real-time monitoring of Dataform workflow invocations via sensors |
| 10 | +- **Rich Metadata**: Asset metadata (retrieved at definition time) and materialization metadata (captured at runtime) offer a great degree of detail of remote BQ assets. |
| 11 | +- **SQL Query Tracking**: Preserves and displays SQL queries with proper formatting |
| 12 | +- **GCP Integration**: Seamless integration with Google Cloud Platform using default credentials |
| 13 | + |
| 14 | +### Orchestration |
| 15 | +Orchestrate Dataform invocations remotely via configuration of a dagster schedule automation. This configuration offers more flexibility than release and workflow configurations through the GCP Console in Dataform, including settings like assertion schema. |
| 16 | + |
| 17 | +### Alerting |
| 18 | +- Developed with flexible alerting in mind; users can define their own Dagster jobs to pass at definition time that will be run upon Dataform asset execution failure or orchestration job failure. |
| 19 | + |
| 20 | +## Installation |
| 21 | + |
| 22 | +### Prerequisites |
| 23 | + |
| 24 | +- Python 3.9 or higher |
| 25 | +- Google Cloud Platform project with Dataform enabled |
| 26 | +- Appropriate GCP credentials configured |
| 27 | + |
| 28 | +### Install the Package |
| 29 | + |
| 30 | +```bash |
| 31 | +pip install dagster-dataform |
| 32 | +``` |
| 33 | + |
| 34 | +Or install from source: |
| 35 | + |
| 36 | +```bash |
| 37 | +git clone <repository-url> |
| 38 | +cd dagster-dataform |
| 39 | +pip install -e . |
| 40 | +``` |
| 41 | + |
| 42 | +## Configuration |
| 43 | + |
| 44 | +### GCP Authentication |
| 45 | + |
| 46 | +This integration uses Google Cloud's default application credentials. The following authentication methods are supported: |
| 47 | + |
| 48 | +1. **Service Account Key File**: |
| 49 | + ```bash |
| 50 | + export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account-key.json" |
| 51 | + ``` |
| 52 | + |
| 53 | +2. **Application Default Credentials**: |
| 54 | + ```bash |
| 55 | + gcloud auth application-default login |
| 56 | + ``` |
| 57 | + |
| 58 | +3. **Workload Identity** (for GKE): |
| 59 | + - Configure workload identity in your Kubernetes cluster |
| 60 | + - The integration will automatically use the service account attached to the pod |
| 61 | + |
| 62 | +4. **Compute Engine/Cloud Run Service Account**: |
| 63 | + - When running on GCP services, the integration will use the default service account |
| 64 | + |
| 65 | +### Required Permissions |
| 66 | + |
| 67 | +Your GCP credentials must have the following permissions: |
| 68 | + |
| 69 | +- `dataform.compilationResults.list` |
| 70 | +- `dataform.compilationResults.get` |
| 71 | +- `dataform.workflowInvocations.list` |
| 72 | +- `dataform.workflowInvocations.get` |
| 73 | +- `bigquery.jobs.get` (for job status monitoring) |
| 74 | + |
| 75 | +## Usage |
| 76 | + |
| 77 | +### Basic Setup |
| 78 | + |
| 79 | +Create a `definitions.py` file in your Dagster project: |
| 80 | + |
| 81 | +```python |
| 82 | +from dagster_dataform.resources import DataformRepositoryResource |
| 83 | +from dagster_dataform.dataform_polling_sensor import ( |
| 84 | + create_dataform_workflow_invocation_sensor, |
| 85 | +) |
| 86 | +from dagster_dataform.resources import load_dataform_assets |
| 87 | +from dagster_dataform.asset_event_sensor import create_asset_event_sensor |
| 88 | +from dagster_dataform.dataform_orchestration_schedule import ( |
| 89 | + create_dataform_orchestration_schedule, |
| 90 | +) |
| 91 | + |
| 92 | +from dagster import Definitions |
| 93 | + |
| 94 | +resource = DataformRepositoryResource( |
| 95 | + project_id="anbc-dev-hcm-cm-de", |
| 96 | + repository_id="clin-analytics-nexus-dataform", |
| 97 | + location="us-east4", |
| 98 | + environment="dev", |
| 99 | + sensor_minimum_interval_seconds=30, |
| 100 | +) |
| 101 | + |
| 102 | +assets = load_dataform_assets(resource, fresh_policy_lag_minutes=1440) |
| 103 | + |
| 104 | +dataform_workflow_invocation_sensor = create_dataform_workflow_invocation_sensor( |
| 105 | + resource, |
| 106 | + exclusion_patterns=[ |
| 107 | + r"^conform_.*", |
| 108 | + r"^stg_.*", |
| 109 | + r"^curated_.*", |
| 110 | + r"^consume_.*", |
| 111 | + ], |
| 112 | + minutes_ago=20, |
| 113 | +) |
| 114 | + |
| 115 | +dataform_orchestration_schedule = create_dataform_orchestration_schedule( |
| 116 | + resource=resource, |
| 117 | + cron_schedule="*/2 * * * *", |
| 118 | + git_commitish=resource.environment, |
| 119 | +) |
| 120 | + |
| 121 | +defs = Definitions( |
| 122 | + assets=assets, |
| 123 | + sensors=[dataform_workflow_invocation_sensor], |
| 124 | + schedules=[dataform_orchestration_schedule], |
| 125 | +) |
| 126 | +``` |
| 127 | + |
| 128 | +For freshness checks to show up in the Dagster UI, add a `dagster.yaml` file at the root of your project with the following content: |
| 129 | + |
| 130 | +```yaml |
| 131 | +freshness: |
| 132 | + enabled: True |
| 133 | +``` |
| 134 | +
|
| 135 | +### Library Objects and Functions |
| 136 | +
|
| 137 | +This section describes the core objects and functions provided by the dagster-dataform integration, including their parameters, return types, and usage examples. |
| 138 | +
|
| 139 | +#### DataformRepositoryResource |
| 140 | +
|
| 141 | +The main resource class that provides access to Google Cloud Dataform services. This resource handles authentication, API calls, and data retrieval from your Dataform repository. |
| 142 | +
|
| 143 | +**Constructor Parameters:** |
| 144 | +- `project_id` (str, required): Your Google Cloud Platform project ID |
| 145 | +- `repository_id` (str, required): Your Dataform repository ID |
| 146 | +- `location` (str, required): GCP region where Dataform is deployed (e.g., "us-central1", "us-east4") |
| 147 | +- `environment` (str, required): Environment name that matches your Dataform branch (e.g., "dev", "prod", "main") |
| 148 | +- `sensor_minimum_interval_seconds` (int, optional): Minimum polling interval for sensors in seconds (default: 120) |
| 149 | + |
| 150 | +**Key Methods:** |
| 151 | +- `create_compilation_result()`: Creates a new compilation result in Dataform |
| 152 | +- `create_workflow_invocation()`: Initiates a workflow execution |
| 153 | +- `get_workflow_invocation_details()`: Retrieves status and details of workflow executions |
| 154 | +- `list_compilation_results()`: Lists available compilation results |
| 155 | +- `query_compilation_result_actions()`: Queries actions from compilation results |
| 156 | + |
| 157 | +**Example:** |
| 158 | +```python |
| 159 | +resource = DataformRepositoryResource( |
| 160 | + project_id="my-gcp-project", |
| 161 | + repository_id="my-dataform-repo", |
| 162 | + location="us-central1", |
| 163 | + environment="dev", |
| 164 | + sensor_minimum_interval_seconds=30, |
| 165 | +) |
| 166 | +``` |
| 167 | + |
| 168 | +#### `load_dataform_assets()` |
| 169 | + |
| 170 | +Automatically discovers and creates Dagster assets from your Dataform compilation results. This function analyzes your Dataform repository and generates asset definitions with proper dependencies and metadata. |
| 171 | + |
| 172 | +**Parameters:** |
| 173 | +- `resource` (DataformRepositoryResource, required): The configured Dataform repository resource |
| 174 | +- `fresh_policy_lag_minutes` (int, optional): Freshness policy lag in minutes for data quality monitoring (default: 1440) |
| 175 | + |
| 176 | +**Returns:** |
| 177 | +- `List[AssetsDefinition]`: A list of Dagster asset definitions representing your Dataform tables and views |
| 178 | + |
| 179 | +**Features:** |
| 180 | +- Automatically infers asset dependencies from Dataform relationships |
| 181 | +- Includes rich metadata (SQL code, project info, documentation links) |
| 182 | +- Applies configurable freshness policies |
| 183 | +- Handles different asset types (tables, views, assertions) |
| 184 | + |
| 185 | +**Example:** |
| 186 | +```python |
| 187 | +assets = load_dataform_assets( |
| 188 | + resource=resource, |
| 189 | + fresh_policy_lag_minutes=1440 # 24 hours |
| 190 | +) |
| 191 | +``` |
| 192 | + |
| 193 | +#### `create_dataform_workflow_invocation_sensor()` |
| 194 | + |
| 195 | +Creates a Dagster sensor that monitors Dataform workflow invocations and triggers notifications or actions based on their status. The sensor polls for new workflow executions and creates asset materializations or observations. |
| 196 | + |
| 197 | +**Parameters:** |
| 198 | +- `resource` (DataformRepositoryResource, required): The configured Dataform repository resource |
| 199 | +- `minutes_ago` (int, required): How far back in time to look for workflow invocations |
| 200 | +- `exclusion_patterns` (List[str], optional): Regex patterns to exclude certain assets from monitoring (ex. assets from workspaces with table prefixes) |
| 201 | +- `job` (JobDefinition, optional): Custom job to run when sensor observes a workflow invocation failure (default: creates a notification job) |
| 202 | + |
| 203 | +**Returns:** |
| 204 | +- `SensorDefinition`: A configured Dagster sensor that monitors workflow invocations |
| 205 | + |
| 206 | +**Behavior:** |
| 207 | +- Polls for workflow invocations within the specified time window |
| 208 | +- Creates asset materializations for successful runs |
| 209 | +- Creates asset observations for failed or in-progress runs |
| 210 | +- Includes metadata like SQL queries, BigQuery job IDs, and execution status |
| 211 | +- Can trigger custom jobs for failure handling |
| 212 | + |
| 213 | +**Example:** |
| 214 | +```python |
| 215 | +sensor = create_dataform_workflow_invocation_sensor( |
| 216 | + resource=resource, |
| 217 | + minutes_ago=20, |
| 218 | + exclusion_patterns=[ |
| 219 | + r"^staging_.*", |
| 220 | + r"^temp_.*", |
| 221 | + ], |
| 222 | +) |
| 223 | +``` |
| 224 | + |
| 225 | +#### `create_dataform_orchestration_schedule()` |
| 226 | + |
| 227 | +Creates a Dagster schedule that orchestrates Dataform workflow executions on a cron schedule. This function provides programmatic control over when and how Dataform workflows are triggered, offering more flexibility than GCP Console configurations. |
| 228 | + |
| 229 | +**Parameters:** |
| 230 | +- `resource` (DataformRepositoryResource, required): The configured Dataform repository resource |
| 231 | +- `cron_schedule` (str, required): Cron expression defining when to run the schedule (e.g., "0 0 * * *" for daily at midnight) |
| 232 | +- `git_commitish` (str, required): Git branch, tag, or commit hash to use for compilation |
| 233 | +- `default_database` (str, optional): Default database override for compilation |
| 234 | +- `default_schema` (str, optional): Default schema override for compilation |
| 235 | +- `default_location` (str, optional): Default location override for compilation |
| 236 | +- `assertion_schema` (str, optional): Schema for data quality assertions |
| 237 | +- `database_suffix` (str, optional): Suffix to append to database names |
| 238 | +- `schema_suffix` (str, optional): Suffix to append to schema names |
| 239 | +- `table_prefix` (str, optional): Prefix to prepend to table names |
| 240 | +- `builtin_assertion_name_prefix` (str, optional): Prefix for built-in assertion names |
| 241 | +- `vars` (Dict[str, Any], optional): Variables to pass to the Dataform compilation |
| 242 | + |
| 243 | +**Returns:** |
| 244 | +- `ScheduleDefinition`: A configured Dagster schedule that orchestrates Dataform workflows |
| 245 | + |
| 246 | +**Features:** |
| 247 | +- Creates compilation results with specified parameters |
| 248 | +- Initiates workflow invocations automatically |
| 249 | +- Monitors execution status with configurable timeout (30 minutes default) |
| 250 | +- Handles success/failure scenarios with proper error reporting |
| 251 | +- Provides detailed logging throughout the process |
| 252 | + |
| 253 | +**Example:** |
| 254 | +```python |
| 255 | +schedule = create_dataform_orchestration_schedule( |
| 256 | + resource=resource, |
| 257 | + cron_schedule="0 2 * * *", # Daily at 2 AM |
| 258 | + git_commitish="main", |
| 259 | + default_database="analytics", |
| 260 | + assertion_schema="data_quality", |
| 261 | + vars={"environment": "production"} |
| 262 | +) |
| 263 | +``` |
| 264 | + |
| 265 | +### Example Asset Metadata |
| 266 | + |
| 267 | +Each asset includes rich metadata: |
| 268 | + |
| 269 | +```python |
| 270 | +{ |
| 271 | + "Project ID": "your-gcp-project-id", |
| 272 | + "Dataset": "your_dataset", |
| 273 | + "Asset Name": "your_table_name", |
| 274 | + "Asset SQL Code": "```sql\nSELECT * FROM source_table\n```", |
| 275 | + "Docs Link": "https://your-docs-link" |
| 276 | +} |
| 277 | +``` |
| 278 | + |
| 279 | +## Running the Integration |
| 280 | + |
| 281 | +### Start Dagster UI |
| 282 | + |
| 283 | +```bash |
| 284 | +dagster dev |
| 285 | +``` |
| 286 | + |
| 287 | +### View Assets |
| 288 | + |
| 289 | +1. Navigate to the Dagster UI |
| 290 | +2. Go to the "Assets" tab |
| 291 | +3. You'll see all your Dataform assets listed |
| 292 | +4. Click on any asset to view its metadata, dependencies, and materialization history |
| 293 | + |
| 294 | +### Monitor Workflows |
| 295 | + |
| 296 | +1. Go to the "Sensors" tab |
| 297 | +2. Find the `dataform_workflow_invocation_sensor` |
| 298 | +3. The sensor will automatically poll for new workflow invocations |
| 299 | +4. View materialization events in the asset history |
| 300 | + |
| 301 | +## Development |
| 302 | + |
| 303 | +### Running Tests |
| 304 | + |
| 305 | +```bash |
| 306 | +# Run all tests |
| 307 | +uv run pytest |
| 308 | + |
| 309 | +# Run specific test files |
| 310 | +uv run pytest dagster_dataform_tests/test_utils.py |
| 311 | +uv run pytest dagster_dataform_tests/test_resources.py |
| 312 | +uv run pytest dagster_dataform_tests/test_dataform_orchestration_schedule.py |
| 313 | +``` |
| 314 | + |
| 315 | +### Building the Package |
| 316 | + |
| 317 | +```bash |
| 318 | +# Install in development mode |
| 319 | +uv sync --dev |
| 320 | + |
| 321 | +# Build package |
| 322 | +uv run python -m build |
| 323 | +``` |
| 324 | + |
| 325 | +### Local Development |
| 326 | + |
| 327 | +```bash |
| 328 | +# Install in development mode |
| 329 | +uv sync --dev |
| 330 | + |
| 331 | +# Run with your local Dagster instance |
| 332 | +uv run dagster dev |
| 333 | +``` |
| 334 | + |
| 335 | +## Troubleshooting |
| 336 | + |
| 337 | +### Common Issues |
| 338 | + |
| 339 | +1. **Authentication Errors**: |
| 340 | + - Ensure `GOOGLE_APPLICATION_CREDENTIALS` is set correctly |
| 341 | + - Verify your service account has the required permissions |
| 342 | + - Check that the Dataform API is enabled in your GCP project |
| 343 | + |
| 344 | +2. **No Assets Found**: |
| 345 | + - Verify your Dataform repository has been compiled |
| 346 | + - Check that the `environment` parameter matches your Dataform branch |
| 347 | + - Ensure the repository ID is correct |
| 348 | + |
| 349 | +3. **Sensor Not Triggering**: |
| 350 | + - Check the sensor's minimum interval configuration |
| 351 | + - Verify that workflow invocations exist in the specified time window |
| 352 | + - Review sensor logs for any errors |
| 353 | + |
| 354 | +### Debug Mode |
| 355 | + |
| 356 | +Enable debug logging by setting the log level: |
| 357 | + |
| 358 | +```python |
| 359 | +import logging |
| 360 | +logging.basicConfig(level=logging.DEBUG) |
| 361 | +``` |
| 362 | + |
| 363 | +## Contributing |
| 364 | + |
| 365 | +1. Fork the repository |
| 366 | +2. Create a feature branch |
| 367 | +3. Make your changes |
| 368 | +4. Add tests for new functionality |
| 369 | +5. Submit a pull request |
| 370 | + |
| 371 | +## License |
| 372 | + |
| 373 | +[Add your license information here] |
0 commit comments