diff --git a/ui/src/actions/queuesActions.ts b/ui/src/actions/queuesActions.ts index 082c94a..4ecd769 100644 --- a/ui/src/actions/queuesActions.ts +++ b/ui/src/actions/queuesActions.ts @@ -1,3 +1,4 @@ +import { Dispatch } from "redux"; import { deleteQueue, listQueues, @@ -5,11 +6,12 @@ import { pauseQueue, resumeQueue, } from "../api"; -import { Dispatch } from "redux"; +import { toErrorString } from "../utils"; // List of queue related action types. export const LIST_QUEUES_BEGIN = "LIST_QUEUES_BEGIN"; export const LIST_QUEUES_SUCCESS = "LIST_QUEUES_SUCCESS"; +export const LIST_QUEUES_ERROR = "LIST_QUEUES_ERROR"; export const DELETE_QUEUE_BEGIN = "DELETE_QUEUE_BEGIN"; export const DELETE_QUEUE_SUCCESS = "DELETE_QUEUE_SUCCESS"; export const DELETE_QUEUE_ERROR = "DELETE_QUEUE_ERROR"; @@ -29,6 +31,11 @@ interface ListQueuesSuccessAction { payload: ListQueuesResponse; } +interface ListQueuesErrorAction { + type: typeof LIST_QUEUES_ERROR; + error: string; +} + interface DeleteQueueBeginAction { type: typeof DELETE_QUEUE_BEGIN; queue: string; // name of the queue @@ -81,6 +88,7 @@ interface ResumeQueueErrorAction { export type QueuesActionTypes = | ListQueuesBeginAction | ListQueuesSuccessAction + | ListQueuesErrorAction | DeleteQueueBeginAction | DeleteQueueSuccessAction | DeleteQueueErrorAction @@ -94,12 +102,19 @@ export type QueuesActionTypes = export function listQueuesAsync() { return async (dispatch: Dispatch) => { dispatch({ type: LIST_QUEUES_BEGIN }); - // TODO: try/catch and dispatch error action on failure - const response = await listQueues(); - dispatch({ - type: LIST_QUEUES_SUCCESS, - payload: response, - }); + try { + const response = await listQueues(); + dispatch({ + type: LIST_QUEUES_SUCCESS, + payload: response, + }); + } catch (error) { + console.error(`listQueuesAsync: ${toErrorString(error)}`); + dispatch({ + type: LIST_QUEUES_ERROR, + error: error.response.data, + }); + } }; } diff --git a/ui/src/actions/redisInfoActions.ts b/ui/src/actions/redisInfoActions.ts index 09215d3..eef37fc 100644 --- a/ui/src/actions/redisInfoActions.ts +++ b/ui/src/actions/redisInfoActions.ts @@ -1,5 +1,6 @@ import { Dispatch } from "redux"; import { getRedisInfo, RedisInfoResponse } from "../api"; +import { toErrorString } from "../utils"; // List of redis-info related action types. export const GET_REDIS_INFO_BEGIN = "GET_REDIS_INFO_BEGIN"; @@ -33,10 +34,10 @@ export function getRedisInfoAsync() { const response = await getRedisInfo(); dispatch({ type: GET_REDIS_INFO_SUCCESS, payload: response }); } catch (error) { - console.error("getRedisInfoAsync: ", error); + console.error(`getRedisInfoAsync: ${toErrorString(error)}`); dispatch({ - type: GET_REDIS_INFO_BEGIN, - error: "Could not fetch redis info", + type: GET_REDIS_INFO_ERROR, + error: error.response.data, }); } }; diff --git a/ui/src/actions/schedulerEntriesActions.ts b/ui/src/actions/schedulerEntriesActions.ts index 4df0132..cac68be 100644 --- a/ui/src/actions/schedulerEntriesActions.ts +++ b/ui/src/actions/schedulerEntriesActions.ts @@ -5,6 +5,7 @@ import { listSchedulerEntries, ListSchedulerEntriesResponse, } from "../api"; +import { toErrorString } from "../utils"; // List of scheduler-entry related action types. export const LIST_SCHEDULER_ENTRIES_BEGIN = "LIST_SCHEDULER_ENTRIES_BEGIN"; @@ -67,10 +68,10 @@ export function listSchedulerEntriesAsync() { payload: response, }); } catch (error) { - console.error(error); + console.error(`listSchedulerEnqueueEventsAsync: ${toErrorString(error)}`); dispatch({ type: LIST_SCHEDULER_ENTRIES_ERROR, - error: "Could not retrieve scheduler entries", + error: error.response.data, }); } }; diff --git a/ui/src/actions/serversActions.ts b/ui/src/actions/serversActions.ts index 633082a..2311f4e 100644 --- a/ui/src/actions/serversActions.ts +++ b/ui/src/actions/serversActions.ts @@ -1,5 +1,6 @@ import { Dispatch } from "redux"; import { listServers, ListServersResponse } from "../api"; +import { toErrorString } from "../utils"; // List of server related action types. export const LIST_SERVERS_BEGIN = "LIST_SERVERS_BEGIN"; @@ -34,10 +35,10 @@ export function listServersAsync() { payload: response, }); } catch (error) { - console.error("listServersAsync: ", error); + console.error(`listServersAsync: ${toErrorString(error)}`); dispatch({ type: LIST_SERVERS_ERROR, - error: "Could not retrieve servers info", + error: error.response.data, }); } }; diff --git a/ui/src/reducers/queuesReducer.ts b/ui/src/reducers/queuesReducer.ts index 36021a8..0eb6fa4 100644 --- a/ui/src/reducers/queuesReducer.ts +++ b/ui/src/reducers/queuesReducer.ts @@ -11,6 +11,7 @@ import { DELETE_QUEUE_BEGIN, DELETE_QUEUE_ERROR, DELETE_QUEUE_SUCCESS, + LIST_QUEUES_ERROR, } from "../actions/queuesActions"; import { BATCH_DELETE_DEAD_TASKS_SUCCESS, @@ -49,6 +50,7 @@ import { Queue } from "../api"; interface QueuesState { loading: boolean; data: QueueInfo[]; + error: string; } export interface QueueInfo { @@ -57,7 +59,7 @@ export interface QueueInfo { requestPending: boolean; // indicates pause/resume/delete action is pending on this queue } -const initialState: QueuesState = { data: [], loading: false }; +const initialState: QueuesState = { data: [], loading: false, error: "" }; function queuesReducer( state = initialState, @@ -72,6 +74,7 @@ function queuesReducer( return { ...state, loading: false, + error: "", data: queues.map((q: Queue) => ({ name: q.queue, currentStats: q, @@ -79,6 +82,13 @@ function queuesReducer( })), }; + case LIST_QUEUES_ERROR: + return { + ...state, + loading: false, + error: action.error, + }; + case DELETE_QUEUE_BEGIN: case PAUSE_QUEUE_BEGIN: case RESUME_QUEUE_BEGIN: { diff --git a/ui/src/reducers/redisInfoReducer.ts b/ui/src/reducers/redisInfoReducer.ts index 69c83da..f995e78 100644 --- a/ui/src/reducers/redisInfoReducer.ts +++ b/ui/src/reducers/redisInfoReducer.ts @@ -8,6 +8,7 @@ import { RedisInfo } from "../api"; interface RedisInfoState { loading: boolean; + error: string; address: string; data: RedisInfo | null; rawData: string | null; @@ -15,6 +16,7 @@ interface RedisInfoState { const initialState: RedisInfoState = { loading: false, + error: "", address: "", data: null, rawData: null, @@ -35,11 +37,13 @@ export default function redisInfoReducer( return { ...state, loading: false, + error: action.error, }; case GET_REDIS_INFO_SUCCESS: return { loading: false, + error: "", address: action.payload.address, data: action.payload.info, rawData: action.payload.raw_info, diff --git a/ui/src/reducers/schedulerEntriesReducer.ts b/ui/src/reducers/schedulerEntriesReducer.ts index 0eaf39d..aa9c34a 100644 --- a/ui/src/reducers/schedulerEntriesReducer.ts +++ b/ui/src/reducers/schedulerEntriesReducer.ts @@ -51,7 +51,6 @@ function schedulerEntriesReducer( data: action.payload.entries, }; case LIST_SCHEDULER_ENTRIES_ERROR: - // TODO: set error state return { ...state, loading: false, diff --git a/ui/src/reducers/serversReducer.ts b/ui/src/reducers/serversReducer.ts index d0cce82..d8ee44a 100644 --- a/ui/src/reducers/serversReducer.ts +++ b/ui/src/reducers/serversReducer.ts @@ -8,11 +8,13 @@ import { ServerInfo } from "../api"; interface ServersState { loading: boolean; + error: string; data: ServerInfo[]; } const initialState: ServersState = { loading: false, + error: "", data: [], }; @@ -30,12 +32,14 @@ export default function serversReducer( case LIST_SERVERS_SUCCESS: return { loading: false, + error: "", data: action.payload.servers, }; case LIST_SERVERS_ERROR: return { ...state, + error: action.error, loading: false, }; diff --git a/ui/src/utils.ts b/ui/src/utils.ts index e1a4fb9..610bb52 100644 --- a/ui/src/utils.ts +++ b/ui/src/utils.ts @@ -1,3 +1,14 @@ +import { AxiosError } from "axios"; + +// toErrorString returns a string representaion of axios error. +export function toErrorString(error: AxiosError): string { + const { response } = error; + if (!response) { + return "error: no error response data available"; + } + return `${response.status} (${response.statusText}): ${response.data}`; +} + interface Duration { hour: number; minute: number; diff --git a/ui/src/views/DashboardView.tsx b/ui/src/views/DashboardView.tsx index b8ffaca..939c134 100644 --- a/ui/src/views/DashboardView.tsx +++ b/ui/src/views/DashboardView.tsx @@ -6,6 +6,8 @@ import Grid from "@material-ui/core/Grid"; import Paper from "@material-ui/core/Paper"; import Typography from "@material-ui/core/Typography"; import InfoIcon from "@material-ui/icons/Info"; +import Alert from "@material-ui/lab/Alert"; +import AlertTitle from "@material-ui/lab/AlertTitle"; import { listQueuesAsync, pauseQueueAsync, @@ -67,6 +69,7 @@ function mapStateToProps(state: AppState) { ...q.currentStats, requestPending: q.requestPending, })), + error: state.queues.error, pollInterval: state.settings.pollInterval, queueStats: state.queueStats.data, }; @@ -115,6 +118,15 @@ function DashboardView(props: Props) { return ( + {props.error.length > 0 && ( + + + Error + Could not retreive queues live data —{" "} + See the logs for details + + + )}
diff --git a/ui/src/views/RedisInfoView.tsx b/ui/src/views/RedisInfoView.tsx index 18cff4e..450c78c 100644 --- a/ui/src/views/RedisInfoView.tsx +++ b/ui/src/views/RedisInfoView.tsx @@ -6,6 +6,8 @@ import Grid from "@material-ui/core/Grid"; import Typography from "@material-ui/core/Typography"; import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; +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 { getRedisInfoAsync } from "../actions/redisInfoActions"; @@ -23,6 +25,7 @@ const useStyles = makeStyles((theme) => ({ function mapStateToProps(state: AppState) { return { loading: state.redis.loading, + error: state.redis.error, redisInfo: state.redis.data, redisAddress: state.redis.address, redisInfoRaw: state.redis.rawData, @@ -50,102 +53,122 @@ function RedisInfoView(props: Props) { return ( - - Redis Info - - Connected to: {props.redisAddress} - - - {redisInfo !== null && ( + {props.error === "" ? ( <> - - Server + Redis Info + + Connected to: {props.redisAddress} - - - - - - - - - - Memory - - - - - - - - - - - - - - - Connections - - - - - - - - - - - - Persistence - - - - - - - - - - - )} - {redisInfoRaw !== null && ( - <> - - - INFO Command Output - - - {redisInfoRaw} - - + {redisInfo !== null && ( + <> + + + Server + + + + + + + + + + + + Memory + + + + + + + + + + + + + + + Connections + + + + + + + + + + + + Persistence + + + + + + + + + + + )} + {redisInfoRaw !== null && ( + <> + + + INFO Command Output + + + {redisInfoRaw} + + + + )} + ) : ( + + + Error + Could not retreive redis live data —{" "} + See the logs for details + + )} diff --git a/ui/src/views/SchedulersView.tsx b/ui/src/views/SchedulersView.tsx index ba0066b..2293254 100644 --- a/ui/src/views/SchedulersView.tsx +++ b/ui/src/views/SchedulersView.tsx @@ -6,6 +6,8 @@ import Grid from "@material-ui/core/Grid"; import Paper from "@material-ui/core/Paper"; import SchedulerEntriesTable from "../components/SchedulerEntriesTable"; import Typography from "@material-ui/core/Typography"; +import Alert from "@material-ui/lab/Alert"; +import AlertTitle from "@material-ui/lab/AlertTitle"; import { AppState } from "../store"; import { listSchedulerEntriesAsync } from "../actions/schedulerEntriesActions"; import { usePolling } from "../hooks"; @@ -30,6 +32,7 @@ const useStyles = makeStyles((theme) => ({ function mapStateToProps(state: AppState) { return { loading: state.schedulerEntries.loading, + error: state.schedulerEntries.error, entries: state.schedulerEntries.data, pollInterval: state.settings.pollInterval, }; @@ -48,14 +51,24 @@ function SchedulersView(props: Props) { return ( - - - - Scheduler Entries - - - - + {props.error === "" ? ( + + + + Scheduler Entries + + + + + ) : ( + + + Error + Could not retreive scheduler entries live data —{" "} + See the logs for details + + + )} ); diff --git a/ui/src/views/ServersView.tsx b/ui/src/views/ServersView.tsx index 3b1c13d..01d4728 100644 --- a/ui/src/views/ServersView.tsx +++ b/ui/src/views/ServersView.tsx @@ -5,6 +5,8 @@ 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 Alert from "@material-ui/lab/Alert"; +import AlertTitle from "@material-ui/lab/AlertTitle"; import ServersTable from "../components/ServersTable"; import { listServersAsync } from "../actions/serversActions"; import { AppState } from "../store"; @@ -30,6 +32,7 @@ const useStyles = makeStyles((theme) => ({ function mapStateToProps(state: AppState) { return { loading: state.servers.loading, + error: state.servers.error, servers: state.servers.data, pollInterval: state.settings.pollInterval, }; @@ -48,14 +51,24 @@ function ServersView(props: Props) { return ( - - - - Servers - - - - + {props.error === "" ? ( + + + + Servers + + + + + ) : ( + + + Error + Could not retreive servers live data —{" "} + See the logs for details + + + )} );