Commit 9d15a66
Add asset check and partition status query commands to dg api (#22132)
Implement new query capabilities for asset checks and partition status, adding missing commands that `dagctl` supports. Introduces a new `dg api asset-check` command group with `list` and `get-executions` subcommands, plus `dg api asset get-partition-status` for partition materialization stats.
**Objective #22131:** Objective: Add missing query commands to dg api
## Key Changes
- New `asset-check` command group with `list` and `get-executions` subcommands for querying check metadata and execution history
- New `asset get-partition-status` command to fetch partition materialization statistics (materialized, failed, in-progress, total)
- Complete 4-layer implementation across schemas, GraphQL adapter, API, and CLI layers following existing patterns
- Comprehensive test coverage with snapshot tests for formatting and dynamic command execution recording
- GraphQL queries for `assetChecksOrError`, `assetCheckExecutions`, and asset partition stats with proper error handling
<details>
<summary>Files Changed</summary>
### Added (9 files)
- `dagster-oss/.../dagster_dg_cli/api_layer/api/asset_check.py` - Asset check API with list and get_check_executions methods
- `dagster-oss/.../dagster_dg_cli/api_layer/graphql_adapter/asset_check.py` - GraphQL queries and response processors for asset checks
- `dagster-oss/.../dagster_dg_cli/api_layer/schemas/asset_check.py` - Pydantic models for checks and executions
- `dagster-oss/.../dagster_dg_cli/cli/api/asset_check.py` - Click commands for asset-check group
- `dagster_dg_cli_tests/cli_tests/api_tests/asset_check_tests/` - Complete test suite with scenarios, snapshots, and recordings
### Modified (6 files)
- `asset.py` (schemas) - Added DgApiPartitionStats model
- `asset.py` (graphql_adapter) - Added ASSET_PARTITION_STATUS_QUERY and get_asset_partition_status_via_graphql function
- `asset.py` (api) - Added get_partition_status method to DgApiAssetApi
- `asset.py` (cli) - Added get-partition-status command to asset group
- `cli_group.py` - Registered asset-check command group
- `formatters.py` - Added format_asset_checks, format_asset_check_executions, format_partition_status
</details>
## User Experience
**Before:**
```
$ dg api asset-check list --asset-key my/asset
Error: No such command 'asset-check'
```
**After:**
```
$ dg api asset-check list --asset-key my/asset
NAME BLOCKING DESCRIPTION
freshness_check Yes Checks that the asset is materialized within the last 24 ...
row_count_check No Validates minimum row count
$ dg api asset get-partition-status my/asset
Materialized 10
Failed 1
In Progress 0
Total 20
```
Users can now query asset checks and partition materialization stats directly from the CLI, matching the capabilities previously only available in `dagctl`.
<details>
<summary>original-plan</summary>
# Plan: Asset checks + partition status commands
Part of Objective #22131, Steps 1.1–1.3
## Context
`dg api` is missing query commands that `dagctl` supports: listing asset checks, getting check execution history, and getting partition materialization stats. This PR adds a new `dg api asset-check` top-level command group and a `dg api asset get-partition-status` subcommand.
## Changes
### 1. New `asset-check` command group (Steps 1.1 + 1.2)
Create 4 new files following the existing 4-layer pattern (schedule/sensor are the closest models):
**Schema** — `dagster_dg_cli/api_layer/schemas/asset_check.py`
- `DgApiAssetCheck` — name, asset_key, description, blocking, job_names, can_execute_individually
- `DgApiAssetCheckList` — items list
- `DgApiAssetCheckExecutionStatus` enum — IN_PROGRESS, SUCCEEDED, FAILED, SKIPPED, EXECUTION_FAILED
- `DgApiAssetCheckExecution` — id, run_id, status, timestamp, partition, step_key, check_name, asset_key
- `DgApiAssetCheckExecutionList` — items list with cursor/has_more
**GraphQL adapter** — `dagster_dg_cli/api_layer/graphql_adapter/asset_check.py`
- `ASSET_CHECKS_QUERY` — query `assetChecksOrError(assetKey)` returning check name, description, blocking, jobNames, canExecuteIndividually
- `ASSET_CHECK_EXECUTIONS_QUERY` — query `assetCheckExecutions(assetKey, checkName, limit, cursor)` returning execution details
- Process functions for each query
**API class** — `dagster_dg_cli/api_layer/api/asset_check.py`
- `DgApiAssetCheckApi(client: IGraphQLClient)` frozen dataclass
- `list_asset_checks(asset_key: str)` method
- `get_check_executions(asset_key: str, check_name: str, limit: int, cursor: str | None)` method
**CLI** — `dagster_dg_cli/cli/api/asset_check.py`
- `dg api asset-check list --asset-key ASSET_KEY [--json]`
- `dg api asset-check get-executions --asset-key ASSET_KEY --check-name CHECK_NAME [--limit N] [--cursor C] [--json]`
- `asset_check_group` click group
**Formatters** — add to `dagster_dg_cli/cli/api/formatters.py`
- `format_asset_checks(checks, as_json)` — table: NAME | BLOCKING | DESCRIPTION
- `format_asset_check_executions(executions, as_json)` — table: STATUS | RUN_ID | TIMESTAMP | PARTITION
### 2. `dg api asset get-partition-status` (Step 1.3)
Extend existing asset files:
**Schema** — add to `dagster_dg_cli/api_layer/schemas/asset.py`
- `DgApiPartitionStats` — num_materialized, num_failed, num_materializing, num_partitions
**GraphQL adapter** — add to `dagster_dg_cli/api_layer/graphql_adapter/asset.py`
- `ASSET_PARTITION_STATUS_QUERY` — query `assetNodeOrError(assetKey)` with `partitionStats { numMaterialized numPartitions numFailed numMaterializing }`
- `get_asset_partition_status_via_graphql()` function
**API class** — add to `dagster_dg_cli/api_layer/api/asset.py`
- `get_partition_status(asset_key: str)` method on `DgApiAssetApi`
**CLI** — add to `dagster_dg_cli/cli/api/asset.py`
- `dg api asset get-partition-status ASSET_KEY [--json]`
- Register in asset_group commands dict
**Formatters** — add to `dagster_dg_cli/cli/api/formatters.py`
- `format_partition_status(stats, as_json)` — detail view with materialized/failed/in-progress/total
### 3. Registration
**`dagster_dg_cli/cli/api/cli_group.py`** — add `asset-check` entry:
```python
from dagster_dg_cli.cli.api.asset_check import asset_check_group
# in commands dict:
"asset-check": asset_check_group,
```
## Files to create
- `dagster-oss/.../dagster_dg_cli/api_layer/schemas/asset_check.py`
- `dagster-oss/.../dagster_dg_cli/api_layer/graphql_adapter/asset_check.py`
- `dagster-oss/.../dagster_dg_cli/api_layer/api/asset_check.py`
- `dagster-oss/.../dagster_dg_cli/cli/api/asset_check.py`
## Files to modify
- `dagster-oss/.../dagster_dg_cli/api_layer/schemas/asset.py` — add `DgApiPartitionStats`
- `dagster-oss/.../dagster_dg_cli/api_layer/graphql_adapter/asset.py` — add partition status query
- `dagster-oss/.../dagster_dg_cli/api_layer/api/asset.py` — add `get_partition_status` method
- `dagster-oss/.../dagster_dg_cli/cli/api/asset.py` — add `get-partition-status` command, register in group
- `dagster-oss/.../dagster_dg_cli/cli/api/cli_group.py` — register `asset-check` group
- `dagster-oss/.../dagster_dg_cli/cli/api/formatters.py` — add 3 formatter functions
## Key patterns to follow
- Use `@dg_response_schema`, `@dg_api_options(deployment_scoped=True)`, `@cli_telemetry_wrapper` decorators
- Use `DagsterPlusCliConfig.create_for_deployment()` for config
- Use `create_dg_api_graphql_client()` for client
- Use `handle_api_errors()` context manager
- Pydantic BaseModel for schemas (not @record — per schemas/CLAUDE.md)
- `--asset-key` as a required click option (not argument) for `asset-check` commands
- Handle GraphQL union types (`AssetChecksOrError` can return `NeedsMigration`, `NeedsUserCodeUpgrade`, `NeedsAgentUpgrade`)
## GraphQL queries to use
- **Asset checks**: `assetChecksOrError(assetKey: AssetKeyInput!)` on root query — returns `AssetChecks` with `checks` list
- **Check executions**: `assetCheckExecutions(assetKey, checkName, limit, cursor)` on root query — returns list of `AssetCheckExecution`
- **Partition stats**: `assetNodeOrError(assetKey)` → `partitionStats { numMaterialized numPartitions numFailed numMaterializing }` on `AssetNode`
## Verification
1. `just ruff` — formatting/linting
2. `just pyright dagster-oss/python_modules/libraries/dagster-dg-cli` — type checking
3. Manual test against a Dagster Plus deployment:
- `dg api asset-check list --asset-key my/asset`
- `dg api asset-check get-executions --asset-key my/asset --check-name my_check`
- `dg api asset get-partition-status my/asset`
- Verify both table and `--json` output
</details>
<!-- WARNING: Machine-generated. Manual edits may break erk tooling. -->
<!-- erk:metadata-block:plan-header -->
<details>
<summary>plan-header</summary>
```yaml
schema_version: '2'
created_at: '2026-03-27T13:08:46.961224+00:00'
created_by: OwenKephart
plan_comment_id: null
last_dispatched_run_id: null
last_dispatched_node_id: null
last_dispatched_at: null
last_local_impl_at: '2026-03-27T20:16:14.922955+00:00'
last_local_impl_event: ended
last_local_impl_session: bca30886-68ef-4d5a-86a8-da0b723c5f33
last_local_impl_user: owen
last_remote_impl_at: null
last_remote_impl_run_id: null
last_remote_impl_session_id: null
branch_name: plnd/O22131-add-asset-check-par-03-27-1308
objective_issue: 22131
created_from_session: 00f71716-732a-4986-8bbd-911aca48d96e
lifecycle_stage: impl
worktree_name: erk-slot-02
last_session_branch: planned-pr-context/22132
last_session_id: bca30886-68ef-4d5a-86a8-da0b723c5f33
last_session_at: '2026-03-27T13:16:18.334090+00:00'
last_session_source: local
```
</details>
<!-- /erk:metadata-block:plan-header -->
---
To replicate this PR locally, run:
```
erk pr teleport 22132
```
Internal-RevId: 1ebfdadb9f73b5220e7109874d45c795125a5e6a1 parent 679d84b commit 9d15a66
31 files changed
Lines changed: 1483 additions & 0 deletions
File tree
- python_modules/libraries/dagster-dg-cli
- dagster_dg_cli_tests/cli_tests/api_tests
- __snapshots__
- asset_check_tests
- __snapshots__
- recordings
- success_empty_checks_text
- success_empty_checks
- success_empty_executions_text
- success_empty_executions
- success_get_check_executions_text
- success_get_check_executions
- success_list_asset_checks_text
- success_list_asset_checks
- asset_tests
- __snapshots__
- recordings
- error_partition_status_not_found
- error_partition_status_unpartitioned
- success_partition_status_text
- success_partition_status
- dagster_dg_cli
- api_layer
- api
- graphql_adapter
- schemas
- cli/api
Lines changed: 7 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
8 | 8 | | |
9 | 9 | | |
10 | 10 | | |
| 11 | + | |
11 | 12 | | |
12 | 13 | | |
13 | 14 | | |
| |||
20 | 21 | | |
21 | 22 | | |
22 | 23 | | |
| 24 | + | |
23 | 25 | | |
24 | 26 | | |
25 | 27 | | |
| |||
116 | 118 | | |
117 | 119 | | |
118 | 120 | | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
Lines changed: 44 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
Lines changed: 53 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
19 | 19 | | |
20 | 20 | | |
21 | 21 | | |
| 22 | + | |
22 | 23 | | |
23 | 24 | | |
24 | 25 | | |
| |||
786 | 787 | | |
787 | 788 | | |
788 | 789 | | |
| 790 | + | |
| 791 | + | |
| 792 | + | |
| 793 | + | |
| 794 | + | |
| 795 | + | |
| 796 | + | |
| 797 | + | |
| 798 | + | |
| 799 | + | |
| 800 | + | |
| 801 | + | |
| 802 | + | |
| 803 | + | |
| 804 | + | |
| 805 | + | |
| 806 | + | |
| 807 | + | |
| 808 | + | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
| 836 | + | |
| 837 | + | |
| 838 | + | |
| 839 | + | |
| 840 | + | |
| 841 | + | |
Lines changed: 166 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
| 54 | + | |
| 55 | + | |
| 56 | + | |
| 57 | + | |
| 58 | + | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
181 | 181 | | |
182 | 182 | | |
183 | 183 | | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
Lines changed: 53 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
| 52 | + | |
| 53 | + | |
0 commit comments