Restore ability to mark task groups as success/failed in UI#60161
Restore ability to mark task groups as success/failed in UI#60161arjav1528 wants to merge 35 commits intoapache:mainfrom
Conversation
|
@arjav1528 Thank you for working on that! 👍 |
5db3cd8 to
c9d177e
Compare
is the UI ok or do you want me to refactor that |
bbovenzi
left a comment
There was a problem hiding this comment.
Thanks for taking this on! Do you mind doing more manually testing to find where MarkAs isn't quite working? I think there are some issues with the API that we need to tackle before we can merge any UI changes.
b068b63 to
da03292
Compare
bbovenzi
left a comment
There was a problem hiding this comment.
I tried using this locally and still had issues. Could you both manually test this out and also add fastapi test for dry runs?
Sorry about the trouble. I’ve tested this locally and couldn’t reproduce the issue. I have reproduced the issue, solved it, you can run it locally now |
d24fdae to
34153d8
Compare
…ate update logic and removing listener triggers
…ion for improved clarity and reuse in task instance patching
… ID, including dry run functionality and improved state management
…sk_instances.py Co-authored-by: Jason(Zhe-You) Liu <68415893+jason810496@users.noreply.github.com>
… affected instances and streamline updates for both single task instances and task groups
… instances in patching process
…points, enhancing dry run functionality and validation logic
…sk_instances.py Co-authored-by: Jason(Zhe-You) Liu <68415893+jason810496@users.noreply.github.com>
…llection of unique task instances in patching logic
… based on test group and downgrade SQLAlchemy flag
…tances to ensure correct handling of failed states
…r improved clarity and consistency
…up_id over identifier, improving error handling and validation flow
…ce function to simplify code and improve readability
…fier for task group, enhancing clarity in API requests
… for task group, improving API request structure
…bulk update logic to remove dry_run parameter for improved clarity
… for consistency across test groups
…y and consistency in API requests
5df0117 to
657b80b
Compare
|
Feel free to let us know if this one is ready for next round review. Thanks! |
|
@arjav1528 This PR has been converted to draft because it does not yet meet our Pull Request quality criteria. Issues found:
What to do next:
Converting a PR to draft is not a rejection — it is an invitation to bring the PR up to the project's standards so that maintainer review time is spent productively. If you have questions, feel free to ask on the Airflow Slack. |
|
@arjav1528 Any progress on Pierre's review? We definitely will need to rebase this. |
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Restores “mark task group as success/failed” in the UI by adding task-group mark-as components and extending the Task Instance API to support task-group targeting and dry-run previews.
Changes:
- Added UI components/hooks for marking task groups and bulk/dry-run updates.
- Extended FastAPI Task Instance patch/dry-run routes to accept
task_group_idand added a new bulk dry-run endpoint. - Updated OpenAPI generated client/types and added unit tests (Python + UI) around the new behavior.
Reviewed changes
Copilot reviewed 24 out of 24 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| airflow-core/tests/unit/api_fastapi/core_api/routes/public/test_task_instances.py | Adds coverage for task_group_id patch/dry-run and bulk dry-run behavior; updates existing expectations. |
| airflow-core/src/airflow/ui/src/queries/usePatchTaskInstanceDryRun.ts | Passes new identifier field required by updated OpenAPI path. |
| airflow-core/src/airflow/ui/src/queries/usePatchTaskInstance.ts | Updates mutation variable typing to align with new OpenAPI request shape. |
| airflow-core/src/airflow/ui/src/queries/usePatchTaskGroupDryRun.ts | Introduces hook for task-group dry-run via Task Instance dry-run endpoint. |
| airflow-core/src/airflow/ui/src/queries/usePatchTaskGroup.ts | Adds mutation hook to patch task-group instances and invalidate relevant caches. |
| airflow-core/src/airflow/ui/src/queries/useBulkUpdateTaskInstancesDryRun.ts | Adds UI hook intended to call new bulk dry-run behavior. |
| airflow-core/src/airflow/ui/src/queries/useBulkUpdateTaskInstancesDryRun.test.ts | Tests the dry-run hook request shape and caching behavior. |
| airflow-core/src/airflow/ui/src/queries/useBulkUpdateTaskInstances.ts | Adds wrapper hook for the bulk TI update API plus cache invalidations/toasts. |
| airflow-core/src/airflow/ui/src/queries/useBulkUpdateTaskInstances.test.ts | Adds unit tests for bulk update hook behavior (success + error). |
| airflow-core/src/airflow/ui/src/pages/TaskInstance/Header.tsx | Updates patch calls to include identifier. |
| airflow-core/src/airflow/ui/src/pages/GroupTaskInstance/Header.tsx | Adds the new “Mark as” dropdown into the group TI header actions. |
| airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkTaskInstanceAsDialog.tsx | Updates patch calls to include identifier. |
| airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkGroupTaskInstanceAsDialog.tsx | Adds the new dialog UX for task-group mark-as with options + dry-run preview + notes. |
| airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkGroupTaskInstanceAsDialog.test.tsx | Adds unit tests for the new dialog behavior and hook call wiring. |
| airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstance/MarkGroupTaskInstanceAsButton.tsx | Adds the dropdown button + hotkeys to open the dialog with selected state. |
| airflow-core/src/airflow/ui/src/components/Clear/TaskInstance/ClearTaskInstanceDialog.tsx | Updates patch calls to include identifier. |
| airflow-core/src/airflow/ui/openapi-gen/requests/types.gen.ts | Regenerates types for updated TI endpoints and adds bulk dry-run types. |
| airflow-core/src/airflow/ui/openapi-gen/requests/services.gen.ts | Regenerates service methods/paths including TI identifier and bulk dry-run endpoint. |
| airflow-core/src/airflow/ui/openapi-gen/queries/queries.ts | Regenerates query/mutation hooks for updated TI endpoints and adds bulk dry-run mutation. |
| airflow-core/src/airflow/ui/openapi-gen/queries/common.ts | Adds the generated result type for bulk TI dry-run. |
| airflow-core/src/airflow/api_fastapi/core_api/services/public/task_instances.py | Implements task-group TI selection, unique-TI collection helper, and bulk dry-run handling. |
| airflow-core/src/airflow/api_fastapi/core_api/routes/public/task_instances.py | Extends TI patch/dry-run routes for task groups and adds /taskInstances/dry_run endpoint. |
| airflow-core/src/airflow/api_fastapi/core_api/openapi/v2-rest-api-generated.yaml | Updates OpenAPI paths/params for identifier-based endpoints and adds bulk dry-run path. |
| .github/workflows/run-unit-tests.yml | Tweaks comment around TOTAL_TEST_TIMEOUT. |
| query: { | ||
| dry_run: true, | ||
| }, | ||
| url: "/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances", |
There was a problem hiding this comment.
The backend route added in this PR is PATCH /api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/dry_run (path suffix), but this hook calls /taskInstances with dry_run=true query param. This mismatch will cause 404s and break the UI dry-run. Update the hook to call the new /taskInstances/dry_run endpoint (or switch to the generated TaskInstanceService.bulkTaskInstancesDryRun / useTaskInstanceServiceBulkTaskInstancesDryRun).
| query: { | |
| dry_run: true, | |
| }, | |
| url: "/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances", | |
| url: "/api/v2/dags/{dag_id}/dagRuns/{dag_run_id}/taskInstances/dry_run", |
| expect(mockRequest).toHaveBeenCalledWith( | ||
| expect.anything(), | ||
| expect.objectContaining({ | ||
| method: "PATCH", | ||
| path: { | ||
| dag_id: "test-dag", | ||
| dag_run_id: "test-run", | ||
| }, | ||
| query: { | ||
| dry_run: true, | ||
| }, | ||
| }), | ||
| ); |
There was a problem hiding this comment.
This test locks in the (now incorrect) client contract of calling /taskInstances with dry_run=true. If the hook is updated to use the new /taskInstances/dry_run endpoint, update this assertion to validate the correct URL and remove the dry_run query expectation. This is required to prevent the test suite from passing while the UI remains incompatible with the API.
| def patch_task_instance( | ||
| dag_id: str, | ||
| dag_run_id: str, | ||
| task_id: str, | ||
| dag_bag: DagBagDep, | ||
| body: PatchTaskInstanceBody, | ||
| user: GetUserDep, | ||
| session: SessionDep, | ||
| identifier: str | None = None, | ||
| task_id: str | None = None, | ||
| map_index: int | None = None, | ||
| task_group_id: str | None = Query(None, description="Task group id to update task instances for"), | ||
| update_mask: list[str] | None = Query(None), | ||
| ) -> TaskInstanceCollectionResponse: |
There was a problem hiding this comment.
This endpoint’s path is .../taskInstances/{identifier}, but identifier is declared optional and there is also a task_id query parameter that can override which task is actually patched. That allows requests like /taskInstances/foo?task_id=bar, where the modified resource doesn’t match the URL path, and it also generates an OpenAPI schema with nullable path params (invalid for required path segments). Make identifier: str (no default) and remove the task_id query parameter from this route; keep task_group_id as the selector for group updates.
| anyOf: | ||
| - type: string | ||
| - type: 'null' |
There was a problem hiding this comment.
The generated OpenAPI marks the identifier path parameter as nullable (string | null). Path params cannot be null when the segment is required. This comes from the FastAPI signature using identifier: str | None = None. Update the route signatures so required path params are non-optional (str with no default), then regenerate the OpenAPI spec/client.
| anyOf: | |
| - type: string | |
| - type: 'null' | |
| type: string |
| toaster.create({ | ||
| description: error.message, | ||
| title: translate("toaster.update.error", { | ||
| resourceName: translate("taskGroup"), |
There was a problem hiding this comment.
This hook is for bulk updating task instances, but the error toast uses resourceName: translate(\"taskGroup\"), which will produce a misleading error title in non-task-group contexts. Use the task-instance translation key (consistent with other TI toasts, e.g. taskInstance_one) or make the resource name configurable via props.
| resourceName: translate("taskGroup"), | |
| resourceName: translate("taskInstance_one"), |
| include_past: past, | ||
| include_upstream: upstream, | ||
| new_state: state, | ||
| note, |
There was a problem hiding this comment.
The dry-run request includes note, but notes don’t affect which task instances are selected; including it increases request churn (and if the dry-run hook’s cache key doesn’t include note, it can lead to confusing cache behavior while typing). Prefer omitting note from dry-run requests, or (if the API intentionally echoes note-related data) include note in the dry-run queryKey so caching stays consistent.
| note, |
| for ti in affected_tis: | ||
| ti_key = ( | ||
| ti.dag_id, | ||
| ti.run_id, | ||
| ti.task_id, | ||
| ti.map_index if ti.map_index is not None else -1, | ||
| ) | ||
| if ti_key not in seen_ti_keys: | ||
| seen_ti_keys.add(ti_key) | ||
| all_affected_tis.append(ti) |
There was a problem hiding this comment.
This manually re-implements unique task instance collection logic. Since _collect_unique_tis(...) is introduced in the same module and already encapsulates this behavior, reuse it here (and in the other loop below) to avoid duplication and ensure keying stays consistent across endpoints.
| for ti in affected_tis: | |
| ti_key = ( | |
| ti.dag_id, | |
| ti.run_id, | |
| ti.task_id, | |
| ti.map_index if ti.map_index is not None else -1, | |
| ) | |
| if ti_key not in seen_ti_keys: | |
| seen_ti_keys.add(ti_key) | |
| all_affected_tis.append(ti) | |
| _collect_unique_tis(affected_tis, seen_ti_keys, all_affected_tis) |
Description
This PR adds two new React components that restore the task group marking functionality:
MarkGroupTaskInstanceAsButton- A dropdown button component that allows users to select a state (success/failed) and opens a dialog for confirmationMarkGroupTaskInstanceAsDialog- A dialog component that provides:The button is integrated into the Group Task Instance page header, alongside the existing Clear button.
Screenshots
Solves #60121
Closes: #56103