apache / apache/airflow

Add a `has_events` filter to the Assets search view

Open
#72,281 3 comments 0 reactions 1 assignee Claimed by @jasperjonkhans View on GitHub
area:API area:UI good first issue kind:feature
Dominant language
Python
Stars
46.9k
Forks
17.8k
Avg merge
2d 9h
Merged PRs (30d)
472

Description

## Description

Sub-issue of #53052, covering one of the filters that issue asks for on the Search Assets view.

Of the five filters requested in #53052, two have shipped (`Asset Group` and `Last Event Timestamp Range`) and one is in flight as a draft PR (#70971, Dag ID). `Has Events` is unstarted, and it is the smallest of the remaining three because it needs no schema change and no new relationship, just one filter parameter and its UI control.

The filter answers a question users actually ask of a large asset catalogue: which assets are declared but have never actually been updated, and which ones are live. Today the Assets list shows a `Last Asset Event` column, so the information is visible per row, but there is no way to narrow the list to one group or the other.

### What needs to change?

**API.** Add a boolean filter to the UI assets endpoint. There is an exact precedent to copy in `airflow-core/src/airflow/api_fastapi/common/parameters.py`: `_HasAssetScheduleFilter` does the same job for Dags, using a distinct subquery with `in_` and `notin_`.

The asset equivalent filters `AssetModel.id` against a subquery of `AssetEvent.asset_id`. Add the class next to `_HasAssetScheduleFilter`, then the annotated alias alongside `QueryHasAssetScheduleFilter`, and wire it into `get_assets` in `airflow-core/src/airflow/api_fastapi/core_api/routes/ui/assets.py`, both as a parameter and in the `filters=[...]` list passed to `paginated_select`.

Two notes for whoever implements it. The query built by `generate_assets_with_last_event_query()` already outer joins each asset to its latest event, so it is tempting to implement this as a null check on `last_asset_event_timestamp` instead. Prefer the subquery form, because it matches the existing precedent and does not depend on the join aliasing staying as it is. Separately, `AssetEvent.asset_id` is declared `nullable=False`, so the `notin_` branch is safe here, but it is worth keeping that in mind since `NOT IN` against a nullable column silently returns nothing.

**UI.** The wiring is the same shape as the `Last Event Timestamp Range` filter that already exists on this view:

- Add the key to `airflow-core/src/airflow/ui/src/constants/searchParams.ts`
- Add it to the `FilterableSearchParamsKeys` union in `airflow-core/src/airflow/ui/src/utils/useFiltersHandler.ts`
- Add the config in `airflow-core/src/airflow/ui/src/constants/filterConfigs.tsx`
- Add the key to `assetsFilterKeys` in `airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx` and pass the value through to `useAssetServiceGetAssetsUi` alongside the existing `lastAssetEventTimestampGte` and `lastAssetEventTimestampLte` arguments
- Add the label to the `assets:filters` translation namespace, next to the existing `lastEventDateRange` key

On the control type, use `FilterTypes.SELECT` with explicit true and false options rather than `FilterTypes.BOOLEAN`. The existing Yes/No style filters in this codebase are built that way, `FAVORITE` and `MISSED` in `filterConfigs.tsx` are both worth looking at, because a select can express three states (unset, yes, no) while the boolean control is a checkbox and cannot represent "no filter applied" distinctly from "false".

The generated API client is not hand edited. After the API change, run the prek hooks so the persisted OpenAPI spec and the generated client pick up the new query parameter, as described in [`contributing-docs/16_adding_api_endpoints.rst`](https://github.com/apache/airflow/blob/main/contributing-docs/16_adding_api_endpoints.rst).

## Definition of Done

1. New filter parameter on the UI assets endpoint, filtering assets that do and do not have at least one `AssetEvent`.
2. Filter control on the Assets list, with the selection held in the URL so a filtered view is shareable and survives a reload, matching the behaviour of the filters already on that view.
3. Tests in `airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_assets.py` covering all three states, meaning true, false, and the parameter absent, with at least one asset that has events and one that has none.
4. Regenerated OpenAPI spec and client committed, produced by the prek hooks rather than by hand.
5. These should pass:

```bash
breeze testing core-tests airflow-core/tests/unit/api_fastapi/core_api/routes/ui/test_assets.py

prek run --from-ref main --stage pre-commit
```

---
Drafted-by: Claude Code (Opus 5); reviewed and edited by @jroachgolf84 before posting

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.