Skip to content

UI: Use bulk clearDagRuns endpoint for bulk Dag run clear#67846

Open
pierrejeambrun wants to merge 2 commits into
apache:mainfrom
astronomer:feat/ui-bulk-clear-dag-runs
Open

UI: Use bulk clearDagRuns endpoint for bulk Dag run clear#67846
pierrejeambrun wants to merge 2 commits into
apache:mainfrom
astronomer:feat/ui-bulk-clear-dag-runs

Conversation

@pierrejeambrun
Copy link
Copy Markdown
Member

@pierrejeambrun pierrejeambrun commented Jun 1, 2026

Wires the Dag Runs bulk-clear action to the new POST /dags/{dag_id}/clearDagRuns bulk endpoint, replacing the previous client-side fan-out that issued one single-run clear request per selected run.

  • useBulkClearDagRuns now makes a single atomic clearDagRuns call (~ wildcard, each entry carries its own dag_id) instead of Promise.allSettled over per-run calls.
  • useBulkClearDagRunsDryRun now makes a single dry-run call instead of a useQueries fan-out.
  • BulkClearDagRunsButton is unchanged — both hooks keep their signatures and return shapes.

Behavior change: the bulk clear is now atomic — all selected runs clear or none do. The previous fan-out allowed partial success (clear the runs that succeed, report per-run errors). This aligns dag-run bulk clear with the existing post_clear_task_instances endpoint, which is already a single all-or-nothing transaction. On failure the dialog stays open and shows a single request-level error.

Depends on #67709 (the bulk endpoint + regenerated client live on that branch). This PR is stacked on it — until #67709 merges, the diff here also shows #67709's commits; the UI change itself is the single commit "UI: Use bulk clearDagRuns endpoint instead of per-run fan-out".


Was generative AI tooling used to co-author this PR?
  • Yes — Claude Code (Opus 4.8)

Generated-by: Claude Code (Opus 4.8) following the guidelines

@boring-cyborg boring-cyborg Bot added area:airflow-ctl area:API Airflow's REST/HTTP API area:UI Related to UI/UX. For Frontend Developers. backport-to-airflow-ctl/v0-1-test labels Jun 1, 2026
@pierrejeambrun pierrejeambrun force-pushed the feat/ui-bulk-clear-dag-runs branch from 3838df0 to bf8f98f Compare June 2, 2026 09:12
@pierrejeambrun
Copy link
Copy Markdown
Member Author

Screen.Recording.2026-06-02.at.15.09.56.mov
Screen.Recording.2026-06-02.at.15.12.37.mov

@pierrejeambrun
Copy link
Copy Markdown
Member Author

Screenshot 2026-06-02 at 16 00 15

@pierrejeambrun pierrejeambrun force-pushed the feat/ui-bulk-clear-dag-runs branch from 53dabe2 to a7fa62f Compare June 3, 2026 08:33
Comment on lines +43 to +74
const { data: response, isFetching } = useQuery({
enabled: enabled && selectedDagRuns.length > 0,
queryFn: () =>
DagRunService.clearDagRuns({
// ``~`` clears across Dags in a single call; every entry carries its own dag_id.
dagId: "~",
requestBody: {
dag_runs: selectedDagRuns.map((dagRun) => ({
dag_id: dagRun.dag_id,
dag_run_id: dagRun.dag_run_id,
})),
dry_run: true,
only_failed: options.onlyFailed,
only_new: options.onlyNew,
},
}) as Promise<ClearTaskInstanceCollectionResponse>,
queryKey: [
useBulkClearDagRunsDryRunKey,
selectedDagRuns.map((dagRun) => `${dagRun.dag_id}.${dagRun.dag_run_id}`).sort(),
{ only_failed: options.onlyFailed, only_new: options.onlyNew },
],
refetchOnMount: "always" as const,
});

const isFetching = results.some((result) => result.isFetching);
// Each per-run call is scoped to a distinct ``(dag_id, dag_run_id)`` so the
// concatenated array can't contain duplicates; the response is also
// homogeneous (``only_new=true`` yields ``NewTaskResponse`` placeholders,
// ``false`` yields real ``TaskInstanceResponse``), so the cast is safe even
// though the OpenAPI type widens to a union.
const taskInstances = results.flatMap((result) => result.data?.task_instances ?? []);
const data: TaskInstanceCollectionResponse =
taskInstances.length === 0
? EMPTY
: {
task_instances: taskInstances as Array<TaskInstanceResponse>,
total_entries: taskInstances.length,
};
// ``only_new=true`` yields ``NewTaskResponse`` placeholders, ``false`` yields real
// ``TaskInstanceResponse``; the OpenAPI type widens to a union, so narrow it here.
const data: TaskInstanceCollectionResponse = response
? {
task_instances: response.task_instances as Array<TaskInstanceResponse>,
total_entries: response.total_entries,
}
: EMPTY;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a lot of typecasting that I would like to avoid

dagId: "~",
requestBody: {
dag_runs: dagRuns.map((dagRun) => ({
dag_id: dagRun.dag_id,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't be handling errors, pending, or even calling the API endpoint directly on our own. Use the hook.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:airflow-ctl area:API Airflow's REST/HTTP API area:UI Related to UI/UX. For Frontend Developers. backport-to-airflow-ctl/v0-1-test

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants