Add cancel action to ActiveTasksTable

This commit is contained in:
Ken Hibino 2020-12-23 06:59:44 -08:00
parent cbfc1af9c0
commit 50639cabb8
7 changed files with 274 additions and 8 deletions

View File

@ -78,8 +78,7 @@ func main() {
// Task endpoints.
api.HandleFunc("/queues/{qname}/active_tasks", newListActiveTasksHandlerFunc(inspector)).Methods("GET")
api.HandleFunc("/queues/{qname}/active_tasks/{task_id}:cancel", newCancelActiveTaskHandlerFunc(inspector)).Methods("POST")
api.HandleFunc("/queues/{qname}/active_tasks/:cancel_all", newCancelAllActiveTasksHandlerFunc(inspector)).Methods("POST")
api.HandleFunc("/queues/{qname}/active_tasks:batch_cancel", newBatchCancelActiveTasksHandlerFunc(inspector)).Methods("POST")
api.HandleFunc("/queues/{qname}/active_tasks:cancel_all", newCancelAllActiveTasksHandlerFunc(inspector)).Methods("POST")
api.HandleFunc("/queues/{qname}/active_tasks:batch_cancel", newBatchCancelActiveTasksHandlerFunc(inspector)).Methods("POST")
api.HandleFunc("/queues/{qname}/pending_tasks", newListPendingTasksHandlerFunc(inspector)).Methods("GET")
api.HandleFunc("/queues/{qname}/scheduled_tasks", newListScheduledTasksHandlerFunc(inspector)).Methods("GET")

View File

@ -1,4 +1,6 @@
import {
batchCancelActiveTasks,
BatchCancelTasksResponse,
batchDeleteDeadTasks,
batchDeleteRetryTasks,
batchDeleteScheduledTasks,
@ -11,6 +13,7 @@ import {
batchRunScheduledTasks,
BatchRunTasksResponse,
cancelActiveTask,
cancelAllActiveTasks,
deleteAllDeadTasks,
deleteAllRetryTasks,
deleteAllScheduledTasks,
@ -60,6 +63,16 @@ 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 CANCEL_ALL_ACTIVE_TASKS_BEGIN = "CANCEL_ALL_ACTIVE_TASKS_BEGIN";
export const CANCEL_ALL_ACTIVE_TASKS_SUCCESS =
"CANCEL_ALL_ACTIVE_TASKS_SUCCESS";
export const CANCEL_ALL_ACTIVE_TASKS_ERROR = "CANCEL_ALL_ACTIVE_TASKS_ERROR";
export const BATCH_CANCEL_ACTIVE_TASKS_BEGIN =
"BATCH_CANCEL_ACTIVE_TASKS_BEGIN";
export const BATCH_CANCEL_ACTIVE_TASKS_SUCCESS =
"BATCH_CANCEL_ACTIVE_TASKS_SUCCESS";
export const BATCH_CANCEL_ACTIVE_TASKS_ERROR =
"BATCH_CANCEL_ACTIVE_TASKS_ERROR";
export const RUN_SCHEDULED_TASK_BEGIN = "RUN_DEAD_TASK_BEGIN";
export const RUN_SCHEDULED_TASK_SUCCESS = "RUN_DEAD_TASK_SUCCESS";
export const RUN_SCHEDULED_TASK_ERROR = "RUN_DEAD_TASK_ERROR";
@ -253,6 +266,41 @@ interface CancelActiveTaskErrorAction {
error: string;
}
interface CancelAllActiveTasksBeginAction {
type: typeof CANCEL_ALL_ACTIVE_TASKS_BEGIN;
queue: string;
}
interface CancelAllActiveTasksSuccessAction {
type: typeof CANCEL_ALL_ACTIVE_TASKS_SUCCESS;
queue: string;
}
interface CancelAllActiveTasksErrorAction {
type: typeof CANCEL_ALL_ACTIVE_TASKS_ERROR;
queue: string;
error: string;
}
interface BatchCancelActiveTasksBeginAction {
type: typeof BATCH_CANCEL_ACTIVE_TASKS_BEGIN;
queue: string;
taskIds: string[];
}
interface BatchCancelActiveTasksSuccessAction {
type: typeof BATCH_CANCEL_ACTIVE_TASKS_SUCCESS;
queue: string;
payload: BatchCancelTasksResponse;
}
interface BatchCancelActiveTasksErrorAction {
type: typeof BATCH_CANCEL_ACTIVE_TASKS_ERROR;
queue: string;
taskIds: string[];
error: string;
}
interface RunScheduledTaskBeginAction {
type: typeof RUN_SCHEDULED_TASK_BEGIN;
queue: string;
@ -705,6 +753,12 @@ export type TasksActionTypes =
| CancelActiveTaskBeginAction
| CancelActiveTaskSuccessAction
| CancelActiveTaskErrorAction
| CancelAllActiveTasksBeginAction
| CancelAllActiveTasksSuccessAction
| CancelAllActiveTasksErrorAction
| BatchCancelActiveTasksBeginAction
| BatchCancelActiveTasksSuccessAction
| BatchCancelActiveTasksErrorAction
| RunScheduledTaskBeginAction
| RunScheduledTaskSuccessAction
| RunScheduledTaskErrorAction
@ -910,6 +964,45 @@ export function cancelActiveTaskAsync(queue: string, taskId: string) {
};
}
export function cancelAllActiveTasksAsync(queue: string) {
return async (dispatch: Dispatch<TasksActionTypes>) => {
dispatch({ type: CANCEL_ALL_ACTIVE_TASKS_BEGIN, queue });
try {
await cancelAllActiveTasks(queue);
dispatch({ type: CANCEL_ALL_ACTIVE_TASKS_SUCCESS, queue });
} catch (error) {
console.error("cancelAllActiveTasksAsync: ", error);
dispatch({
type: CANCEL_ALL_ACTIVE_TASKS_ERROR,
error: "Could not cancel all tasks",
queue,
});
}
};
}
export function batchCancelActiveTasksAsync(queue: string, taskIds: string[]) {
return async (dispatch: Dispatch<TasksActionTypes>) => {
dispatch({ type: BATCH_CANCEL_ACTIVE_TASKS_BEGIN, queue, taskIds });
try {
const response = await batchCancelActiveTasks(queue, taskIds);
dispatch({
type: BATCH_CANCEL_ACTIVE_TASKS_SUCCESS,
queue: queue,
payload: response,
});
} catch (error) {
console.error("batchCancelActiveTasksAsync: ", error);
dispatch({
type: BATCH_CANCEL_ACTIVE_TASKS_ERROR,
error: `Could not batch cancel tasks: ${taskIds}`,
queue,
taskIds,
});
}
};
}
export function runScheduledTaskAsync(queue: string, taskKey: string) {
return async (dispatch: Dispatch<TasksActionTypes>) => {
dispatch({ type: RUN_SCHEDULED_TASK_BEGIN, queue, taskKey });

View File

@ -41,6 +41,11 @@ export interface ListSchedulerEntriesResponse {
entries: SchedulerEntry[];
}
export interface BatchCancelTasksResponse {
canceled_ids: string[];
error_ids: string[];
}
export interface BatchDeleteTasksResponse {
deleted_keys: string[];
failed_keys: string[];
@ -198,6 +203,27 @@ export async function cancelActiveTask(
});
}
export async function cancelAllActiveTasks(qname: string): Promise<void> {
await axios({
method: "post",
url: `${BASE_URL}/queues/${qname}/active_tasks:cancel_all`,
});
}
export async function batchCancelActiveTasks(
qname: string,
taskIds: string[]
): Promise<BatchCancelTasksResponse> {
const resp = await axios({
method: "post",
url: `${BASE_URL}/queues/${qname}/active_tasks:batch_cancel`,
data: {
task_ids: taskIds,
},
});
return resp.data;
}
export async function listPendingTasks(
qname: string,
pageOpts?: PaginationOptions

View File

@ -26,6 +26,8 @@ import syntaxHighlightStyle from "react-syntax-highlighter/dist/esm/styles/hljs/
import {
listActiveTasksAsync,
cancelActiveTaskAsync,
batchCancelActiveTasksAsync,
cancelAllActiveTasksAsync,
} from "../actions/tasksActions";
import { AppState } from "../store";
import TablePaginationActions, {
@ -47,11 +49,18 @@ function mapStateToProps(state: AppState) {
return {
loading: state.tasks.activeTasks.loading,
tasks: state.tasks.activeTasks.data,
batchActionPending: state.tasks.activeTasks.batchActionPending,
allActionPending: state.tasks.activeTasks.allActionPending,
pollInterval: state.settings.pollInterval,
};
}
const mapDispatchToProps = { listActiveTasksAsync, cancelActiveTaskAsync };
const mapDispatchToProps = {
listActiveTasksAsync,
cancelActiveTaskAsync,
batchCancelActiveTasksAsync,
cancelAllActiveTasksAsync,
};
const connector = connect(mapStateToProps, mapDispatchToProps);
@ -91,6 +100,16 @@ function ActiveTasksTable(props: Props & ReduxProps) {
}
};
const handleCancelAllClick = () => {
props.cancelAllActiveTasksAsync(queue);
};
const handleBatchCancelClick = () => {
props
.batchCancelActiveTasksAsync(queue, selectedIds)
.then(() => setSelectedIds([]));
};
const fetchData = useCallback(() => {
const pageOpts = { page: page + 1, size: pageSize };
listActiveTasksAsync(queue, pageOpts);
@ -124,15 +143,15 @@ function ActiveTasksTable(props: Props & ReduxProps) {
{
tooltip: "Cancel",
icon: <CancelIcon />,
onClick: () => console.log("TODO"),
disabled: false,
onClick: handleBatchCancelClick,
disabled: props.batchActionPending,
},
]}
menuItemActions={[
{
label: "Cancel All",
onClick: () => console.log("TODO"),
disabled: false,
onClick: handleCancelAllClick,
disabled: props.allActionPending,
},
]}
/>

View File

@ -70,7 +70,10 @@ export default function TableActions(props: Props) {
{props.menuItemActions.map((action) => (
<MenuItem
key={action.label}
onClick={action.onClick}
onClick={() => {
action.onClick();
closeMenu();
}}
disabled={action.disabled}
>
{action.label}

View File

@ -3,6 +3,7 @@ import {
SnackbarActionTypes,
} from "../actions/snackbarActions";
import {
BATCH_CANCEL_ACTIVE_TASKS_SUCCESS,
BATCH_DELETE_DEAD_TASKS_SUCCESS,
BATCH_DELETE_RETRY_TASKS_SUCCESS,
BATCH_DELETE_SCHEDULED_TASKS_SUCCESS,
@ -11,6 +12,7 @@ import {
BATCH_RUN_DEAD_TASKS_SUCCESS,
BATCH_RUN_RETRY_TASKS_SUCCESS,
BATCH_RUN_SCHEDULED_TASKS_SUCCESS,
CANCEL_ALL_ACTIVE_TASKS_SUCCESS,
DELETE_ALL_DEAD_TASKS_SUCCESS,
DELETE_ALL_RETRY_TASKS_SUCCESS,
DELETE_ALL_SCHEDULED_TASKS_SUCCESS,
@ -53,6 +55,22 @@ function snackbarReducer(
isOpen: false,
};
case BATCH_CANCEL_ACTIVE_TASKS_SUCCESS: {
const n = action.payload.canceled_ids.length;
return {
isOpen: true,
message: `Cancelation signal sent to ${n} ${
n === 1 ? "task" : "tasks"
}`,
};
}
case CANCEL_ALL_ACTIVE_TASKS_SUCCESS:
return {
isOpen: true,
message: `Cancelation signal sent to all tasks in ${action.queue} queue`,
};
case RUN_SCHEDULED_TASK_SUCCESS:
return {
isOpen: true,

View File

@ -90,6 +90,12 @@ import {
BATCH_KILL_RETRY_TASKS_SUCCESS,
BATCH_KILL_RETRY_TASKS_BEGIN,
BATCH_KILL_RETRY_TASKS_ERROR,
BATCH_CANCEL_ACTIVE_TASKS_BEGIN,
BATCH_CANCEL_ACTIVE_TASKS_SUCCESS,
BATCH_CANCEL_ACTIVE_TASKS_ERROR,
CANCEL_ALL_ACTIVE_TASKS_BEGIN,
CANCEL_ALL_ACTIVE_TASKS_SUCCESS,
CANCEL_ALL_ACTIVE_TASKS_ERROR,
} from "../actions/tasksActions";
import {
ActiveTask,
@ -130,6 +136,8 @@ export interface DeadTaskExtended extends DeadTask {
interface TasksState {
activeTasks: {
loading: boolean;
batchActionPending: boolean;
allActionPending: boolean;
error: string;
data: ActiveTaskExtended[];
};
@ -164,6 +172,8 @@ interface TasksState {
const initialState: TasksState = {
activeTasks: {
loading: false,
batchActionPending: false,
allActionPending: false,
error: "",
data: [],
},
@ -214,6 +224,7 @@ function tasksReducer(
return {
...state,
activeTasks: {
...state.activeTasks,
loading: false,
error: "",
data: action.payload.tasks.map((task) => ({
@ -413,6 +424,103 @@ function tasksReducer(
},
};
case BATCH_CANCEL_ACTIVE_TASKS_BEGIN: {
const newData = state.activeTasks.data.map((task) => {
if (!action.taskIds.includes(task.id)) {
return task;
}
return { ...task, requestPending: true };
});
return {
...state,
activeTasks: {
...state.activeTasks,
batchActionPending: true,
data: newData,
},
};
}
case BATCH_CANCEL_ACTIVE_TASKS_SUCCESS: {
const newData = state.activeTasks.data.map((task) => {
if (action.payload.canceled_ids.includes(task.id)) {
return { ...task, canceling: true, requestPending: false };
}
if (action.payload.error_ids.includes(task.id)) {
return { ...task, requestPending: false };
}
return task;
});
return {
...state,
activeTasks: {
...state.activeTasks,
batchActionPending: false,
data: newData,
},
};
}
case BATCH_CANCEL_ACTIVE_TASKS_ERROR: {
const newData = state.activeTasks.data.map((task) => {
return { ...task, requestPending: false };
});
return {
...state,
activeTasks: {
...state.activeTasks,
batchActionPending: false,
data: newData,
},
};
}
case CANCEL_ALL_ACTIVE_TASKS_BEGIN: {
const newData = state.activeTasks.data.map((task) => ({
...task,
requestPending: true,
}));
return {
...state,
activeTasks: {
...state.activeTasks,
allActionPending: true,
data: newData,
},
};
}
case CANCEL_ALL_ACTIVE_TASKS_SUCCESS: {
const newData = state.activeTasks.data.map((task) => ({
...task,
requestPending: false,
canceling: true,
}));
return {
...state,
activeTasks: {
...state.activeTasks,
allActionPending: false,
data: newData,
},
};
}
case CANCEL_ALL_ACTIVE_TASKS_ERROR: {
const newData = state.activeTasks.data.map((task) => ({
...task,
requestPending: false,
}));
return {
...state,
activeTasks: {
...state.activeTasks,
allActionPending: false,
data: newData,
},
};
}
case RUN_SCHEDULED_TASK_BEGIN:
case KILL_SCHEDULED_TASK_BEGIN:
case DELETE_SCHEDULED_TASK_BEGIN: