Fetch servers info and show in ServersTable

This commit is contained in:
Ken Hibino 2020-12-31 07:02:54 -08:00
parent d78abcf584
commit 3f9e8820d9
6 changed files with 178 additions and 5 deletions

View File

@ -0,0 +1,44 @@
import { Dispatch } from "redux";
import { listServers, ListServersResponse } from "../api";
// List of server related action types.
export const LIST_SERVERS_BEGIN = "LIST_SERVERS_BEGIN";
export const LIST_SERVERS_SUCCESS = "LIST_SERVERS_SUCCESS";
export const LIST_SERVERS_ERROR = "LIST_SERVERS_ERROR";
interface ListServersBeginAction {
type: typeof LIST_SERVERS_BEGIN;
}
interface ListServersSuccessAction {
type: typeof LIST_SERVERS_SUCCESS;
payload: ListServersResponse;
}
interface ListServersErrorAction {
type: typeof LIST_SERVERS_ERROR;
error: string; // error description
}
// Union of all server related actions.
export type ServersActionTypes =
| ListServersBeginAction
| ListServersSuccessAction
| ListServersErrorAction;
export function listServersAsync() {
return async (dispatch: Dispatch<ServersActionTypes>) => {
dispatch({ type: LIST_SERVERS_BEGIN });
try {
const response = await listServers();
dispatch({
type: LIST_SERVERS_SUCCESS,
payload: response,
});
} catch (error) {
console.error("listServersAsync: ", error);
dispatch({
type: LIST_SERVERS_ERROR,
error: "Could not retrieve servers info",
});
}
};
}

View File

@ -32,6 +32,10 @@ export interface ListDeadTasksResponse {
stats: Queue;
}
export interface ListServersResponse {
servers: ServerInfo[];
}
export interface ListSchedulerEntriesResponse {
entries: SchedulerEntry[];
}
@ -129,7 +133,20 @@ export interface DeadTask extends BaseTask {
}
export interface ServerInfo {
// TODO: fill this out
id: string;
host: string;
pid: number;
concurrency: number;
queue_priorities: { [qname: string]: number };
strict_priority_enabled: boolean;
start_time: string;
status: string;
active_workers: WorkerInfo[];
}
export interface WorkerInfo {
task: ActiveTask;
start_time: string;
}
export interface SchedulerEntry {
@ -545,6 +562,14 @@ export async function runAllDeadTasks(qname: string): Promise<void> {
});
}
export async function listServers(): Promise<ListServersResponse> {
const resp = await axios({
method: "get",
url: `${BASE_URL}/servers`,
});
return resp.data;
}
export async function listSchedulerEntries(): Promise<ListSchedulerEntriesResponse> {
const resp = await axios({
method: "get",

View File

@ -1,7 +1,6 @@
import React, { useState } from "react";
import clsx from "clsx";
import { makeStyles } from "@material-ui/core/styles";
import IconButton from "@material-ui/core/IconButton";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
@ -13,6 +12,7 @@ import Alert from "@material-ui/lab/Alert";
import AlertTitle from "@material-ui/lab/AlertTitle";
import { ServerInfo } from "../api";
import { SortDirection, SortableTableColumn } from "../types/table";
import { timeAgo } from "../utils";
const useStyles = makeStyles((theme) => ({
table: {
@ -140,8 +140,43 @@ export default function ServersTable(props: Props) {
))}
</TableRow>
</TableHead>
<TableBody></TableBody>
<TableBody>
{sortServerInfos(props.servers, cmpFunc).map((srv) => (
<Row key={srv.id} server={srv} />
))}
</TableBody>
</Table>
</TableContainer>
);
}
interface RowProps {
server: ServerInfo;
}
const useRowStyles = makeStyles((theme) => ({
rowRoot: {
"& > *": {
borderBottom: "unset",
},
},
noBorder: {
border: "none",
},
}));
function Row(props: RowProps) {
const classes = useRowStyles();
const { server } = props;
return (
<TableRow className={classes.rowRoot}>
<TableCell>{server.host}</TableCell>
<TableCell>{server.pid}</TableCell>
<TableCell>{server.status}</TableCell>
<TableCell>
{server.active_workers.length}/{server.concurrency}
</TableCell>
<TableCell>{JSON.stringify(server.queue_priorities)}</TableCell>
<TableCell>{timeAgo(server.start_time)}</TableCell>
</TableRow>
);
}

View File

@ -0,0 +1,45 @@
import {
LIST_SERVERS_BEGIN,
LIST_SERVERS_ERROR,
LIST_SERVERS_SUCCESS,
ServersActionTypes,
} from "../actions/serversActions";
import { ServerInfo } from "../api";
interface ServersState {
loading: boolean;
data: ServerInfo[];
}
const initialState: ServersState = {
loading: false,
data: [],
};
export default function serversReducer(
state = initialState,
action: ServersActionTypes
): ServersState {
switch (action.type) {
case LIST_SERVERS_BEGIN:
return {
...state,
loading: true,
};
case LIST_SERVERS_SUCCESS:
return {
loading: false,
data: action.payload.servers,
};
case LIST_SERVERS_ERROR:
return {
...state,
loading: false,
};
default:
return state;
}
}

View File

@ -2,6 +2,7 @@ import { combineReducers, configureStore } from "@reduxjs/toolkit";
import settingsReducer from "./reducers/settingsReducer";
import queuesReducer from "./reducers/queuesReducer";
import tasksReducer from "./reducers/tasksReducer";
import serversReducer from "./reducers/serversReducer";
import schedulerEntriesReducer from "./reducers/schedulerEntriesReducer";
import snackbarReducer from "./reducers/snackbarReducer";
import queueStatsReducer from "./reducers/queueStatsReducer";
@ -10,6 +11,7 @@ const rootReducer = combineReducers({
settings: settingsReducer,
queues: queuesReducer,
tasks: tasksReducer,
servers: serversReducer,
schedulerEntries: schedulerEntriesReducer,
snackbar: snackbarReducer,
queueStats: queueStatsReducer,

View File

@ -1,10 +1,14 @@
import React from "react";
import { connect, ConnectedProps } from "react-redux";
import Container from "@material-ui/core/Container";
import { makeStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/Paper";
import Typography from "@material-ui/core/Typography";
import ServersTable from "../components/ServersTable";
import { listServersAsync } from "../actions/serversActions";
import { AppState } from "../store";
import { usePolling } from "../hooks";
const useStyles = makeStyles((theme) => ({
container: {
@ -23,8 +27,24 @@ const useStyles = makeStyles((theme) => ({
},
}));
export default function ServersView() {
function mapStateToProps(state: AppState) {
return {
loading: state.servers.loading,
servers: state.servers.data,
pollInterval: state.settings.pollInterval,
};
}
const connector = connect(mapStateToProps, { listServersAsync });
type Props = ConnectedProps<typeof connector>;
function ServersView(props: Props) {
const { pollInterval, listServersAsync } = props;
const classes = useStyles();
usePolling(listServersAsync, pollInterval);
return (
<Container maxWidth="lg" className={classes.container}>
<Grid container spacing={3}>
@ -33,10 +53,12 @@ export default function ServersView() {
<Typography variant="h6" className={classes.heading}>
Servers
</Typography>
<ServersTable servers={[]} />
<ServersTable servers={props.servers} />
</Paper>
</Grid>
</Grid>
</Container>
);
}
export default connector(ServersView);