-
Notifications
You must be signed in to change notification settings - Fork 16.3k
feat(api/ui)#50779 : add asset group filtering and asset group views #51682
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
bugraoz93
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
API side looks good! Could you please fix static checks? It is failing for Compile / format / lint UI
|
Runned precommit locally and got zero errors how can I replicate this setup and make sure it passes consistently? |
guan404ming
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, this feature is great. Feel free to ask anything.
| <HStack gap={4} mb={2}> | ||
| <Box> | ||
| <Text color="chakra-fg" fontSize="xs"> | ||
| {/* i18n: Producing Tasks label */}Producing Tasks |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We currently disable literal string in ui due to i18n thus you need to update the translation file like en/assets.json or en/common.json and use useTranslation hook to access them. You could also run npm run lint in ui folder to see your lint error.
Yes, I have a question. I created a new file called sidebar.tsx, but I'm not able to catch the errors in it , even though the runner here on Git is marking them. Do you know what might be going on? |
pierrejeambrun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice effort!
I believe there are a few things to update before we can merge but this can be a great feature and addition for Airflow 3.1.0
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dependencies.py
Outdated
Show resolved
Hide resolved
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dependencies.py
Outdated
Show resolved
Hide resolved
airflow-core/src/airflow/ui/src/pages/AssetsGroupedList/AssetsGroupedList.tsx
Outdated
Show resolved
Hide resolved
airflow-core/src/airflow/ui/src/pages/AssetsGroupedList/AssetsGroupedList.tsx
Outdated
Show resolved
Hide resolved
airflow-core/src/airflow/ui/src/pages/AssetsList/AssetsList.tsx
Outdated
Show resolved
Hide resolved
airflow-core/src/airflow/ui/src/queries/useMultiDependencyGraph.ts
Outdated
Show resolved
Hide resolved
Be sure to be rebased on latest main with latest devtools updates (We recently merged a few improvement to this part). You can then run pre-commit hooks or eslint:
|
|
I'm getting a mypy error when running the pre-commit hook for airflow-core. Here's the stack trace: AssertionError: Should never get here in normal mode, got TypeAlias:numpy.bool_ instead of TypeInfo |
pierrejeambrun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No idea what is wrong in your setup. You can run pre-commit on main to be sure that everything is in order. It is really hard for me to help you without more context and iterating. Everything should be detailed in the contribution docs where you can find all the steps and troubleshooting help.
airflow-core/src/airflow/api_fastapi/core_api/routes/ui/dependencies.py
Outdated
Show resolved
Hide resolved
47ce735 to
f71a6f5
Compare
f71a6f5 to
7bd5da7
Compare
|
Hi, the pipeline is currently failing on Tests AMD / CI image checks / Static checks (pull_request), specifically at the step "Generate SVG from Airflow CTL Commands". However, when I run the checks locally: pre-commit run (on modified files only) skips this check, as expected, since I haven't modified any related files; pre-commit run --all-files passes the check successfully, including the "Generate SVG from Airflow CTL Commands" step. Please let me know if there's anything else I should look into or adjust. Thanks! |
That was temporary. As usual - rebase. Always Be Rebased. There is another issue in main being fixed as we speak though so you might wait a few minutes. |
|
This is a PR that targets fixing the current main "generate constraints" failure #52464 - once merged, rebasing your PR should fix unrelated issue. |
…views Add backend and OpenAPI support for filtering assets by group (`group_pattern`) in /assets endpoint. Implement multi-asset dependency queries in /ui/dependencies endpoint with node_ids parameter. Add new frontend pages and components for asset group views, including group graph and sidebar. Refactor asset list and group list to support group filtering, merging, and deduplication. Add and update tests for group filtering, group views, and multi-asset dependency queries. Co-authored-by: Francisco Núncio <francisco.nuncio@tecnico.ulisboa.pt>
- Added `/assets/groups` endpoint to return asset groups with their assets and counts. - Added `AssetGroupResponse` and `AssetGroupCollectionResponse` models to API and OpenAPI schemas. - Updated backend, OpenAPI YAML, and TypeScript types to support asset group listing and filtering. - Updated frontend to use new `useAssetServiceGetAssetGroups` hook for grouped asset views and sidebar. - Refactored asset list and group pages to use new group API and improved i18n. - Updated dependencies API and frontend to use exploded `node_ids` query parameter (array, not comma-separated). - Updated backend logic for `/dependencies` to return 404 if any requested node_id is missing. - Updated and fixed unit tests for dependencies and asset groups to match new API and error handling. - Improved search and filtering for assets and asset groups in UI.
|
Rebased it for you after merging my PR. |
|
After my last changes, the scheduler query count tests are failing with a much higher number of queries than expected, even though I did not touch any scheduler or ORM/database logic. I double-checked and only changed API and UI code. Has anyone seen this before or have any idea what could cause this? |
|
@davidfgcorreia that could be transient, unrelated, you can close and reopen the PR to retrigger the CI, or just rebase or just push an amended commit. |
pierrejeambrun
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for addressing the previous comments.
I think the PR needs refinement, refactoring, simplifications before we can merge this, I think it still remains an important effort there.
That would be a great addition to 3.1.0
| node_id: str | None = None, | ||
| node_ids: list[str] = Query(None, description="List of node ids"), | ||
| ) -> BaseGraphResponse: | ||
| """Dependencies graph. Supports a single node_id or multiple node_ids as exploded query parameters (node_ids=foo&node_ids=bar).""" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| """Dependencies graph. Supports a single node_id or multiple node_ids as exploded query parameters (node_ids=foo&node_ids=bar).""" | |
| """Dependencies graph. Supports a single node_id or multiple node_ids |
| def get_dependencies(session: SessionDep, node_id: str | None = None) -> BaseGraphResponse: | ||
| """Dependencies graph.""" | ||
| def get_dependencies( | ||
| node_id: str | None = None, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| node_id: str | None = None, |
We should probably only use node_ids even for single node to simplify the API. That's private API so it doens't have to be backward compatible.
| const truncate = (str: string, max = 32) => (str.length > max ? `${str.slice(0, max - 3)}...` : str); | ||
|
|
||
| const nameCellRenderer = ({ row: { original } }: { row: { original: AssetResponse } }) => ( | ||
| <NameCell original={original} /> | ||
| ); | ||
| const lastAssetEventCellRenderer = ({ row: { original } }: { row: { original: AssetResponse } }) => ( | ||
| <LastAssetEventCell original={original} /> | ||
| ); | ||
| const groupCellRenderer = ({ row: { original } }: { row: { original: AssetResponse } }) => ( | ||
| <GroupCell original={original} /> | ||
| ); | ||
| const consumingDagsCellRenderer = ({ row: { original } }: { row: { original: AssetResponse } }) => ( | ||
| <ConsumingDagsCell original={original} /> | ||
| ); | ||
| const producingTasksCellRenderer = ({ row: { original } }: { row: { original: AssetResponse } }) => ( | ||
| <ProducingTasksCell original={original} /> | ||
| ); | ||
| const triggerCellRenderer = ({ row: { original } }: { row: { original: AssetResponse } }) => ( | ||
| <TriggerCell original={original} /> | ||
| ); | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly to other columns definition, inline this directly in the columns definition.
| const [sort] = sorting; | ||
| const orderBy = sort ? `${sort.desc ? "-" : ""}${sort.id}` : undefined; | ||
|
|
||
| // Use o novo hook para buscar grupos paginados |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Comment not in english.
| const [sort] = sorting; | ||
| const orderBy = sort ? `${sort.desc ? "-" : ""}${sort.id}` : undefined; | ||
|
|
||
| const searchMatchMode = "any"; // "any" para OR, "all" para AND |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment not in english.
| assets_query = ( | ||
| select(AssetModel) | ||
| .where(AssetModel.group.in_(groups)) | ||
| .options( | ||
| subqueryload(AssetModel.scheduled_dags), | ||
| subqueryload(AssetModel.producing_tasks), | ||
| subqueryload(AssetModel.consuming_tasks), | ||
| ) | ||
| ) | ||
| assets = session.scalars(assets_query).all() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will query all assets from the db. Can't happen I think
| group_query = ( | ||
| select(AssetModel.group, func.count(AssetModel.id)) | ||
| .where( | ||
| AssetModel.active.has() if only_active.value else True, | ||
| *([AssetModel.group == group] if group else []), | ||
| ) | ||
| .group_by(AssetModel.group) | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will query all assets from the db grouped by asset group, but that could be thousands of them, that' not scalable.
| if len(node_ids) == 1: | ||
| node_id = node_ids[0] | ||
| filtered_connected_components = [cc for cc in connected_components if node_id in cc] | ||
| if len(filtered_connected_components) != 1: | ||
| raise ValueError( | ||
| f"Unique connected component not found, got {filtered_connected_components} for connected components of node {node_id}, expected only 1 connected component." | ||
| ) | ||
| connected_component = filtered_connected_components[0] | ||
| filtered_nodes = [node for node in nodes if node["id"] in connected_component] | ||
| filtered_edges = [ | ||
| edge | ||
| for edge in edges | ||
| if (edge["source_id"] in connected_component and edge["target_id"] in connected_component) | ||
| ] | ||
| return {"nodes": filtered_nodes, "edges": filtered_edges} | ||
| # Multiple node_ids: merge all relevant components | ||
| relevant_components = [cc for cc in connected_components if any(nid in cc for nid in node_ids)] | ||
| # NEW: Check that all node_ids are present in the merged set | ||
| if not relevant_components: | ||
| raise ValueError(f"No connected component found for node(s) {node_ids}.") | ||
| merged_node_ids = set().union(*relevant_components) | ||
| missing = [nid for nid in node_ids if nid not in merged_node_ids] | ||
| if missing: | ||
| raise ValueError(f"No connected component found for node(s) {missing}.") | ||
| filtered_nodes = [node for node in nodes if node["id"] in merged_node_ids] | ||
| filtered_edges = [ | ||
| edge | ||
| for edge in edges | ||
| if (edge["source_id"] in connected_component and edge["target_id"] in connected_component) | ||
| if (edge["source_id"] in merged_node_ids and edge["target_id"] in merged_node_ids) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs refactoring, that's really hard to read .
| <b>{translate("group", { ns: "assets" })}</b> | ||
| <br /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
use chakra.p, chakra.br, etc.
| <Text color="chakra-fg-subtle" fontSize="xs"> | ||
| {ev.timestamp} | ||
| </Text> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably shouldn't display timestamp as text, check other location of the app for Time component.
|
Hi @davidfgcorreia, thanks for the contribution! May I ask if this PR was closed by accident? |
|
I can't work on this issue no longer. |
Add backend and OpenAPI support for filtering assets by group (
group_pattern) in /assets endpoint.Implement multi-asset dependency queries in /ui/dependencies endpoint with node_ids parameter.
Add new frontend pages and components for asset group views, including group graph and sidebar.
Refactor asset list and group list to support group filtering, merging, and deduplication.
Add and update tests for group filtering, group views, and multi-asset dependency queries.
introduced small bug in caching on the asset_grath.
https://youtu.be/ybpRUjEhdoQ
^ Add meaningful description above
Read the Pull Request Guidelines for more information.
In case of fundamental code changes, an Airflow Improvement Proposal (AIP) is needed.
In case of a new dependency, check compliance with the ASF 3rd Party License Policy.
In case of backwards incompatible changes please leave a note in a newsfragment file, named
{pr_number}.significant.rstor{issue_number}.significant.rst, in airflow-core/newsfragments.