Skip to content

Commit 717d8cf

Browse files
Jcohen0102705815
andauthored
New dagster-dataform Integration (#227)
* [dagster-dataform] Initial README * [dagster-dataform] Root files * [dagster-dataform] Integration modules * [dagster-dataform] Integration unit tests * [dagster-dataform] Add prereq for freshness checks --------- Co-authored-by: 2705815 <Justin.Cohen2@aetna.com>
1 parent 091abd7 commit 717d8cf

15 files changed

Lines changed: 4491 additions & 0 deletions
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
install:
2+
uv sync
3+
4+
build:
5+
uv build
6+
7+
test:
8+
uv run pytest
9+
10+
ruff:
11+
uv run ruff check --fix
12+
uv run ruff format
13+
14+
check:
15+
uv run pyright
Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
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]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from dagster._core.libraries import DagsterLibraryRegistry
2+
3+
__version__ = "0.0.1"
4+
5+
DagsterLibraryRegistry.register(
6+
"example-integration", __version__, is_dagster_package=False
7+
)

0 commit comments

Comments
 (0)