Re: [PR] Add bulk TI deletion UI [airflow]

2026-01-17 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3764196031

   I think splitting this into several PRs is a better approach. I’ll figure 
out the remaining gaps and open targeted PRs accordingly. I’m also happy to 
help review other open PRs for this feature to avoid overlapping work.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2026-01-17 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3764185146

   Apologies for the delay. This PR has become stale, and I noticed there are 
other active PRs addressing the same issue. To avoid redundancy, I’ll go ahead 
and close this one. Thank you everyone for your time and reviews.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2026-01-17 Thread via GitHub


guan404ming closed pull request #51564: Add bulk TI deletion UI
URL: https://github.com/apache/airflow/pull/51564


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2026-01-09 Thread via GitHub


Lee-W commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3727697293

   I'm now making it a draft. Feel free to mark it as ready


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2026-01-09 Thread via GitHub


Lee-W commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3727695211

   @guan404ming would like to follow up whehter wer're still working on this 
one?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-12-10 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2607004503


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,110 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import type { TaskInstanceResponse } from "openapi/requests";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId?: string;
+  readonly dagRunId?: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+export const useBulkDeleteTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  dagId === undefined || dagRunId === undefined
+? []
+: [UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId: dagRunId })],
+];
+

Review Comment:
   Grid is not updated after a successful delete. We should probably do that 
too. cf video. (clearing tasks from the same run)



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-12-10 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2607029557


