refactoring api integration

master
Tudor Stanciu 2023-04-15 00:53:11 +03:00
parent 0e025e6c68
commit 3a211bcb17
9 changed files with 144 additions and 192 deletions

View File

@ -1,3 +0,0 @@
import useApi from "./useApi";
export default useApi;

View File

@ -1,115 +0,0 @@
import { get, post } from "../utils/axios";
import { toast } from "react-toastify";
const networkRoute = `${process.env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/network`;
const systemRoute = `${process.env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/system`;
const powerActionsRoute = `${process.env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/resurrector`;
const securityRoute = `${process.env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/security`;
const handleError = err => {
let message;
switch (err?.status) {
case 500:
message = err.title;
break;
case 404:
message = err.message;
break;
default:
message = err.title;
}
toast.error(message);
};
const defaultOptions = {
onCompleted: () => {},
onError: handleError
};
const call = async (request, options) => {
const internalOptions = { ...defaultOptions, ...options };
const { onCompleted, onError } = internalOptions;
try {
const result = await request();
onCompleted(result);
} catch (error) {
onError(error);
}
};
const getPermissions = (options = defaultOptions) => {
const promise = call(() => get(`${securityRoute}/permissions`), options);
return promise;
};
const getSystemVersion = (options = defaultOptions) => {
const releaseNotesPromise = call(
() => get(`${systemRoute}/version`),
options
);
return releaseNotesPromise;
};
const readReleaseNotes = (options = defaultOptions) => {
const releaseNotesPromise = call(
() => get(`${systemRoute}/release-notes`),
options
);
return releaseNotesPromise;
};
const readMachines = (options = defaultOptions) => {
const machinesPromise = call(() => get(`${networkRoute}/machines`), options);
return machinesPromise;
};
const wakeMachine = (machineId, options = defaultOptions) => {
const promise = call(
() => post(`${powerActionsRoute}/wake`, { machineId }),
options
);
return promise;
};
const pingMachine = (machineId, options = defaultOptions) => {
const promise = call(
() => post(`${powerActionsRoute}/ping`, { machineId }),
options
);
return promise;
};
const shutdownMachine = (machineId, delay, force, options = defaultOptions) => {
const promise = call(
() => post(`${powerActionsRoute}/shutdown`, { machineId, delay, force }),
options
);
return promise;
};
const restartMachine = (machineId, delay, force, options = defaultOptions) => {
const promise = call(
() => post(`${powerActionsRoute}/restart`, { machineId, delay, force }),
options
);
return promise;
};
const useApi = () => {
return {
getPermissions,
getSystemVersion,
readReleaseNotes,
readMachines,
wakeMachine,
pingMachine,
shutdownMachine,
restartMachine
};
};
export default useApi;

View File

@ -1,8 +1,8 @@
import React, { useState, useEffect } from "react";
import PropTypes from "prop-types";
import useApi from "../../../api";
import ReleaseNotesList from "./ReleaseNotesList";
import TimelineComponent from "../timeline/TimelineComponent";
import { routes, get } from "../../../utils/api";
const sort = releases =>
releases.sort((a, b) => new Date(b.date) - new Date(a.date));
@ -10,16 +10,12 @@ const sort = releases =>
const ReleaseNotesContainer = ({ view }) => {
const [state, setState] = useState({ data: [], loaded: false });
const api = useApi();
useEffect(() => {
if (state.loaded) return;
api.readReleaseNotes({
onCompleted: data => {
setState({ data, loaded: true });
}
get(routes.releaseNotes, {
onCompleted: data => setState({ data, loaded: true })
});
}, [api, state.loaded]);
}, [state.loaded]);
return (
<>

View File

@ -1,20 +1,16 @@
import React, { useState, useEffect } from "react";
import SystemVersionComponent from "./SystemVersionComponent";
import useApi from "../../../api";
import { routes, get } from "../../../utils/api";
const SystemVersionContainer = () => {
const [state, setState] = useState({ data: {}, loaded: false });
const api = useApi();
useEffect(() => {
if (state.loaded) return;
api.getSystemVersion({
onCompleted: data => {
setState({ data, loaded: true });
}
get(routes.systemVersion, {
onCompleted: data => setState({ data, loaded: true })
});
}, [api, state.loaded]);
}, [state.loaded]);
return <>{state.loaded && <SystemVersionComponent data={state.data} />}</>;
};

View File

@ -6,7 +6,7 @@ import { ViewModes } from "./ViewModeSelection";
import { useToast } from "../../../hooks";
import { LastPage, RotateLeft, Launch, Stop } from "@material-ui/icons";
import { useTranslation } from "react-i18next";
import useApi from "../../../api";
import { routes, post } from "../../../utils/api";
const MachineContainer = ({ machine, viewMode }) => {
const [logs, setLogs] = useState([]);
@ -14,8 +14,6 @@ const MachineContainer = ({ machine, viewMode }) => {
const { success, error } = useToast();
const { t } = useTranslation();
const api = useApi();
const addLog = useCallback(
text => {
setLogs(prev => [...prev, text]);
@ -37,29 +35,41 @@ const MachineContainer = ({ machine, viewMode }) => {
const pingMachine = useCallback(
async machine => {
await api.pingMachine(machine.machineId, {
onCompleted: manageActionResponse
});
await post(
routes.pingMachine,
{ machineId: machine.machineId },
{
onCompleted: manageActionResponse
}
);
},
[manageActionResponse, api]
[manageActionResponse]
);
const shutdownMachine = useCallback(
async machine => {
await api.shutdownMachine(machine.machineId, 0, false, {
onCompleted: manageActionResponse
});
await post(
routes.shutdownMachine,
{ machineId: machine.machineId, delay: 0, force: false },
{
onCompleted: manageActionResponse
}
);
},
[manageActionResponse, api]
[manageActionResponse]
);
const restartMachine = useCallback(
async machine => {
await api.restartMachine(machine.machineId, 0, false, {
onCompleted: manageActionResponse
});
await post(
routes.restartMachine,
{ machineId: machine.machineId, delay: 0, force: false },
{
onCompleted: manageActionResponse
}
);
},
[manageActionResponse, api]
[manageActionResponse]
);
const actions = [

View File

@ -3,11 +3,11 @@ import {
NetworkStateContext,
NetworkDispatchContext
} from "../../network/state/contexts";
import useApi from "../../../api";
import MachinesListComponent from "./MachinesListComponent";
import PageTitle from "../../../components/common/PageTitle";
import { useTranslation } from "react-i18next";
import ViewModeSelection, { ViewModes } from "./ViewModeSelection";
import { routes, get } from "../../../utils/api";
const MachinesContainer = () => {
const [viewMode, setViewMode] = useState(null);
@ -16,16 +16,14 @@ const MachinesContainer = () => {
const dispatchActions = useContext(NetworkDispatchContext);
const { t } = useTranslation();
const api = useApi();
const handleReadMachines = useCallback(async () => {
await api.readMachines({
await get(routes.machines, {
onCompleted: machines => {
const data = Object.assign(machines, { loaded: true });
dispatchActions.onNetworkChange("machines", data);
}
});
}, [dispatchActions, api]);
}, [dispatchActions]);
useEffect(() => {
if (!state.network.machines.loaded) {

View File

@ -5,7 +5,7 @@ import { PowerSettingsNew } from "@material-ui/icons";
import { useTranslation } from "react-i18next";
import { useToast } from "../../../../hooks";
import { msToMinAndSec } from "../../../../utils/time";
import useApi from "../../../../api";
import { routes, post } from "../../../../utils/api";
const initialState = { on: false };
const defaultPingInterval = 1200000; //20 minutes
@ -17,7 +17,6 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
const { t } = useTranslation();
const { success, error } = useToast();
const api = useApi();
const pingInterval =
process.env.REACT_APP_MACHINE_PING_INTERVAL || defaultPingInterval;
@ -38,43 +37,51 @@ const WakeComponent = ({ machine, addLog, disabled }) => {
);
const wakeMachine = useCallback(async () => {
await api.wakeMachine(machine.machineId, {
onCompleted: result => {
setState(prev => ({ ...prev, on: result.success }));
log(`[Wake]: Success: ${result.success}. Status: ${result.status}`);
if (result.success) {
success(result.status);
await post(
routes.wakeMachine,
{ machineId: machine.machineId },
{
onCompleted: result => {
setState(prev => ({ ...prev, on: result.success }));
log(`[Wake]: Success: ${result.success}. Status: ${result.status}`);
if (result.success) {
success(result.status);
//retrigger
log(
`Periodic ping will be re-triggered in ${startingTime} ms [${msToMinAndSec(
startingTime
)}]`
);
setTimeout(() => {
setTrigger(prev => !prev);
}, startingTime);
} else {
error(result.status);
//retrigger
log(
`Periodic ping will be re-triggered in ${startingTime} ms [${msToMinAndSec(
startingTime
)}]`
);
setTimeout(() => {
setTrigger(prev => !prev);
}, startingTime);
} else {
error(result.status);
}
}
}
});
}, [log, success, error, startingTime, machine.machineId, api]);
);
}, [log, success, error, startingTime, machine.machineId]);
const pingInLoop = useCallback(async () => {
await api.pingMachine(machine.machineId, {
onCompleted: result => {
setState(prev => ({ ...prev, on: result.success }));
log(`[Ping]: Success: ${result.success}. Status: ${result.status}`);
await post(
routes.pingMachine,
{ machineId: machine.machineId },
{
onCompleted: result => {
setState(prev => ({ ...prev, on: result.success }));
log(`[Ping]: Success: ${result.success}. Status: ${result.status}`);
if (result.success) {
setTimeout(() => {
setTrigger(prev => !prev);
}, pingInterval);
}
},
onError: () => {}
});
if (result.success) {
setTimeout(() => {
setTrigger(prev => !prev);
}, pingInterval);
}
},
onError: () => {}
}
);
}, [machine, log, pingInterval]); // eslint-disable-line react-hooks/exhaustive-deps
// if "api" is added in pingInLoop dependencies, the useEffect is triggered in loop

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect, useContext, useMemo } from "react";
import PropTypes from "prop-types";
import useApi from "../api";
import { routes, get } from "../utils/api";
const permissionCodes = {
VIEW_DASHBOARD: "VIEW_DASHBOARD",
@ -64,13 +64,11 @@ const usePermissions = () => {
const UserPermissionsProvider = ({ children }) => {
const [permissions, setPermissions] = useState(initialState);
const { getPermissions } = useApi();
useEffect(() => {
getPermissions({
get(routes.permissions, {
onCompleted: data => setPermissions({ ...data, loading: false })
});
}, [getPermissions]);
}, []);
return (
<UserPermissionsContext.Provider value={permissions}>

65
src/utils/api.js Normal file
View File

@ -0,0 +1,65 @@
import * as axios from "../utils/axios";
import { toast } from "react-toastify";
const networkRoute = `${process.env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/network`;
const systemRoute = `${process.env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/system`;
const powerActionsRoute = `${process.env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/resurrector`;
const securityRoute = `${process.env.REACT_APP_NETWORK_RESURRECTOR_API_URL}/security`;
const routes = {
permissions: `${securityRoute}/permissions`,
systemVersion: `${systemRoute}/version`,
releaseNotes: `${systemRoute}/release-notes`,
machines: `${networkRoute}/machines`,
wakeMachine: `${powerActionsRoute}/wake`,
pingMachine: `${powerActionsRoute}/ping`,
shutdownMachine: `${powerActionsRoute}/shutdown`,
restartMachine: `${powerActionsRoute}/restart`
};
const handleError = err => {
let message;
switch (err?.status) {
case 500:
message = err.title;
break;
case 404:
message = err.message;
break;
default:
message = err.title;
}
toast.error(message);
};
const defaultOptions = {
onCompleted: () => {},
onError: handleError
};
const call = async (request, options = defaultOptions) => {
const internalOptions = { ...defaultOptions, ...options };
const { onCompleted, onError } = internalOptions;
try {
const result = await request();
onCompleted(result);
} catch (error) {
onError(error);
}
};
const get = (route, options) => {
const promise = call(() => axios.get(route), options);
return promise;
};
const post = (route, data, options) => {
const promise = call(() => axios.post(route, data), options);
return promise;
};
export { routes, get, post };