diff --git a/ui/src/actions/tasksActions.ts b/ui/src/actions/tasksActions.ts index 4aff61f..2e7be94 100644 --- a/ui/src/actions/tasksActions.ts +++ b/ui/src/actions/tasksActions.ts @@ -16,6 +16,7 @@ import { listScheduledTasks, ListScheduledTasksResponse, PaginationOptions, + runDeadTask, } from "../api"; import { Dispatch } from "redux"; @@ -38,6 +39,9 @@ export const LIST_DEAD_TASKS_ERROR = "LIST_DEAD_TASKS_ERROR"; export const CANCEL_ACTIVE_TASK_BEGIN = "CANCEL_ACTIVE_TASK_BEGIN"; export const CANCEL_ACTIVE_TASK_SUCCESS = "CANCEL_ACTIVE_TASK_SUCCESS"; export const CANCEL_ACTIVE_TASK_ERROR = "CANCEL_ACTIVE_TASK_ERROR"; +export const RUN_DEAD_TASK_BEGIN = "RUN_DEAD_TASK_BEGIN"; +export const RUN_DEAD_TASK_SUCCESS = "RUN_DEAD_TASK_SUCCESS"; +export const RUN_DEAD_TASK_ERROR = "RUN_DEAD_TASK_ERROR"; export const DELETE_SCHEDULED_TASK_BEGIN = "DELETE_SCHEDULED_TASK_BEGIN"; export const DELETE_SCHEDULED_TASK_SUCCESS = "DELETE_SCHEDULED_TASK_SUCCESS"; export const DELETE_SCHEDULED_TASK_ERROR = "DELETE_SCHEDULED_TASK_ERROR"; @@ -156,6 +160,25 @@ interface CancelActiveTaskErrorAction { error: string; } +interface RunDeadTaskBeginAction { + type: typeof RUN_DEAD_TASK_BEGIN; + queue: string; + taskKey: string; +} + +interface RunDeadTaskSuccessAction { + type: typeof RUN_DEAD_TASK_SUCCESS; + queue: string; + taskKey: string; +} + +interface RunDeadTaskErrorAction { + type: typeof RUN_DEAD_TASK_ERROR; + queue: string; + taskKey: string; + error: string; +} + interface DeleteScheduledTaskBeginAction { type: typeof DELETE_SCHEDULED_TASK_BEGIN; queue: string; @@ -252,6 +275,9 @@ export type TasksActionTypes = | CancelActiveTaskBeginAction | CancelActiveTaskSuccessAction | CancelActiveTaskErrorAction + | RunDeadTaskBeginAction + | RunDeadTaskSuccessAction + | RunDeadTaskErrorAction | DeleteScheduledTaskBeginAction | DeleteScheduledTaskSuccessAction | DeleteScheduledTaskErrorAction @@ -397,6 +423,24 @@ export function cancelActiveTaskAsync(queue: string, taskId: string) { }; } +export function runDeadTaskAsync(queue: string, taskKey: string) { + return async (dispatch: Dispatch) => { + dispatch({ type: RUN_DEAD_TASK_BEGIN, queue, taskKey }); + try { + await runDeadTask(queue, taskKey); + dispatch({ type: RUN_DEAD_TASK_SUCCESS, queue, taskKey }); + } catch (error) { + console.error("runDeadTaskAsync: ", error); + dispatch({ + type: RUN_DEAD_TASK_ERROR, + error: `Could not run task: ${taskKey}`, + queue, + taskKey, + }); + } + }; +} + export function deleteScheduledTaskAsync(queue: string, taskKey: string) { return async (dispatch: Dispatch) => { dispatch({ type: DELETE_SCHEDULED_TASK_BEGIN, queue, taskKey }); diff --git a/ui/src/api.ts b/ui/src/api.ts index 7395eef..d891535 100644 --- a/ui/src/api.ts +++ b/ui/src/api.ts @@ -268,6 +268,16 @@ export async function deleteRetryTask( }); } +export async function runDeadTask( + qname: string, + taskKey: string +): Promise { + await axios({ + method: "post", + url: `${BASE_URL}/queues/${qname}/dead_tasks/${taskKey}:run`, + }); +} + export async function deleteDeadTask( qname: string, taskKey: string diff --git a/ui/src/components/DeadTasksTable.tsx b/ui/src/components/DeadTasksTable.tsx index 397766f..6eca407 100644 --- a/ui/src/components/DeadTasksTable.tsx +++ b/ui/src/components/DeadTasksTable.tsx @@ -27,6 +27,7 @@ import syntaxHighlightStyle from "react-syntax-highlighter/dist/esm/styles/hljs/ import { AppState } from "../store"; import { listDeadTasksAsync, + runDeadTaskAsync, deleteDeadTaskAsync, batchDeleteDeadTasksAsync, } from "../actions/tasksActions"; @@ -69,6 +70,7 @@ function mapStateToProps(state: AppState) { const mapDispatchToProps = { listDeadTasksAsync, + runDeadTaskAsync, deleteDeadTaskAsync, batchDeleteDeadTasksAsync, }; @@ -203,6 +205,9 @@ function DeadTasksTable(props: Props & ReduxProps) { ); } }} + onRunClick={() => { + props.runDeadTaskAsync(queue, task.key); + }} onDeleteClick={() => { props.deleteDeadTaskAsync(queue, task.key); }} @@ -237,6 +242,7 @@ interface RowProps { task: DeadTaskExtended; isSelected: boolean; onSelectChange: (checked: boolean) => void; + onRunClick: () => void; onDeleteClick: () => void; } @@ -274,6 +280,9 @@ 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 da689cc..11eb641 100644 --- a/ui/src/reducers/queuesReducer.ts +++ b/ui/src/reducers/queuesReducer.ts @@ -23,6 +23,7 @@ import { LIST_PENDING_TASKS_SUCCESS, LIST_RETRY_TASKS_SUCCESS, LIST_SCHEDULED_TASKS_SUCCESS, + RUN_DEAD_TASK_SUCCESS, TasksActionTypes, } from "../actions/tasksActions"; import { DailyStat, Queue } from "../api"; @@ -151,6 +152,23 @@ function queuesReducer( return { ...state, data: newData }; } + case RUN_DEAD_TASK_SUCCESS: { + const newData = state.data.map((queueInfo) => { + if (queueInfo.name !== action.queue) { + return queueInfo; + } + return { + ...queueInfo, + currentStats: { + ...queueInfo.currentStats, + pending: queueInfo.currentStats.pending + 1, + dead: queueInfo.currentStats.dead - 1, + }, + }; + }); + return { ...state, data: newData }; + } + case DELETE_SCHEDULED_TASK_SUCCESS: { const newData = state.data.map((queueInfo) => { if (queueInfo.name !== action.queue) { diff --git a/ui/src/reducers/snackbarReducer.ts b/ui/src/reducers/snackbarReducer.ts index 601e1a3..35c91e3 100644 --- a/ui/src/reducers/snackbarReducer.ts +++ b/ui/src/reducers/snackbarReducer.ts @@ -7,6 +7,7 @@ import { DELETE_DEAD_TASK_SUCCESS, DELETE_RETRY_TASK_SUCCESS, DELETE_SCHEDULED_TASK_SUCCESS, + RUN_DEAD_TASK_SUCCESS, TasksActionTypes, } from "../actions/tasksActions"; @@ -33,6 +34,13 @@ function snackbarReducer( isOpen: false, }; + case RUN_DEAD_TASK_SUCCESS: + return { + isOpen: true, + // TODO: show only task id + message: `Dead task ${action.taskKey} is now pending`, + }; + case DELETE_SCHEDULED_TASK_SUCCESS: return { isOpen: true, diff --git a/ui/src/reducers/tasksReducer.ts b/ui/src/reducers/tasksReducer.ts index f010831..9b62dc8 100644 --- a/ui/src/reducers/tasksReducer.ts +++ b/ui/src/reducers/tasksReducer.ts @@ -30,6 +30,9 @@ import { BATCH_DELETE_DEAD_TASKS_BEGIN, BATCH_DELETE_DEAD_TASKS_SUCCESS, BATCH_DELETE_DEAD_TASKS_ERROR, + RUN_DEAD_TASK_BEGIN, + RUN_DEAD_TASK_SUCCESS, + RUN_DEAD_TASK_ERROR, } from "../actions/tasksActions"; import { ActiveTask, @@ -419,6 +422,7 @@ function tasksReducer( }, }; + case RUN_DEAD_TASK_BEGIN: case DELETE_DEAD_TASK_BEGIN: return { ...state, @@ -433,6 +437,7 @@ function tasksReducer( }, }; + case RUN_DEAD_TASK_SUCCESS: case DELETE_DEAD_TASK_SUCCESS: return { ...state, @@ -444,6 +449,7 @@ function tasksReducer( }, }; + case RUN_DEAD_TASK_ERROR: case DELETE_DEAD_TASK_ERROR: return { ...state,