mirror of
https://github.com/hibiken/asynqmon.git
synced 2025-01-19 03:05:53 +08:00
Add delete button for scheduled and dead tasks
This commit is contained in:
parent
601a7c8add
commit
ce978f4516
@ -1,6 +1,8 @@
|
||||
import {
|
||||
cancelActiveTask,
|
||||
deleteDeadTask,
|
||||
deleteRetryTask,
|
||||
deleteScheduledTask,
|
||||
listActiveTasks,
|
||||
ListActiveTasksResponse,
|
||||
listDeadTasks,
|
||||
@ -34,9 +36,15 @@ 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 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";
|
||||
export const DELETE_RETRY_TASK_BEGIN = "DELETE_RETRY_TASK_BEGIN";
|
||||
export const DELETE_RETRY_TASK_SUCCESS = "DELETE_RETRY_TASK_SUCCESS";
|
||||
export const DELETE_RETRY_TASK_ERROR = "DELETE_RETRY_TASK_ERROR";
|
||||
export const DELETE_DEAD_TASK_BEGIN = "DELETE_DEAD_TASK_BEGIN";
|
||||
export const DELETE_DEAD_TASK_SUCCESS = "DELETE_DEAD_TASK_SUCCESS";
|
||||
export const DELETE_DEAD_TASK_ERROR = "DELETE_DEAD_TASK_ERROR";
|
||||
|
||||
interface ListActiveTasksBeginAction {
|
||||
type: typeof LIST_ACTIVE_TASKS_BEGIN;
|
||||
@ -142,6 +150,25 @@ interface CancelActiveTaskErrorAction {
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface DeleteScheduledTaskBeginAction {
|
||||
type: typeof DELETE_SCHEDULED_TASK_BEGIN;
|
||||
queue: string;
|
||||
taskKey: string;
|
||||
}
|
||||
|
||||
interface DeleteScheduledTaskSuccessAction {
|
||||
type: typeof DELETE_SCHEDULED_TASK_SUCCESS;
|
||||
queue: string;
|
||||
taskKey: string;
|
||||
}
|
||||
|
||||
interface DeleteScheduledTaskErrorAction {
|
||||
type: typeof DELETE_SCHEDULED_TASK_ERROR;
|
||||
queue: string;
|
||||
taskKey: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface DeleteRetryTaskBeginAction {
|
||||
type: typeof DELETE_RETRY_TASK_BEGIN;
|
||||
queue: string;
|
||||
@ -160,6 +187,26 @@ interface DeleteRetryTaskErrorAction {
|
||||
taskKey: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
interface DeleteDeadTaskBeginAction {
|
||||
type: typeof DELETE_DEAD_TASK_BEGIN;
|
||||
queue: string;
|
||||
taskKey: string;
|
||||
}
|
||||
|
||||
interface DeleteDeadTaskSuccessAction {
|
||||
type: typeof DELETE_DEAD_TASK_SUCCESS;
|
||||
queue: string;
|
||||
taskKey: string;
|
||||
}
|
||||
|
||||
interface DeleteDeadTaskErrorAction {
|
||||
type: typeof DELETE_DEAD_TASK_ERROR;
|
||||
queue: string;
|
||||
taskKey: string;
|
||||
error: string;
|
||||
}
|
||||
|
||||
// Union of all tasks related action types.
|
||||
export type TasksActionTypes =
|
||||
| ListActiveTasksBeginAction
|
||||
@ -180,9 +227,15 @@ export type TasksActionTypes =
|
||||
| CancelActiveTaskBeginAction
|
||||
| CancelActiveTaskSuccessAction
|
||||
| CancelActiveTaskErrorAction
|
||||
| DeleteScheduledTaskBeginAction
|
||||
| DeleteScheduledTaskSuccessAction
|
||||
| DeleteScheduledTaskErrorAction
|
||||
| DeleteRetryTaskBeginAction
|
||||
| DeleteRetryTaskSuccessAction
|
||||
| DeleteRetryTaskErrorAction;
|
||||
| DeleteRetryTaskErrorAction
|
||||
| DeleteDeadTaskBeginAction
|
||||
| DeleteDeadTaskSuccessAction
|
||||
| DeleteDeadTaskErrorAction;
|
||||
|
||||
export function listActiveTasksAsync(
|
||||
qname: string,
|
||||
@ -316,6 +369,24 @@ export function cancelActiveTaskAsync(queue: string, taskId: string) {
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteScheduledTaskAsync(queue: string, taskKey: string) {
|
||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
||||
dispatch({ type: DELETE_SCHEDULED_TASK_BEGIN, queue, taskKey });
|
||||
try {
|
||||
await deleteScheduledTask(queue, taskKey);
|
||||
dispatch({ type: DELETE_SCHEDULED_TASK_SUCCESS, queue, taskKey });
|
||||
} catch (error) {
|
||||
console.error("deleteScheduledTaskAsync: ", error);
|
||||
dispatch({
|
||||
type: DELETE_SCHEDULED_TASK_ERROR,
|
||||
error: `Could not delete task: ${taskKey}`,
|
||||
queue,
|
||||
taskKey,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteRetryTaskAsync(queue: string, taskKey: string) {
|
||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
||||
dispatch({ type: DELETE_RETRY_TASK_BEGIN, queue, taskKey });
|
||||
@ -333,3 +404,21 @@ export function deleteRetryTaskAsync(queue: string, taskKey: string) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function deleteDeadTaskAsync(queue: string, taskKey: string) {
|
||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
||||
dispatch({ type: DELETE_DEAD_TASK_BEGIN, queue, taskKey });
|
||||
try {
|
||||
await deleteDeadTask(queue, taskKey);
|
||||
dispatch({ type: DELETE_DEAD_TASK_SUCCESS, queue, taskKey });
|
||||
} catch (error) {
|
||||
console.error("deleteDeadTaskAsync: ", error);
|
||||
dispatch({
|
||||
type: DELETE_DEAD_TASK_ERROR,
|
||||
error: `Could not delete task: ${taskKey}`,
|
||||
queue,
|
||||
taskKey,
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -243,6 +243,16 @@ export async function listDeadTasks(
|
||||
return resp.data;
|
||||
}
|
||||
|
||||
export async function deleteScheduledTask(
|
||||
qname: string,
|
||||
taskKey: string
|
||||
): Promise<void> {
|
||||
await axios({
|
||||
method: "delete",
|
||||
url: `${BASE_URL}/queues/${qname}/scheduled_tasks/${taskKey}`,
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteRetryTask(
|
||||
qname: string,
|
||||
taskKey: string
|
||||
@ -253,6 +263,16 @@ export async function deleteRetryTask(
|
||||
});
|
||||
}
|
||||
|
||||
export async function deleteDeadTask(
|
||||
qname: string,
|
||||
taskKey: string
|
||||
): Promise<void> {
|
||||
await axios({
|
||||
method: "delete",
|
||||
url: `${BASE_URL}/queues/${qname}/dead_tasks/${taskKey}`,
|
||||
});
|
||||
}
|
||||
|
||||
export async function listSchedulerEntries(): Promise<ListSchedulerEntriesResponse> {
|
||||
const resp = await axios({
|
||||
method: "get",
|
||||
|
@ -22,14 +22,17 @@ import AlertTitle from "@material-ui/lab/AlertTitle";
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import syntaxHighlightStyle from "react-syntax-highlighter/dist/esm/styles/hljs/github";
|
||||
import { AppState } from "../store";
|
||||
import { listDeadTasksAsync } from "../actions/tasksActions";
|
||||
import { DeadTask } from "../api";
|
||||
import {
|
||||
listDeadTasksAsync,
|
||||
deleteDeadTaskAsync,
|
||||
} from "../actions/tasksActions";
|
||||
import TablePaginationActions, {
|
||||
defaultPageSize,
|
||||
rowsPerPageOptions,
|
||||
} from "./TablePaginationActions";
|
||||
import { timeAgo } from "../timeutil";
|
||||
import { usePolling } from "../hooks";
|
||||
import { DeadTaskExtended } from "../reducers/tasksReducer";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
table: {
|
||||
@ -53,7 +56,7 @@ function mapStateToProps(state: AppState) {
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = { listDeadTasksAsync };
|
||||
const mapDispatchToProps = { listDeadTasksAsync, deleteDeadTaskAsync };
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
@ -126,7 +129,13 @@ function DeadTasksTable(props: Props & ReduxProps) {
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{props.tasks.map((task) => (
|
||||
<Row key={task.id} task={task} />
|
||||
<Row
|
||||
key={task.id}
|
||||
task={task}
|
||||
onDeleteClick={() => {
|
||||
props.deleteDeadTaskAsync(queue, task.key);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
@ -152,7 +161,12 @@ function DeadTasksTable(props: Props & ReduxProps) {
|
||||
);
|
||||
}
|
||||
|
||||
function Row(props: { task: DeadTask }) {
|
||||
interface RowProps {
|
||||
task: DeadTaskExtended;
|
||||
onDeleteClick: () => void;
|
||||
}
|
||||
|
||||
function Row(props: RowProps) {
|
||||
const { task } = props;
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const classes = useRowStyles();
|
||||
@ -175,7 +189,9 @@ function Row(props: { task: DeadTask }) {
|
||||
<TableCell>{timeAgo(task.last_failed_at)}</TableCell>
|
||||
<TableCell>{task.error_message}</TableCell>
|
||||
<TableCell>
|
||||
<Button>Cancel</Button>
|
||||
<Button onClick={props.onDeleteClick} disabled={task.requestPending}>
|
||||
Delete
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
|
@ -163,7 +163,12 @@ const useRowStyles = makeStyles({
|
||||
},
|
||||
});
|
||||
|
||||
function Row(props: { task: RetryTaskExtended; onDeleteClick: () => void }) {
|
||||
interface RowProps {
|
||||
task: RetryTaskExtended;
|
||||
onDeleteClick: () => void;
|
||||
}
|
||||
|
||||
function Row(props: RowProps) {
|
||||
const { task } = props;
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const classes = useRowStyles();
|
||||
|
@ -21,15 +21,18 @@ import Alert from "@material-ui/lab/Alert";
|
||||
import AlertTitle from "@material-ui/lab/AlertTitle";
|
||||
import SyntaxHighlighter from "react-syntax-highlighter";
|
||||
import syntaxHighlightStyle from "react-syntax-highlighter/dist/esm/styles/hljs/github";
|
||||
import { listScheduledTasksAsync } from "../actions/tasksActions";
|
||||
import {
|
||||
listScheduledTasksAsync,
|
||||
deleteScheduledTaskAsync,
|
||||
} from "../actions/tasksActions";
|
||||
import { AppState } from "../store";
|
||||
import { ScheduledTask } from "../api";
|
||||
import TablePaginationActions, {
|
||||
defaultPageSize,
|
||||
rowsPerPageOptions,
|
||||
} from "./TablePaginationActions";
|
||||
import { durationBefore } from "../timeutil";
|
||||
import { usePolling } from "../hooks";
|
||||
import { ScheduledTaskExtended } from "../reducers/tasksReducer";
|
||||
|
||||
const useStyles = makeStyles({
|
||||
table: {
|
||||
@ -45,7 +48,10 @@ function mapStateToProps(state: AppState) {
|
||||
};
|
||||
}
|
||||
|
||||
const mapDispatchToProps = { listScheduledTasksAsync };
|
||||
const mapDispatchToProps = {
|
||||
listScheduledTasksAsync,
|
||||
deleteScheduledTaskAsync,
|
||||
};
|
||||
|
||||
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||
|
||||
@ -117,7 +123,13 @@ function ScheduledTasksTable(props: Props & ReduxProps) {
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{props.tasks.map((task) => (
|
||||
<Row key={task.id} task={task} />
|
||||
<Row
|
||||
key={task.id}
|
||||
task={task}
|
||||
onDeleteClick={() => {
|
||||
props.deleteScheduledTaskAsync(queue, task.key);
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</TableBody>
|
||||
<TableFooter>
|
||||
@ -151,7 +163,12 @@ const useRowStyles = makeStyles({
|
||||
},
|
||||
});
|
||||
|
||||
function Row(props: { task: ScheduledTask }) {
|
||||
interface RowProps {
|
||||
task: ScheduledTaskExtended;
|
||||
onDeleteClick: () => void;
|
||||
}
|
||||
|
||||
function Row(props: RowProps) {
|
||||
const { task } = props;
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const classes = useRowStyles();
|
||||
@ -173,7 +190,9 @@ function Row(props: { task: ScheduledTask }) {
|
||||
<TableCell>{task.type}</TableCell>
|
||||
<TableCell>{durationBefore(task.next_process_at)}</TableCell>
|
||||
<TableCell>
|
||||
<Button>Cancel</Button>
|
||||
<Button onClick={props.onDeleteClick} disabled={task.requestPending}>
|
||||
Delete
|
||||
</Button>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
<TableRow>
|
||||
|
@ -14,7 +14,9 @@ import {
|
||||
DELETE_QUEUE_SUCCESS,
|
||||
} from "../actions/queuesActions";
|
||||
import {
|
||||
DELETE_DEAD_TASK_SUCCESS,
|
||||
DELETE_RETRY_TASK_SUCCESS,
|
||||
DELETE_SCHEDULED_TASK_SUCCESS,
|
||||
LIST_ACTIVE_TASKS_SUCCESS,
|
||||
LIST_DEAD_TASKS_SUCCESS,
|
||||
LIST_PENDING_TASKS_SUCCESS,
|
||||
@ -148,6 +150,22 @@ function queuesReducer(
|
||||
return { ...state, data: newData };
|
||||
}
|
||||
|
||||
case DELETE_SCHEDULED_TASK_SUCCESS: {
|
||||
const newData = state.data.map((queueInfo) => {
|
||||
if (queueInfo.name !== action.queue) {
|
||||
return queueInfo;
|
||||
}
|
||||
return {
|
||||
...queueInfo,
|
||||
currentStats: {
|
||||
...queueInfo.currentStats,
|
||||
scheduled: queueInfo.currentStats.scheduled - 1,
|
||||
},
|
||||
};
|
||||
});
|
||||
return { ...state, data: newData };
|
||||
}
|
||||
|
||||
case DELETE_RETRY_TASK_SUCCESS: {
|
||||
const newData = state.data.map((queueInfo) => {
|
||||
if (queueInfo.name !== action.queue) {
|
||||
@ -164,6 +182,22 @@ function queuesReducer(
|
||||
return { ...state, data: newData };
|
||||
}
|
||||
|
||||
case DELETE_DEAD_TASK_SUCCESS: {
|
||||
const newData = state.data.map((queueInfo) => {
|
||||
if (queueInfo.name !== action.queue) {
|
||||
return queueInfo;
|
||||
}
|
||||
return {
|
||||
...queueInfo,
|
||||
currentStats: {
|
||||
...queueInfo.currentStats,
|
||||
dead: queueInfo.currentStats.dead - 1,
|
||||
},
|
||||
};
|
||||
});
|
||||
return { ...state, data: newData };
|
||||
}
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
@ -21,6 +21,12 @@ import {
|
||||
DELETE_RETRY_TASK_BEGIN,
|
||||
DELETE_RETRY_TASK_SUCCESS,
|
||||
DELETE_RETRY_TASK_ERROR,
|
||||
DELETE_SCHEDULED_TASK_BEGIN,
|
||||
DELETE_SCHEDULED_TASK_SUCCESS,
|
||||
DELETE_SCHEDULED_TASK_ERROR,
|
||||
DELETE_DEAD_TASK_BEGIN,
|
||||
DELETE_DEAD_TASK_SUCCESS,
|
||||
DELETE_DEAD_TASK_ERROR,
|
||||
} from "../actions/tasksActions";
|
||||
import {
|
||||
ActiveTask,
|
||||
@ -40,12 +46,24 @@ export interface ActiveTaskExtended extends ActiveTask {
|
||||
canceling: boolean;
|
||||
}
|
||||
|
||||
export interface ScheduledTaskExtended extends ScheduledTask {
|
||||
// Indicates that a request has been sent for this
|
||||
// task and awaiting for a response.
|
||||
requestPending: boolean;
|
||||
}
|
||||
|
||||
export interface RetryTaskExtended extends RetryTask {
|
||||
// Indicates that a request has been sent for this
|
||||
// task and awaiting for a response.
|
||||
requestPending: boolean;
|
||||
}
|
||||
|
||||
export interface DeadTaskExtended extends DeadTask {
|
||||
// Indicates that a request has been sent for this
|
||||
// task and awaiting for a response.
|
||||
requestPending: boolean;
|
||||
}
|
||||
|
||||
interface TasksState {
|
||||
activeTasks: {
|
||||
loading: boolean;
|
||||
@ -60,7 +78,7 @@ interface TasksState {
|
||||
scheduledTasks: {
|
||||
loading: boolean;
|
||||
error: string;
|
||||
data: ScheduledTask[];
|
||||
data: ScheduledTaskExtended[];
|
||||
};
|
||||
retryTasks: {
|
||||
loading: boolean;
|
||||
@ -70,7 +88,7 @@ interface TasksState {
|
||||
deadTasks: {
|
||||
loading: boolean;
|
||||
error: string;
|
||||
data: DeadTask[];
|
||||
data: DeadTaskExtended[];
|
||||
};
|
||||
}
|
||||
|
||||
@ -187,7 +205,10 @@ function tasksReducer(
|
||||
scheduledTasks: {
|
||||
loading: false,
|
||||
error: "",
|
||||
data: action.payload.tasks,
|
||||
data: action.payload.tasks.map((task) => ({
|
||||
...task,
|
||||
requestPending: false,
|
||||
})),
|
||||
},
|
||||
};
|
||||
|
||||
@ -250,7 +271,10 @@ function tasksReducer(
|
||||
deadTasks: {
|
||||
loading: false,
|
||||
error: "",
|
||||
data: action.payload.tasks,
|
||||
data: action.payload.tasks.map((task) => ({
|
||||
...task,
|
||||
requestPending: false,
|
||||
})),
|
||||
},
|
||||
};
|
||||
|
||||
@ -311,6 +335,45 @@ function tasksReducer(
|
||||
},
|
||||
};
|
||||
|
||||
case DELETE_SCHEDULED_TASK_BEGIN:
|
||||
return {
|
||||
...state,
|
||||
scheduledTasks: {
|
||||
...state.scheduledTasks,
|
||||
data: state.scheduledTasks.data.map((task) => {
|
||||
if (task.key !== action.taskKey) {
|
||||
return task;
|
||||
}
|
||||
return { ...task, requestPending: true };
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
case DELETE_SCHEDULED_TASK_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
scheduledTasks: {
|
||||
...state.scheduledTasks,
|
||||
data: state.scheduledTasks.data.filter(
|
||||
(task) => task.key !== action.taskKey
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
case DELETE_SCHEDULED_TASK_ERROR:
|
||||
return {
|
||||
...state,
|
||||
scheduledTasks: {
|
||||
...state.scheduledTasks,
|
||||
data: state.scheduledTasks.data.map((task) => {
|
||||
if (task.key !== action.taskKey) {
|
||||
return task;
|
||||
}
|
||||
return { ...task, requestPending: false };
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
case DELETE_RETRY_TASK_BEGIN:
|
||||
return {
|
||||
...state,
|
||||
@ -350,6 +413,45 @@ function tasksReducer(
|
||||
},
|
||||
};
|
||||
|
||||
case DELETE_DEAD_TASK_BEGIN:
|
||||
return {
|
||||
...state,
|
||||
deadTasks: {
|
||||
...state.deadTasks,
|
||||
data: state.deadTasks.data.map((task) => {
|
||||
if (task.key !== action.taskKey) {
|
||||
return task;
|
||||
}
|
||||
return { ...task, requestPending: true };
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
case DELETE_DEAD_TASK_SUCCESS:
|
||||
return {
|
||||
...state,
|
||||
deadTasks: {
|
||||
...state.deadTasks,
|
||||
data: state.deadTasks.data.filter(
|
||||
(task) => task.key !== action.taskKey
|
||||
),
|
||||
},
|
||||
};
|
||||
|
||||
case DELETE_DEAD_TASK_ERROR:
|
||||
return {
|
||||
...state,
|
||||
deadTasks: {
|
||||
...state.deadTasks,
|
||||
data: state.deadTasks.data.map((task) => {
|
||||
if (task.key !== action.taskKey) {
|
||||
return task;
|
||||
}
|
||||
return { ...task, requestPending: false };
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user