diff --git a/ui/src/actions/tasksActions.ts b/ui/src/actions/tasksActions.ts index 7e4858a..4eb515a 100644 --- a/ui/src/actions/tasksActions.ts +++ b/ui/src/actions/tasksActions.ts @@ -4,6 +4,7 @@ import { batchRunDeadTasks, BatchRunTasksResponse, cancelActiveTask, + deleteAllDeadTasks, deleteDeadTask, deleteRetryTask, deleteScheduledTask, @@ -60,6 +61,9 @@ export const BATCH_DELETE_DEAD_TASKS_BEGIN = "BATCH_DELETE_DEAD_TASKS_BEGIN"; export const BATCH_DELETE_DEAD_TASKS_SUCCESS = "BATCH_DELETE_DEAD_TASKS_SUCCESS"; export const BATCH_DELETE_DEAD_TASKS_ERROR = "BATCH_DELETE_DEAD_TASKS_ERROR"; +export const DELETE_ALL_DEAD_TASKS_BEGIN = "DELETE_ALL_DEAD_TASKS_BEGIN"; +export const DELETE_ALL_DEAD_TASKS_SUCCESS = "DELETE_ALL_DEAD_TASKS_SUCCESS"; +export const DELETE_ALL_DEAD_TASKS_ERROR = "DELETE_ALL_DEAD_TASKS_ERROR"; interface ListActiveTasksBeginAction { type: typeof LIST_ACTIVE_TASKS_BEGIN; @@ -279,6 +283,22 @@ interface BatchRunDeadTasksErrorAction { error: string; } +interface DeleteAllDeadTasksBeginAction { + type: typeof DELETE_ALL_DEAD_TASKS_BEGIN; + queue: string; +} + +interface DeleteAllDeadTasksSuccessAction { + type: typeof DELETE_ALL_DEAD_TASKS_SUCCESS; + queue: string; +} + +interface DeleteAllDeadTasksErrorAction { + type: typeof DELETE_ALL_DEAD_TASKS_ERROR; + queue: string; + error: string; +} + // Union of all tasks related action types. export type TasksActionTypes = | ListActiveTasksBeginAction @@ -316,7 +336,10 @@ export type TasksActionTypes = | BatchDeleteDeadTasksErrorAction | BatchRunDeadTasksBeginAction | BatchRunDeadTasksSuccessAction - | BatchRunDeadTasksErrorAction; + | BatchRunDeadTasksErrorAction + | DeleteAllDeadTasksBeginAction + | DeleteAllDeadTasksSuccessAction + | DeleteAllDeadTasksErrorAction; export function listActiveTasksAsync( qname: string, @@ -565,3 +588,20 @@ export function batchRunDeadTasksAsync(queue: string, taskKeys: string[]) { } }; } + +export function deleteAllDeadTasksAsync(queue: string) { + return async (dispatch: Dispatch) => { + dispatch({ type: DELETE_ALL_DEAD_TASKS_BEGIN, queue }); + try { + await deleteAllDeadTasks(queue); + dispatch({ type: DELETE_ALL_DEAD_TASKS_SUCCESS, queue }); + } catch (error) { + console.error("deleteAllDeadTasksAsync: ", error); + dispatch({ + type: DELETE_ALL_DEAD_TASKS_ERROR, + error: `Could not delete all dead tasks`, + queue, + }); + } + }; +} diff --git a/ui/src/api.ts b/ui/src/api.ts index 3ec622d..2e85350 100644 --- a/ui/src/api.ts +++ b/ui/src/api.ts @@ -307,6 +307,13 @@ export async function batchDeleteDeadTasks( return resp.data; } +export async function deleteAllDeadTasks(qname: string): Promise { + await axios({ + method: "delete", + url: `${BASE_URL}/queues/${qname}/dead_tasks:delete_all`, + }); +} + export async function batchRunDeadTasks( qname: string, taskKeys: string[] diff --git a/ui/src/components/DeadTasksTable.tsx b/ui/src/components/DeadTasksTable.tsx index 6652b29..56a115f 100644 --- a/ui/src/components/DeadTasksTable.tsx +++ b/ui/src/components/DeadTasksTable.tsx @@ -31,6 +31,7 @@ import { batchDeleteDeadTasksAsync, batchRunDeadTasksAsync, deleteDeadTaskAsync, + deleteAllDeadTasksAsync, listDeadTasksAsync, runDeadTaskAsync, } from "../actions/tasksActions"; @@ -67,6 +68,7 @@ function mapStateToProps(state: AppState) { loading: state.tasks.deadTasks.loading, tasks: state.tasks.deadTasks.data, batchActionPending: state.tasks.deadTasks.batchActionPending, + deleteAllRequestPending: state.tasks.deadTasks.deleteAllRequestPending, pollInterval: state.settings.pollInterval, }; } @@ -75,6 +77,7 @@ const mapDispatchToProps = { listDeadTasksAsync, runDeadTaskAsync, deleteDeadTaskAsync, + deleteAllDeadTasksAsync, batchRunDeadTasksAsync, batchDeleteDeadTasksAsync, }; @@ -125,6 +128,11 @@ function DeadTasksTable(props: Props & ReduxProps) { const handleMenuClose = () => setMenuAnchor(null); + const handleDeleteAllClick = () => { + props.deleteAllDeadTasksAsync(queue); + setMenuAnchor(null); + }; + const fetchData = useCallback(() => { const pageOpts = { page: page + 1, size: pageSize }; listDeadTasksAsync(queue, pageOpts); @@ -170,7 +178,12 @@ function DeadTasksTable(props: Props & ReduxProps) { onClose={handleMenuClose} > Run All - Delete All + + Delete All + {numSelected > 0 && ( { props.deleteDeadTaskAsync(queue, task.key); }} + deleteAllRequestPending={props.deleteAllRequestPending} /> ))} @@ -278,6 +292,7 @@ interface RowProps { onSelectChange: (checked: boolean) => void; onRunClick: () => void; onDeleteClick: () => void; + deleteAllRequestPending: boolean; } function Row(props: RowProps) { @@ -314,10 +329,16 @@ function Row(props: RowProps) { {timeAgo(task.last_failed_at)} {task.error_message} - - diff --git a/ui/src/reducers/queuesReducer.ts b/ui/src/reducers/queuesReducer.ts index 656b944..e3a50c8 100644 --- a/ui/src/reducers/queuesReducer.ts +++ b/ui/src/reducers/queuesReducer.ts @@ -16,6 +16,7 @@ import { import { BATCH_DELETE_DEAD_TASKS_SUCCESS, BATCH_RUN_DEAD_TASKS_SUCCESS, + DELETE_ALL_DEAD_TASKS_SUCCESS, DELETE_DEAD_TASK_SUCCESS, DELETE_RETRY_TASK_SUCCESS, DELETE_SCHEDULED_TASK_SUCCESS, @@ -255,6 +256,22 @@ function queuesReducer( return { ...state, data: newData }; } + case DELETE_ALL_DEAD_TASKS_SUCCESS: { + const newData = state.data.map((queueInfo) => { + if (queueInfo.name !== action.queue) { + return queueInfo; + } + return { + ...queueInfo, + currentStats: { + ...queueInfo.currentStats, + dead: 0, + }, + }; + }); + return { ...state, data: newData }; + } + default: return state; } diff --git a/ui/src/reducers/snackbarReducer.ts b/ui/src/reducers/snackbarReducer.ts index 026d4ce..dd3864c 100644 --- a/ui/src/reducers/snackbarReducer.ts +++ b/ui/src/reducers/snackbarReducer.ts @@ -5,6 +5,7 @@ import { import { BATCH_DELETE_DEAD_TASKS_SUCCESS, BATCH_RUN_DEAD_TASKS_SUCCESS, + DELETE_ALL_DEAD_TASKS_SUCCESS, DELETE_DEAD_TASK_SUCCESS, DELETE_RETRY_TASK_SUCCESS, DELETE_SCHEDULED_TASK_SUCCESS, @@ -79,6 +80,12 @@ function snackbarReducer( }; } + case DELETE_ALL_DEAD_TASKS_SUCCESS: + return { + isOpen: true, + message: `All dead tasks delete`, + }; + default: return state; } diff --git a/ui/src/reducers/tasksReducer.ts b/ui/src/reducers/tasksReducer.ts index 25633ce..9d03c7c 100644 --- a/ui/src/reducers/tasksReducer.ts +++ b/ui/src/reducers/tasksReducer.ts @@ -36,6 +36,9 @@ import { BATCH_RUN_DEAD_TASKS_BEGIN, BATCH_RUN_DEAD_TASKS_ERROR, BATCH_RUN_DEAD_TASKS_SUCCESS, + DELETE_ALL_DEAD_TASKS_BEGIN, + DELETE_ALL_DEAD_TASKS_SUCCESS, + DELETE_ALL_DEAD_TASKS_ERROR, } from "../actions/tasksActions"; import { ActiveTask, @@ -97,6 +100,7 @@ interface TasksState { deadTasks: { loading: boolean; batchActionPending: boolean; + deleteAllRequestPending: boolean; error: string; data: DeadTaskExtended[]; }; @@ -126,6 +130,7 @@ const initialState: TasksState = { deadTasks: { loading: false, batchActionPending: false, + deleteAllRequestPending: false, error: "", data: [], }, @@ -467,6 +472,34 @@ function tasksReducer( }, }; + case DELETE_ALL_DEAD_TASKS_BEGIN: + return { + ...state, + deadTasks: { + ...state.deadTasks, + deleteAllRequestPending: true, + }, + }; + + case DELETE_ALL_DEAD_TASKS_SUCCESS: + return { + ...state, + deadTasks: { + ...state.deadTasks, + deleteAllRequestPending: false, + data: [], + }, + }; + + case DELETE_ALL_DEAD_TASKS_ERROR: + return { + ...state, + deadTasks: { + ...state.deadTasks, + deleteAllRequestPending: false, + }, + }; + case BATCH_RUN_DEAD_TASKS_BEGIN: case BATCH_DELETE_DEAD_TASKS_BEGIN: return {