##
airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstances/MarkTaskInstancesAsDialog.tsx:
##
@@ -0,0 +1,109 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Button, Flex, Heading, VStack } from "@chakra-ui/react";
+import type {
+  TaskInstanceCollectionResponse,
+  TaskInstanceResponse,
+  TaskInstanceState,
+} from "openapi-gen/requests/types.gen";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+
+import { ActionAccordion } from "src/components/ActionAccordion";
+import { StateBadge } from "src/components/StateBadge";
+import { Dialog } from "src/components/ui/Dialog";
+import { useBulkPatchTaskInstances } from 
"src/queries/useBulkPatchTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly onClose: () => void;
+  readonly open: boolean;
+  readonly patchKeys: Array;
+  readonly selectedState: TaskInstanceState;
+};
+
+const MarkTaskInstancesAsDialog = ({
+  clearSelections,
+  dagId,
+  dagRunId,
+  onClose,
+  open,
+  patchKeys,
+  selectedState,
+}: Props) => {
+  const [note, setNote] = useState();
+
+  const { isPending, patchTaskInstances } = useBulkPatchTaskInstances({
+dagId,
+dagRunId,
+onSuccessConfirm: () => {
+  clearSelections();
+  onClose();
+},
+  });
+  const { t: translate } = useTranslation();
+
+  const affectedTasks = {
+task_instances: patchKeys,
+total_entries: patchKeys.length,
+  } as TaskInstanceCollectionResponse;
+
+  const handlePatch = (state: TaskInstanceState) => {
+const actionValue = state === "failed" ? "set_failed" : "set_success";
+
+patchTaskInstances(patchKeys, actionValue, { note });
+onClose();
+  };
+
+  return (
+
+  
+
+  
+
+  
+{translate("dags:runAndTaskActions.markAs.title", {
+  state: selectedState,
+  type: translate("common:taskInstance_other"),
+})}
+:
+  {" "}
+  
+
+  
+
+
+
+
+
+  
+  
+ 
handlePatch(selectedState)}>
+  {translate("modal.confirm")}
+
+  
+

Review Comment:
   I guess it's fine for now since we use the 'bulk' endpoint.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-12-10 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2607005409


##
airflow-core/src/airflow/ui/src/queries/useBulkPatchTaskInstances.ts:
##
@@ -0,0 +1,136 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import type { TaskInstanceResponse } from "openapi/requests";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId?: string;
+  readonly dagRunId?: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+export const useBulkPatchTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+
+  const onSuccess = async (responseData: { patch?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  dagId === undefined || dagRunId === undefined
+? []

Review Comment:
   Same here



##
airflow-core/src/airflow/ui/src/components/MarkAs/TaskInstances/MarkTaskInstancesAsDialog.tsx:
##
@@ -0,0 +1,109 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Button, Flex, Heading, VStack } from "@chakra-ui/react";
+import type {
+  TaskInstanceCollectionResponse,
+  TaskInstanceResponse,
+  TaskInstanceState,
+} from "openapi-gen/requests/types.gen";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+
+import { ActionAccordion } from "src/components/ActionAccordion";
+import { StateBadge } from "src/components/StateBadge";
+import { Dialog } from "src/components/ui/Dialog";
+import { useBulkPatchTaskInstances } from 
"src/queries/useBulkPatchTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly onClose: () => void;
+  readonly open: boolean;
+  readonly patchKeys: Array;
+  readonly selectedState: TaskInstanceState;
+};
+
+const MarkTaskInstancesAsDialog = ({
+  clearSelections,
+  dagId,
+  dagRunId,
+  onClose,
+  open,
+  patchKeys,
+  selectedState,
+}: Props) => {
+  const [note, setNote] = useState();
+
+  const { isPending, patchTaskInstances } = useBulkPatchTaskInstances({
+dagId,
+dagRunId,
+onSuccessConfirm: () => {
+  clearSelections();
+  onClose();
+},
+  });
+  const { t: translate } = useTranslation();
+
+  const affectedTasks = {
+task_instances: patchKeys,
+total_entries: patchKeys.length,
+  } as TaskInstanceCollectionResponse;
+
+  const handlePatch = (state: TaskInstanceState) => {
+const actionValue = state === "failed" ? "set_failed" : "set_success";
+
+patchTaskInstances(patchKeys, actionValue, { note });
+onClose();
+  };
+
+  return (
+
+  
+
+  
+
+  
+{translate("dags:runAndTaskActions.markAs.title", {
+  state: selectedState,
+  type: translate("common:taskInstance_other"),
+})}
+:
+  {" "}
+

Re: [PR] Add bulk TI deletion UI [airflow]

2025-12-10 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3636339394

   I've updated to work with new endpoint. Please take another look, thanks!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-10-20 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2445300548


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,156 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState, useRef } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import type { TaskInstanceResponse } from "openapi/requests";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId?: string;
+  readonly dagRunId?: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+const SEPARATOR = "SEPARATOR";
+
+export const useBulkDeleteTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+  const pendingOperationsRef = useRef(0);
+  const allSuccessesRef = useRef>([]);
+  const hasErrorsRef = useRef(false);
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  dagId === undefined || dagRunId === undefined
+? []
+: [UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId: dagRunId })],
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+hasErrorsRef.current = true;
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+allSuccessesRef.current = [...allSuccessesRef.current, ...success];
+  }
+}
+
+pendingOperationsRef.current -= 1;
+
+if (pendingOperationsRef.current === 0 && !hasErrorsRef.current) {
+  // All operations completed successfully
+  if (allSuccessesRef.current.length > 0) {
+toaster.create({
+  description: 
translate("bulkAction.delete.taskInstance.success.description", {
+count: allSuccessesRef.current.length,
+keys: allSuccessesRef.current.join(", "),
+  }),
+  title: translate("bulkAction.delete.taskInstance.success.title"),
+  type: "success",
+});
+onSuccessConfirm();
+  }
+  // Reset state for next operation
+  allSuccessesRef.current = [];
+  hasErrorsRef.current = false;
+}
+  };
+
+  const onError = (_error: unknown) => {
+setError(_error);
+hasErrorsRef.current = true;
+pendingOperationsRef.current -= 1;
+  };
+
+  const { isPending, mutate } = useTaskInstanceServiceBulkTaskInstances({
+onError,
+onSuccess,
+  });
+
+  const deleteTaskInstances = (entities: Array) => {
+// Reset state for new operation
+allSuccessesRef.current = [];
+hasErrorsRef.current = false;
+setError(undefined);
+
+if (Boolean(dagId) && Boolean(dagRunId) && dagId !== undefined && dagRunId 
!== undefined) {
+  pendingOperationsRef.current = 1;
+  mutate({
+dagId,
+dagRunId,
+requestBody: {
+  actions: [
+{
+  action: "delete",
+  entities: entities.map((ti) => ({ map_index: ti.map_index, 
task_id: ti.task_id })),
+},
+  ],
+},
+  });
+} else {
+  // cross dag run
+  const groupedByDagRunTIs: Record> = 
{};
+
+  entities.forEach((ti) => {
+(groupedByDagRunTIs[`${ti.dag_id}${SEPARATOR}${ti.dag_run_id}`] ??= 
[]).push(ti);
+  });
+
+  pendingOperationsRef.current = Object.keys(groupedByDagRunTIs).length;
+
+  Object.entries(groupedByDagRunTIs).forEach(([key, groupTIs]) => {
+const [groupDagId, groupDagRunId] = key.split(SEPARATO

Re: [PR] Add bulk TI deletion UI [airflow]

2025-10-18 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2426829138


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,156 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState, useRef } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import type { TaskInstanceResponse } from "openapi/requests";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId?: string;
+  readonly dagRunId?: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+const SEPARATOR = "SEPARATOR";
+
+export const useBulkDeleteTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+  const pendingOperationsRef = useRef(0);
+  const allSuccessesRef = useRef>([]);
+  const hasErrorsRef = useRef(false);
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  dagId === undefined || dagRunId === undefined
+? []
+: [UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId: dagRunId })],
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+hasErrorsRef.current = true;
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+allSuccessesRef.current = [...allSuccessesRef.current, ...success];
+  }
+}
+
+pendingOperationsRef.current -= 1;
+
+if (pendingOperationsRef.current === 0 && !hasErrorsRef.current) {
+  // All operations completed successfully
+  if (allSuccessesRef.current.length > 0) {
+toaster.create({
+  description: 
translate("bulkAction.delete.taskInstance.success.description", {
+count: allSuccessesRef.current.length,
+keys: allSuccessesRef.current.join(", "),
+  }),
+  title: translate("bulkAction.delete.taskInstance.success.title"),
+  type: "success",
+});
+onSuccessConfirm();
+  }
+  // Reset state for next operation
+  allSuccessesRef.current = [];
+  hasErrorsRef.current = false;
+}
+  };
+
+  const onError = (_error: unknown) => {
+setError(_error);
+hasErrorsRef.current = true;
+pendingOperationsRef.current -= 1;
+  };
+
+  const { isPending, mutate } = useTaskInstanceServiceBulkTaskInstances({
+onError,
+onSuccess,
+  });
+
+  const deleteTaskInstances = (entities: Array) => {
+// Reset state for new operation
+allSuccessesRef.current = [];
+hasErrorsRef.current = false;
+setError(undefined);
+
+if (Boolean(dagId) && Boolean(dagRunId) && dagId !== undefined && dagRunId 
!== undefined) {
+  pendingOperationsRef.current = 1;
+  mutate({
+dagId,
+dagRunId,
+requestBody: {
+  actions: [
+{
+  action: "delete",
+  entities: entities.map((ti) => ({ map_index: ti.map_index, 
task_id: ti.task_id })),
+},
+  ],
+},
+  });
+} else {
+  // cross dag run
+  const groupedByDagRunTIs: Record> = 
{};
+
+  entities.forEach((ti) => {
+(groupedByDagRunTIs[`${ti.dag_id}${SEPARATOR}${ti.dag_run_id}`] ??= 
[]).push(ti);
+  });
+
+  pendingOperationsRef.current = Object.keys(groupedByDagRunTIs).length;
+
+  Object.entries(groupedByDagRunTIs).forEach(([key, groupTIs]) => {
+const [groupDagId, groupDagRunId] = key.split(SEPARATO

Re: [PR] Add bulk TI deletion UI [airflow]

2025-10-18 Thread via GitHub


pierrejeambrun commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3352657538

   @guan404ming any progress on this one ? I think a  UI warning could suffice 
as mentioned by Brent, and unblock this.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-10-18 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2426832459


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,156 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState, useRef } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import type { TaskInstanceResponse } from "openapi/requests";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId?: string;
+  readonly dagRunId?: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+const SEPARATOR = "SEPARATOR";
+
+export const useBulkDeleteTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+  const pendingOperationsRef = useRef(0);
+  const allSuccessesRef = useRef>([]);
+  const hasErrorsRef = useRef(false);
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  dagId === undefined || dagRunId === undefined
+? []
+: [UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId: dagRunId })],
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+hasErrorsRef.current = true;
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+allSuccessesRef.current = [...allSuccessesRef.current, ...success];
+  }
+}
+
+pendingOperationsRef.current -= 1;
+
+if (pendingOperationsRef.current === 0 && !hasErrorsRef.current) {
+  // All operations completed successfully
+  if (allSuccessesRef.current.length > 0) {
+toaster.create({
+  description: 
translate("bulkAction.delete.taskInstance.success.description", {
+count: allSuccessesRef.current.length,
+keys: allSuccessesRef.current.join(", "),
+  }),
+  title: translate("bulkAction.delete.taskInstance.success.title"),
+  type: "success",
+});
+onSuccessConfirm();
+  }
+  // Reset state for next operation
+  allSuccessesRef.current = [];
+  hasErrorsRef.current = false;
+}
+  };
+
+  const onError = (_error: unknown) => {
+setError(_error);
+hasErrorsRef.current = true;
+pendingOperationsRef.current -= 1;
+  };
+
+  const { isPending, mutate } = useTaskInstanceServiceBulkTaskInstances({
+onError,
+onSuccess,
+  });
+
+  const deleteTaskInstances = (entities: Array) => {
+// Reset state for new operation
+allSuccessesRef.current = [];
+hasErrorsRef.current = false;
+setError(undefined);
+
+if (Boolean(dagId) && Boolean(dagRunId) && dagId !== undefined && dagRunId 
!== undefined) {
+  pendingOperationsRef.current = 1;
+  mutate({
+dagId,
+dagRunId,
+requestBody: {
+  actions: [
+{
+  action: "delete",
+  entities: entities.map((ti) => ({ map_index: ti.map_index, 
task_id: ti.task_id })),
+},
+  ],
+},
+  });
+} else {
+  // cross dag run
+  const groupedByDagRunTIs: Record> = 
{};
+
+  entities.forEach((ti) => {
+(groupedByDagRunTIs[`${ti.dag_id}${SEPARATOR}${ti.dag_run_id}`] ??= 
[]).push(ti);
+  });
+
+  pendingOperationsRef.current = Object.keys(groupedByDagRunTIs).length;
+
+  Object.entries(groupedByDagRunTIs).forEach(([key, groupTIs]) => {
+const [groupDagId, groupDagRunId] = key.split(SEPARATO

Re: [PR] Add bulk TI deletion UI [airflow]

2025-10-17 Thread via GitHub


guan404ming commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2431608351


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,156 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState, useRef } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import type { TaskInstanceResponse } from "openapi/requests";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId?: string;
+  readonly dagRunId?: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+const SEPARATOR = "SEPARATOR";
+
+export const useBulkDeleteTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+  const pendingOperationsRef = useRef(0);
+  const allSuccessesRef = useRef>([]);
+  const hasErrorsRef = useRef(false);
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  dagId === undefined || dagRunId === undefined
+? []
+: [UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId: dagRunId })],
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+hasErrorsRef.current = true;
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+allSuccessesRef.current = [...allSuccessesRef.current, ...success];
+  }
+}
+
+pendingOperationsRef.current -= 1;
+
+if (pendingOperationsRef.current === 0 && !hasErrorsRef.current) {
+  // All operations completed successfully
+  if (allSuccessesRef.current.length > 0) {
+toaster.create({
+  description: 
translate("bulkAction.delete.taskInstance.success.description", {
+count: allSuccessesRef.current.length,
+keys: allSuccessesRef.current.join(", "),
+  }),
+  title: translate("bulkAction.delete.taskInstance.success.title"),
+  type: "success",
+});
+onSuccessConfirm();
+  }
+  // Reset state for next operation
+  allSuccessesRef.current = [];
+  hasErrorsRef.current = false;
+}
+  };
+
+  const onError = (_error: unknown) => {
+setError(_error);
+hasErrorsRef.current = true;
+pendingOperationsRef.current -= 1;
+  };
+
+  const { isPending, mutate } = useTaskInstanceServiceBulkTaskInstances({
+onError,
+onSuccess,
+  });
+
+  const deleteTaskInstances = (entities: Array) => {
+// Reset state for new operation
+allSuccessesRef.current = [];
+hasErrorsRef.current = false;
+setError(undefined);
+
+if (Boolean(dagId) && Boolean(dagRunId) && dagId !== undefined && dagRunId 
!== undefined) {
+  pendingOperationsRef.current = 1;
+  mutate({
+dagId,
+dagRunId,
+requestBody: {
+  actions: [
+{
+  action: "delete",
+  entities: entities.map((ti) => ({ map_index: ti.map_index, 
task_id: ti.task_id })),
+},
+  ],
+},
+  });
+} else {
+  // cross dag run
+  const groupedByDagRunTIs: Record> = 
{};
+
+  entities.forEach((ti) => {
+(groupedByDagRunTIs[`${ti.dag_id}${SEPARATOR}${ti.dag_run_id}`] ??= 
[]).push(ti);
+  });
+
+  pendingOperationsRef.current = Object.keys(groupedByDagRunTIs).length;
+
+  Object.entries(groupedByDagRunTIs).forEach(([key, groupTIs]) => {
+const [groupDagId, groupDagRunId] = key.split(SEPARATOR);

Re: [PR] Add bulk TI deletion UI [airflow]

2025-10-17 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2426802121


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,156 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState, useRef } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import type { TaskInstanceResponse } from "openapi/requests";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId?: string;
+  readonly dagRunId?: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+const SEPARATOR = "SEPARATOR";
+
+export const useBulkDeleteTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+  const pendingOperationsRef = useRef(0);
+  const allSuccessesRef = useRef>([]);
+  const hasErrorsRef = useRef(false);
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  dagId === undefined || dagRunId === undefined
+? []
+: [UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId: dagRunId })],
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+hasErrorsRef.current = true;
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+allSuccessesRef.current = [...allSuccessesRef.current, ...success];
+  }
+}
+
+pendingOperationsRef.current -= 1;
+
+if (pendingOperationsRef.current === 0 && !hasErrorsRef.current) {
+  // All operations completed successfully
+  if (allSuccessesRef.current.length > 0) {
+toaster.create({
+  description: 
translate("bulkAction.delete.taskInstance.success.description", {
+count: allSuccessesRef.current.length,
+keys: allSuccessesRef.current.join(", "),
+  }),
+  title: translate("bulkAction.delete.taskInstance.success.title"),
+  type: "success",
+});
+onSuccessConfirm();
+  }
+  // Reset state for next operation
+  allSuccessesRef.current = [];
+  hasErrorsRef.current = false;
+}
+  };
+
+  const onError = (_error: unknown) => {
+setError(_error);
+hasErrorsRef.current = true;
+pendingOperationsRef.current -= 1;
+  };
+
+  const { isPending, mutate } = useTaskInstanceServiceBulkTaskInstances({
+onError,
+onSuccess,
+  });
+
+  const deleteTaskInstances = (entities: Array) => {
+// Reset state for new operation
+allSuccessesRef.current = [];
+hasErrorsRef.current = false;
+setError(undefined);
+
+if (Boolean(dagId) && Boolean(dagRunId) && dagId !== undefined && dagRunId 
!== undefined) {
+  pendingOperationsRef.current = 1;
+  mutate({
+dagId,
+dagRunId,
+requestBody: {
+  actions: [
+{
+  action: "delete",
+  entities: entities.map((ti) => ({ map_index: ti.map_index, 
task_id: ti.task_id })),
+},
+  ],
+},
+  });
+} else {
+  // cross dag run
+  const groupedByDagRunTIs: Record> = 
{};
+
+  entities.forEach((ti) => {
+(groupedByDagRunTIs[`${ti.dag_id}${SEPARATOR}${ti.dag_run_id}`] ??= 
[]).push(ti);
+  });
+
+  pendingOperationsRef.current = Object.keys(groupedByDagRunTIs).length;
+
+  Object.entries(groupedByDagRunTIs).forEach(([key, groupTIs]) => {
+const [groupDagId, groupDagRunId] = key.split(SEPARATO

Re: [PR] Add bulk TI deletion UI [airflow]

2025-10-13 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2426829138


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,156 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState, useRef } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import type { TaskInstanceResponse } from "openapi/requests";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId?: string;
+  readonly dagRunId?: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+const SEPARATOR = "SEPARATOR";
+
+export const useBulkDeleteTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+  const pendingOperationsRef = useRef(0);
+  const allSuccessesRef = useRef>([]);
+  const hasErrorsRef = useRef(false);
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  dagId === undefined || dagRunId === undefined
+? []
+: [UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId: dagRunId })],
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+hasErrorsRef.current = true;
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+allSuccessesRef.current = [...allSuccessesRef.current, ...success];
+  }
+}
+
+pendingOperationsRef.current -= 1;
+
+if (pendingOperationsRef.current === 0 && !hasErrorsRef.current) {
+  // All operations completed successfully
+  if (allSuccessesRef.current.length > 0) {
+toaster.create({
+  description: 
translate("bulkAction.delete.taskInstance.success.description", {
+count: allSuccessesRef.current.length,
+keys: allSuccessesRef.current.join(", "),
+  }),
+  title: translate("bulkAction.delete.taskInstance.success.title"),
+  type: "success",
+});
+onSuccessConfirm();
+  }
+  // Reset state for next operation
+  allSuccessesRef.current = [];
+  hasErrorsRef.current = false;
+}
+  };
+
+  const onError = (_error: unknown) => {
+setError(_error);
+hasErrorsRef.current = true;
+pendingOperationsRef.current -= 1;
+  };
+
+  const { isPending, mutate } = useTaskInstanceServiceBulkTaskInstances({
+onError,
+onSuccess,
+  });
+
+  const deleteTaskInstances = (entities: Array) => {
+// Reset state for new operation
+allSuccessesRef.current = [];
+hasErrorsRef.current = false;
+setError(undefined);
+
+if (Boolean(dagId) && Boolean(dagRunId) && dagId !== undefined && dagRunId 
!== undefined) {
+  pendingOperationsRef.current = 1;
+  mutate({
+dagId,
+dagRunId,
+requestBody: {
+  actions: [
+{
+  action: "delete",
+  entities: entities.map((ti) => ({ map_index: ti.map_index, 
task_id: ti.task_id })),
+},
+  ],
+},
+  });
+} else {
+  // cross dag run
+  const groupedByDagRunTIs: Record> = 
{};
+
+  entities.forEach((ti) => {
+(groupedByDagRunTIs[`${ti.dag_id}${SEPARATOR}${ti.dag_run_id}`] ??= 
[]).push(ti);
+  });
+
+  pendingOperationsRef.current = Object.keys(groupedByDagRunTIs).length;
+
+  Object.entries(groupedByDagRunTIs).forEach(([key, groupTIs]) => {
+const [groupDagId, groupDagRunId] = key.split(SEPARATO

Re: [PR] Add bulk TI deletion UI [airflow]

2025-10-13 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2426802121


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,156 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState, useRef } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import type { TaskInstanceResponse } from "openapi/requests";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId?: string;
+  readonly dagRunId?: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+const SEPARATOR = "SEPARATOR";
+
+export const useBulkDeleteTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+  const pendingOperationsRef = useRef(0);
+  const allSuccessesRef = useRef>([]);
+  const hasErrorsRef = useRef(false);
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  dagId === undefined || dagRunId === undefined
+? []
+: [UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId: dagRunId })],
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+hasErrorsRef.current = true;
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+allSuccessesRef.current = [...allSuccessesRef.current, ...success];
+  }
+}
+
+pendingOperationsRef.current -= 1;
+
+if (pendingOperationsRef.current === 0 && !hasErrorsRef.current) {
+  // All operations completed successfully
+  if (allSuccessesRef.current.length > 0) {
+toaster.create({
+  description: 
translate("bulkAction.delete.taskInstance.success.description", {
+count: allSuccessesRef.current.length,
+keys: allSuccessesRef.current.join(", "),
+  }),
+  title: translate("bulkAction.delete.taskInstance.success.title"),
+  type: "success",
+});
+onSuccessConfirm();
+  }
+  // Reset state for next operation
+  allSuccessesRef.current = [];
+  hasErrorsRef.current = false;
+}
+  };
+
+  const onError = (_error: unknown) => {
+setError(_error);
+hasErrorsRef.current = true;
+pendingOperationsRef.current -= 1;
+  };
+
+  const { isPending, mutate } = useTaskInstanceServiceBulkTaskInstances({
+onError,
+onSuccess,
+  });
+
+  const deleteTaskInstances = (entities: Array) => {
+// Reset state for new operation
+allSuccessesRef.current = [];
+hasErrorsRef.current = false;
+setError(undefined);
+
+if (Boolean(dagId) && Boolean(dagRunId) && dagId !== undefined && dagRunId 
!== undefined) {
+  pendingOperationsRef.current = 1;
+  mutate({
+dagId,
+dagRunId,
+requestBody: {
+  actions: [
+{
+  action: "delete",
+  entities: entities.map((ti) => ({ map_index: ti.map_index, 
task_id: ti.task_id })),
+},
+  ],
+},
+  });
+} else {
+  // cross dag run
+  const groupedByDagRunTIs: Record> = 
{};
+
+  entities.forEach((ti) => {
+(groupedByDagRunTIs[`${ti.dag_id}${SEPARATOR}${ti.dag_run_id}`] ??= 
[]).push(ti);
+  });
+
+  pendingOperationsRef.current = Object.keys(groupedByDagRunTIs).length;
+
+  Object.entries(groupedByDagRunTIs).forEach(([key, groupTIs]) => {
+const [groupDagId, groupDagRunId] = key.split(SEPARATO

Re: [PR] Add bulk TI deletion UI [airflow]

2025-10-02 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3362005222

   > @guan404ming any progress on this one ? I think a UI warning could suffice 
as mentioned by Brent, and unblock this.
   
   Sure! I'm also agree with this solution. I've update the warning to a more 
detailed version, please let me if it could be better. Thanks!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-09-27 Thread via GitHub


FrankPortman commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-613011

   Not being able to easily sort/search-by-params/delete runs/TIs in bulk is a 
**big** con of AF3 for our use case.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-09-16 Thread via GitHub


bbovenzi commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3299833731

   > I guess it's up to users if they want to do it, but we should probably 
make it 100% clear it will delete all history and audit records of the TI.
   > 
   > I don't _personally_ like it, but now I think about it more I'm not sure 
that we should stop users from being able to do this if they want to.
   > 
   > Maybe a future change to add a config option to disable deleteing them at 
the deploy level
   
   How about we update the copy "This will remove all metadata related" and 
spell out what will be deleted? 


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-09-16 Thread via GitHub


ashb commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3299464136

   I guess it's up to users if they want to do it, but we should probably make 
it 100% clear it will delete all history and audit records of the TI.
   
   I don't _personally_ like it, but now I think about it more I'm not sure 
that we should stop users from being able to do this if they want to.
   
   Maybe a future change to add a config option to disable deleteing them at 
the deploy level


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-09-03 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3242877602

   This is actual deletion - we issue DELETE on the TI row. The 
`TaskInstanceHistory` table has CASCADE delete, so all history records are 
permanently lost when the TI is deleted. When the scheduler recreates the TI, 
it gets a new UUID and try_number resets to 0, losing all previous execution 
history.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-09-03 Thread via GitHub


ashb commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2313704208


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -249,6 +323,41 @@ export const TaskInstances = () => {
 onStateChange={setTableURLState}
 total={data?.total_entries}
   />
+  
+
+  
+{selectedRows.size} {translate("common:selected")}
+  
+  
+   1 ? "taskInstance_other" : 
"taskInstance_one"}`,
+)}`}
+disabled={selectedRows.size === 0}
+  >
+

Re: [PR] Add bulk TI deletion UI [airflow]

2025-09-01 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3243539026

   > -1. We should never delete TIs and TIH. This shouldn't even be a feature 
in the API!
   
   It seems like the singular TI deletion is already in our UI and API. Should 
we need to also remove those related features?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-09-01 Thread via GitHub


ashb commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3242939972

   -1. We should never delete TIs and TIH. This shouldn't even be a feature in 
the API!


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-09-01 Thread via GitHub


guan404ming commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2314272971


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -249,6 +323,41 @@ export const TaskInstances = () => {
 onStateChange={setTableURLState}
 total={data?.total_entries}
   />
+  
+
+  
+{selectedRows.size} {translate("common:selected")}
+  
+  
+   1 ? "taskInstance_other" : 
"taskInstance_one"}`,
+)}`}
+disabled={selectedRows.size === 0}
+  >
+ No, we don't have bulk clear currently in UI. It still needs to 
update API to support the feature.
   - want? -> Some users may want to clear multiple TI. It would take lots of 
time to clear one by one. So I think this is optional one but it would be 
useful in some cases.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-09-01 Thread via GitHub


ashb commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3242014593

   Hang on. With the TI history, I'm not sure we want to _ever_ delete TIs. Is 
this just a wording, or are we actually issuing a delete request on the TI row? 
(If so what happens to the history/tries table when the scheduler goes and 
recreates these rows?


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-31 Thread via GitHub


guan404ming commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2312399144


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -249,6 +323,41 @@ export const TaskInstances = () => {
 onStateChange={setTableURLState}
 total={data?.total_entries}
   />
+  
+
+  
+{selectedRows.size} {translate("common:selected")}
+  
+  
+   1 ? "taskInstance_other" : 
"taskInstance_one"}`,
+)}`}
+disabled={selectedRows.size === 0}
+  >
+https://github.com/user-attachments/assets/1f24c725-45a8-4477-a3b8-0a5592b123b0
   
   
   



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-31 Thread via GitHub


guan404ming commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2312399144


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -249,6 +323,41 @@ export const TaskInstances = () => {
 onStateChange={setTableURLState}
 total={data?.total_entries}
   />
+  
+
+  
+{selectedRows.size} {translate("common:selected")}
+  
+  
+   1 ? "taskInstance_other" : 
"taskInstance_one"}`,
+)}`}
+disabled={selectedRows.size === 0}
+  >
+

Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-29 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2310702206


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -249,6 +323,41 @@ export const TaskInstances = () => {
 onStateChange={setTableURLState}
 total={data?.total_entries}
   />
+  
+
+  
+{selectedRows.size} {translate("common:selected")}
+  
+  
+   1 ? "taskInstance_other" : 
"taskInstance_one"}`,
+)}`}
+disabled={selectedRows.size === 0}
+  >
+

Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-29 Thread via GitHub


guan404ming commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2310023033


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -51,17 +60,55 @@ const {
   STATE: STATE_PARAM,
 }: SearchParamsKeysType = SearchParamsKeys;
 
-const taskInstanceColumns = ({
+const getColumns = ({
+  allRowsSelected,
   dagId,
+  onRowSelect,
+  onSelectAll,
   runId,
+  selectedRows,
   taskId,
   translate,
 }: {
   dagId?: string;
   runId?: string;
   taskId?: string;
   translate: TFunction;
-}): Array> => [
+} & GetColumnsParams): Array> => [
+  ...(Boolean(dagId)

Review Comment:
   > I feel like people would want to perform bulk actions on the main task 
instance list page too?
   
   You're right. I initially disable it since cross dag deletion is not 
literally supported by our backend and need a great refactor for bulk service. 
But I think we could handle it in our hook now and refactor our backend after 
then. I've updated!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-29 Thread via GitHub


guan404ming commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2309903871


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -249,6 +323,41 @@ export const TaskInstances = () => {
 onStateChange={setTableURLState}
 total={data?.total_entries}
   />
+  
+
+  
+{selectedRows.size} {translate("common:selected")}
+  
+  
+   1 ? "taskInstance_other" : 
"taskInstance_one"}`,
+)}`}
+disabled={selectedRows.size === 0}
+  >
+

Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-27 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2305243650


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,87 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState } from "react";
+import { useTranslation } from "react-i18next";
+
+import {
+  useTaskInstanceServiceGetTaskInstancesKey,
+  useTaskInstanceServiceBulkTaskInstances,
+  UseGridServiceGetGridTiSummariesKeyFn,
+} from "openapi/queries";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+export const useBulkDeleteTaskInstances = ({ dagId, dagRunId, onSuccessConfirm 
}: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+  const { t: translate } = useTranslation("common");
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  UseGridServiceGetGridTiSummariesKeyFn({ dagId, runId: dagRunId }),
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+toaster.create({
+  description: translate("bulkAction.success.description", {

Review Comment:
   https://github.com/user-attachments/assets/eefec485-1be7-4364-9d50-dbbc1e39dde5";
 />
   
   We should probably write up separate translation descriptions for each 
action instead of relying on so many variable inputs.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-27 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2305237454


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -249,6 +323,41 @@ export const TaskInstances = () => {
 onStateChange={setTableURLState}
 total={data?.total_entries}
   />
+  
+
+  
+{selectedRows.size} {translate("common:selected")}
+  
+  
+   1 ? "taskInstance_other" : 
"taskInstance_one"}`,
+)}`}
+disabled={selectedRows.size === 0}
+  >
+

Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-27 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2305233420


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -51,17 +60,55 @@ const {
   STATE: STATE_PARAM,
 }: SearchParamsKeysType = SearchParamsKeys;
 
-const taskInstanceColumns = ({
+const getColumns = ({
+  allRowsSelected,
   dagId,
+  onRowSelect,
+  onSelectAll,
   runId,
+  selectedRows,
   taskId,
   translate,
 }: {
   dagId?: string;
   runId?: string;
   taskId?: string;
   translate: TFunction;
-}): Array> => [
+} & GetColumnsParams): Array> => [
+  ...(Boolean(dagId)

Review Comment:
   Why put a boolean on the select? I feel like people would want to perform 
bulk actions on the main task instance list page too?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-21 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3211562768

   > Backend PR has been merged, I think we can proceed forward with this when 
we have time.
   
   I think frontend part is almost done but I encounter some issues when 
deleting mapped ti and has submitted [pr](#54791) to fix it. I think I could 
mark this one as ready to review and would rebase after the fix got merged


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-21 Thread via GitHub


pierrejeambrun commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3210583927

   Backend PR has been merged, I think we can proceed forward with this.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-04 Thread via GitHub


phanikumv commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3150361699

   Waiting on the backend PR https://github.com/apache/airflow/pull/51850


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-08-01 Thread via GitHub


github-actions[bot] commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-3146046538

   This pull request has been automatically marked as stale because it has not 
had recent activity. It will be closed in 5 days if no further activity occurs. 
Thank you for your contributions.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-17 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-2981219260

   Thanks for the suggestion, I've implemented the backend part of this PR in 
https://github.com/apache/airflow/pull/51850.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-16 Thread via GitHub


pierrejeambrun commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-2977165577

   > The api needs some modification for handling ti with map_index as well. 
Let me mark this as draft.
   
   
   
   Thanks @guan404ming, do not hesitate to create a different PR for the 
backend part so it's easier to review and merge.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-13 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-2970702831

   > For mapped task instance the confirmation table is not correct, cf 
screenshot, actually 4 TIs are selected (3 mapped TI and 1 non mapped TI), and 
the modal only show 2 TIs)
   
   The api needs some modification for handling ti with map_index as well. Let 
me mark this as draft.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-13 Thread via GitHub


pierrejeambrun commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2145141381


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,83 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState } from "react";
+
+import {
+  UseDagRunServiceGetDagRunKeyFn,
+  useDagRunServiceGetDagRunsKey,
+  UseGridServiceGridDataKeyFn,
+  useTaskInstanceServiceBulkTaskInstances,
+  useTaskInstanceServiceGetTaskInstancesKey,
+} from "openapi/queries";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly clearSelections: VoidFunction;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+export const useBulkDeleteTaskInstances = ({ clearSelections, dagId, dagRunId, 
onSuccessConfirm }: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  UseDagRunServiceGetDagRunKeyFn({ dagId, dagRunId }),
+  [useDagRunServiceGetDagRunsKey],
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  UseGridServiceGridDataKeyFn({ dagId }, [{ dagId }]),
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+toaster.create({
+  description: `${success.length} task instances deleted successfully. 
Keys: ${success.join(", ")}`,
+  title: "Delete Task Instances Request Successful",
+  type: "success",
+});
+clearSelections();
+onSuccessConfirm();

Review Comment:
   Maybe those two could be merged into 1 `onSuccessConfirm` and it's up to the 
caller to pass a fn that will both clear selection + close modal, this way we 
avoid passing one extra prop to achieve that.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-12 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2143532666


##
airflow-core/src/airflow/ui/src/queries/useBulkDeleteTaskInstances.ts:
##
@@ -0,0 +1,83 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useQueryClient } from "@tanstack/react-query";
+import { useState } from "react";
+
+import {
+  UseDagRunServiceGetDagRunKeyFn,
+  useDagRunServiceGetDagRunsKey,
+  UseGridServiceGridDataKeyFn,
+  useTaskInstanceServiceBulkTaskInstances,
+  useTaskInstanceServiceGetTaskInstancesKey,
+} from "openapi/queries";
+import { toaster } from "src/components/ui";
+
+type Props = {
+  readonly clearSelections: VoidFunction;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly onSuccessConfirm: VoidFunction;
+};
+
+export const useBulkDeleteTaskInstances = ({ clearSelections, dagId, dagRunId, 
onSuccessConfirm }: Props) => {
+  const queryClient = useQueryClient();
+  const [error, setError] = useState(undefined);
+
+  const onSuccess = async (responseData: { delete?: { errors: Array; 
success: Array } }) => {
+const queryKeys = [
+  UseDagRunServiceGetDagRunKeyFn({ dagId, dagRunId }),
+  [useDagRunServiceGetDagRunsKey],
+  [useTaskInstanceServiceGetTaskInstancesKey],
+  UseGridServiceGridDataKeyFn({ dagId }, [{ dagId }]),
+];
+
+await Promise.all(queryKeys.map((key) => queryClient.invalidateQueries({ 
queryKey: key })));
+
+if (responseData.delete) {
+  const { errors, success } = responseData.delete;
+
+  if (Array.isArray(errors) && errors.length > 0) {
+const apiError = errors[0] as { error: string };
+
+setError({
+  body: { detail: apiError.error },
+});
+  } else if (Array.isArray(success) && success.length > 0) {
+toaster.create({
+  description: `${success.length} task instances deleted successfully. 
Keys: ${success.join(", ")}`,
+  title: "Delete Task Instances Request Successful",

Review Comment:
   translations needed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-12 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2143532306


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/TaskInstances.tsx:
##
@@ -246,6 +310,33 @@ export const TaskInstances = () => {
 onStateChange={setTableURLState}
 total={data?.total_entries}
   />
+  
+
+  
+{selectedRows.size} {translate("common:selected")}
+  
+  
+  

Review Comment:
   Translation needed



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-12 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2143530839


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstancesButton.tsx:
##
@@ -0,0 +1,124 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { useDisclosure } from "@chakra-ui/react";
+import { Button, Flex, Heading, Text } from "@chakra-ui/react";
+import type { TaskInstanceCollectionResponse, TaskInstanceResponse } from 
"openapi-gen/requests/types.gen";
+import { useTranslation } from "react-i18next";
+import { FiTrash2 } from "react-icons/fi";
+
+import { ActionAccordion } from "src/components/ActionAccordion";
+import ActionButton from "src/components/ui/ActionButton";
+import { Dialog } from "src/components/ui/Dialog";
+import { useBulkDeleteTaskInstances } from 
"src/queries/useBulkDeleteTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly deleteKeys: Array;
+};
+
+const DeleteTaskInstancesButton = ({ clearSelections, dagId, dagRunId, 
deleteKeys }: Props) => {
+  const { onClose, onOpen, open } = useDisclosure();
+  const { isPending, mutate } = useBulkDeleteTaskInstances({
+clearSelections,
+dagId,
+dagRunId,
+onSuccessConfirm: onClose,
+  });
+  const { t: translate } = useTranslation();
+
+  if (deleteKeys.length === 0) {
+return undefined;
+  }
+
+  const taskIds = deleteKeys.map((ti) => ti.task_id);
+  const type = translate("common:taskInstance_other");
+  const title = translate("dags:runAndTaskActions.delete.dialog.title", { type 
});
+  const warningText = 
translate("dags:runAndTaskActions.delete.dialog.warning", { type });
+  const deleteButtonText = translate("dags:runAndTaskActions.delete.button", { 
type });
+
+  const handleDelete = () => {
+if (dagId && dagRunId) {
+  mutate({
+dagId,
+dagRunId,
+requestBody: { actions: [{ action: "delete", entities: taskIds }] },
+  });
+} else {
+  // cross dag run
+  const grouped: Record> = {};
+
+  deleteKeys.forEach((ti) => {
+(grouped[ti.dag_run_id] ??= []).push(ti.task_id);
+  });
+  Object.entries(grouped).forEach(([groupDagRunId, groupTaskIds]) => {
+if (dagId && groupDagRunId) {
+  mutate({
+dagId,
+dagRunId: groupDagRunId,
+requestBody: { actions: [{ action: "delete", entities: 
groupTaskIds }] },
+  });
+}
+  });
+}
+  };
+
+  const affectedTasks = {
+task_instances: deleteKeys,
+total_entries: deleteKeys.length,
+  } as TaskInstanceCollectionResponse;
+
+  return (
+<>
+  }
+onClick={onOpen}
+text={deleteButtonText}
+variant="outline"
+withText
+  />
+  
+
+  
+  
+{title}
+

Review Comment:
   ```suggestion
   
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-12 Thread via GitHub


guan404ming commented on PR #51564:
URL: https://github.com/apache/airflow/pull/51564#issuecomment-2967857005

   I have updated the translation and try reuse `AffectedTasks`
   Current ui looks like:
   
   
https://github.com/user-attachments/assets/ed3a2b4d-b800-4443-81d5-664547bafe74
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-12 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2143324096


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstancesButton.tsx:
##
@@ -0,0 +1,123 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Code, Flex, Heading, Text, VStack, useDisclosure } from 
"@chakra-ui/react";
+import { FiTrash, FiTrash2 } from "react-icons/fi";
+
+import { ErrorAlert } from "src/components/ErrorAlert";
+import { Button, Dialog } from "src/components/ui";
+import { useBulkDeleteTaskInstances } from 
"src/queries/useBulkDeleteTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly deleteKeys: Array<{
+dagRunId: string;
+taskId: string;
+  }>;
+};
+
+const DeleteTaskInstancesButton = ({ clearSelections, dagId, dagRunId, 
deleteKeys }: Props) => {
+  const { onClose, onOpen, open } = useDisclosure();
+  const { error, isPending, mutate } = useBulkDeleteTaskInstances({
+clearSelections,
+dagId,
+dagRunId,
+onSuccessConfirm: onClose,
+  });
+
+  if (deleteKeys.length === 0) {
+return undefined;
+  }
+
+  const taskIds = deleteKeys.map((key) => key.taskId);
+
+  return (
+<>
+  
+ Delete
+  
+
+  
+
+  
+
+  Delete Task Instance{deleteKeys.length > 1 ? 
"s" : ""}
+
+  
+
+  
+
+  
+
+  You are about to delete{" "}
+  
+{deleteKeys.length} task instance{deleteKeys.length > 1 ? "s" 
: ""}.
+  
+  
+  
+{taskIds.join(", ")}

Review Comment:
   Or if the table makes it obvious enough. Let's just summarize "X task 
instances across Y dag runs will be deleted permanently"



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-12 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2143322092


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstancesButton.tsx:
##
@@ -0,0 +1,123 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Code, Flex, Heading, Text, VStack, useDisclosure } from 
"@chakra-ui/react";
+import { FiTrash, FiTrash2 } from "react-icons/fi";
+
+import { ErrorAlert } from "src/components/ErrorAlert";
+import { Button, Dialog } from "src/components/ui";
+import { useBulkDeleteTaskInstances } from 
"src/queries/useBulkDeleteTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly deleteKeys: Array<{
+dagRunId: string;
+taskId: string;
+  }>;
+};
+
+const DeleteTaskInstancesButton = ({ clearSelections, dagId, dagRunId, 
deleteKeys }: Props) => {
+  const { onClose, onOpen, open } = useDisclosure();
+  const { error, isPending, mutate } = useBulkDeleteTaskInstances({
+clearSelections,
+dagId,
+dagRunId,
+onSuccessConfirm: onClose,
+  });
+
+  if (deleteKeys.length === 0) {
+return undefined;
+  }
+
+  const taskIds = deleteKeys.map((key) => key.taskId);
+
+  return (
+<>
+  
+ Delete
+  
+
+  
+
+  
+
+  Delete Task Instance{deleteKeys.length > 1 ? 
"s" : ""}
+
+  
+
+  
+
+  
+
+  You are about to delete{" "}
+  
+{deleteKeys.length} task instance{deleteKeys.length > 1 ? "s" 
: ""}.
+  
+  
+  
+{taskIds.join(", ")}

Review Comment:
   Let's reuse the `AffectedTasks` we use in clearing and marking task 
instances. It'll be much easier to read what you're about to delete



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-12 Thread via GitHub


bbovenzi commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2143320288


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstancesButton.tsx:
##
@@ -0,0 +1,123 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Code, Flex, Heading, Text, VStack, useDisclosure } from 
"@chakra-ui/react";
+import { FiTrash, FiTrash2 } from "react-icons/fi";
+
+import { ErrorAlert } from "src/components/ErrorAlert";
+import { Button, Dialog } from "src/components/ui";
+import { useBulkDeleteTaskInstances } from 
"src/queries/useBulkDeleteTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly deleteKeys: Array<{
+dagRunId: string;
+taskId: string;
+  }>;
+};
+
+const DeleteTaskInstancesButton = ({ clearSelections, dagId, dagRunId, 
deleteKeys }: Props) => {
+  const { onClose, onOpen, open } = useDisclosure();
+  const { error, isPending, mutate } = useBulkDeleteTaskInstances({
+clearSelections,
+dagId,
+dagRunId,
+onSuccessConfirm: onClose,
+  });
+
+  if (deleteKeys.length === 0) {
+return undefined;
+  }
+
+  const taskIds = deleteKeys.map((key) => key.taskId);
+
+  return (
+<>
+  
+ Delete
+  
+
+  
+
+  
+
+  Delete Task Instance{deleteKeys.length > 1 ? 
"s" : ""}

Review Comment:
   You should be able to rebase now and get all the english translations.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-11 Thread via GitHub


guan404ming commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2140347820


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstancesButton.tsx:
##
@@ -0,0 +1,123 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Code, Flex, Heading, Text, VStack, useDisclosure } from 
"@chakra-ui/react";
+import { FiTrash, FiTrash2 } from "react-icons/fi";
+
+import { ErrorAlert } from "src/components/ErrorAlert";
+import { Button, Dialog } from "src/components/ui";
+import { useBulkDeleteTaskInstances } from 
"src/queries/useBulkDeleteTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly deleteKeys: Array<{
+dagRunId: string;
+taskId: string;
+  }>;
+};
+
+const DeleteTaskInstancesButton = ({ clearSelections, dagId, dagRunId, 
deleteKeys }: Props) => {
+  const { onClose, onOpen, open } = useDisclosure();
+  const { error, isPending, mutate } = useBulkDeleteTaskInstances({
+clearSelections,
+dagId,
+dagRunId,
+onSuccessConfirm: onClose,
+  });
+
+  if (deleteKeys.length === 0) {
+return undefined;
+  }
+
+  const taskIds = deleteKeys.map((key) => key.taskId);
+
+  return (
+<>
+  
+ Delete
+  
+
+  
+
+  
+
+  Delete Task Instance{deleteKeys.length > 1 ? 
"s" : ""}

Review Comment:
   Thanks for the review.
   Sure, I consider to update translation patch after #51558 got merged since 
it covers part of translation which would be used here and seems almost done.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-11 Thread via GitHub


guan404ming commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2140347820


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstancesButton.tsx:
##
@@ -0,0 +1,123 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Code, Flex, Heading, Text, VStack, useDisclosure } from 
"@chakra-ui/react";
+import { FiTrash, FiTrash2 } from "react-icons/fi";
+
+import { ErrorAlert } from "src/components/ErrorAlert";
+import { Button, Dialog } from "src/components/ui";
+import { useBulkDeleteTaskInstances } from 
"src/queries/useBulkDeleteTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly deleteKeys: Array<{
+dagRunId: string;
+taskId: string;
+  }>;
+};
+
+const DeleteTaskInstancesButton = ({ clearSelections, dagId, dagRunId, 
deleteKeys }: Props) => {
+  const { onClose, onOpen, open } = useDisclosure();
+  const { error, isPending, mutate } = useBulkDeleteTaskInstances({
+clearSelections,
+dagId,
+dagRunId,
+onSuccessConfirm: onClose,
+  });
+
+  if (deleteKeys.length === 0) {
+return undefined;
+  }
+
+  const taskIds = deleteKeys.map((key) => key.taskId);
+
+  return (
+<>
+  
+ Delete
+  
+
+  
+
+  
+
+  Delete Task Instance{deleteKeys.length > 1 ? 
"s" : ""}

Review Comment:
   Sure, I consider to update translation patch after #51558 since it cover 
part of translation which would be used here and almost done.



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-11 Thread via GitHub


guan404ming commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2140347820


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstancesButton.tsx:
##
@@ -0,0 +1,123 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Code, Flex, Heading, Text, VStack, useDisclosure } from 
"@chakra-ui/react";
+import { FiTrash, FiTrash2 } from "react-icons/fi";
+
+import { ErrorAlert } from "src/components/ErrorAlert";
+import { Button, Dialog } from "src/components/ui";
+import { useBulkDeleteTaskInstances } from 
"src/queries/useBulkDeleteTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly deleteKeys: Array<{
+dagRunId: string;
+taskId: string;
+  }>;
+};
+
+const DeleteTaskInstancesButton = ({ clearSelections, dagId, dagRunId, 
deleteKeys }: Props) => {
+  const { onClose, onOpen, open } = useDisclosure();
+  const { error, isPending, mutate } = useBulkDeleteTaskInstances({
+clearSelections,
+dagId,
+dagRunId,
+onSuccessConfirm: onClose,
+  });
+
+  if (deleteKeys.length === 0) {
+return undefined;
+  }
+
+  const taskIds = deleteKeys.map((key) => key.taskId);
+
+  return (
+<>
+  
+ Delete
+  
+
+  
+
+  
+
+  Delete Task Instance{deleteKeys.length > 1 ? 
"s" : ""}

Review Comment:
   Sure, I consider to update translation patch after #51558 got merged since 
it cover part of translation which would be used here and seems almost done.



##
airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstancesButton.tsx:
##
@@ -0,0 +1,123 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Code, Flex, Heading, Text, VStack, useDisclosure } from 
"@chakra-ui/react";
+import { FiTrash, FiTrash2 } from "react-icons/fi";
+
+import { ErrorAlert } from "src/components/ErrorAlert";
+import { Button, Dialog } from "src/components/ui";
+import { useBulkDeleteTaskInstances } from 
"src/queries/useBulkDeleteTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly deleteKeys: Array<{
+dagRunId: string;
+taskId: string;
+  }>;
+};
+
+const DeleteTaskInstancesButton = ({ clearSelections, dagId, dagRunId, 
deleteKeys }: Props) => {
+  const { onClose, onOpen, open } = useDisclosure();
+  const { error, isPending, mutate } = useBulkDeleteTaskInstances({
+clearSelections,
+dagId,
+dagRunId,
+onSuccessConfirm: onClose,
+  });
+
+  if (deleteKeys.length === 0) {
+return undefined;
+  }
+
+  const taskIds = deleteKeys.map((key) => key.taskId);
+
+  return (
+<>
+  
+ Delete
+  
+
+  
+
+  
+
+  Delete Task Instance{deleteKeys.length > 1 ? 
"s" : ""}

Review Comment:
   Sure, I consider to update translation patch after #51558 got merged since 
it cover part of translation which would be used here and seems almost done. 
Don't want to block that one or create conflict. Thanks!



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please co

Re: [PR] Add bulk TI deletion UI [airflow]

2025-06-10 Thread via GitHub


jscheffl commented on code in PR #51564:
URL: https://github.com/apache/airflow/pull/51564#discussion_r2138651333


##
airflow-core/src/airflow/ui/src/pages/TaskInstances/DeleteTaskInstancesButton.tsx:
##
@@ -0,0 +1,123 @@
+/*!
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+import { Code, Flex, Heading, Text, VStack, useDisclosure } from 
"@chakra-ui/react";
+import { FiTrash, FiTrash2 } from "react-icons/fi";
+
+import { ErrorAlert } from "src/components/ErrorAlert";
+import { Button, Dialog } from "src/components/ui";
+import { useBulkDeleteTaskInstances } from 
"src/queries/useBulkDeleteTaskInstances";
+
+type Props = {
+  readonly clearSelections: () => void;
+  readonly dagId: string;
+  readonly dagRunId: string;
+  readonly deleteKeys: Array<{
+dagRunId: string;
+taskId: string;
+  }>;
+};
+
+const DeleteTaskInstancesButton = ({ clearSelections, dagId, dagRunId, 
deleteKeys }: Props) => {
+  const { onClose, onOpen, open } = useDisclosure();
+  const { error, isPending, mutate } = useBulkDeleteTaskInstances({
+clearSelections,
+dagId,
+dagRunId,
+onSuccessConfirm: onClose,
+  });
+
+  if (deleteKeys.length === 0) {
+return undefined;
+  }
+
+  const taskIds = deleteKeys.map((key) => key.taskId);
+
+  return (
+<>
+  
+ Delete
+  
+
+  
+
+  
+
+  Delete Task Instance{deleteKeys.length > 1 ? 
"s" : ""}

Review Comment:
   As we are in the process of translation of UI, can you please add i18n to 
the new UI so that we do not merge new "gaps"?



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]



[PR] Add bulk TI deletion UI [airflow]

2025-06-10 Thread via GitHub


guan404ming opened a new pull request, #51564:
URL: https://github.com/apache/airflow/pull/51564

   
   
   
   
   ## Related PR
   
   - this one is the follow up pr of #50443 to implement bulk TI deletion ui
   
   ## How
   
   - add multi-select in ti table
   - minor fix in bulk TI deletion endpoint
   
   
https://github.com/user-attachments/assets/ed9455dd-ed94-44a0-a913-2641db5663dd
   
   
   ---
   **^ Add meaningful description above**
   Read the **[Pull Request 
Guidelines](https://github.com/apache/airflow/blob/main/contributing-docs/05_pull_requests.rst#pull-request-guidelines)**
 for more information.
   In case of fundamental code changes, an Airflow Improvement Proposal 
([AIP](https://cwiki.apache.org/confluence/display/AIRFLOW/Airflow+Improvement+Proposals))
 is needed.
   In case of a new dependency, check compliance with the [ASF 3rd Party 
License Policy](https://www.apache.org/legal/resolved.html#category-x).
   In case of backwards incompatible changes please leave a note in a 
newsfragment file, named `{pr_number}.significant.rst` or 
`{issue_number}.significant.rst`, in 
[airflow-core/newsfragments](https://github.com/apache/airflow/tree/main/airflow-core/newsfragments).
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]