summaryrefslogtreecommitdiff
path: root/frontend/src/sagas
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src/sagas')
-rw-r--r--frontend/src/sagas/experiments.js183
-rw-r--r--frontend/src/sagas/index.js106
-rw-r--r--frontend/src/sagas/objects.js140
-rw-r--r--frontend/src/sagas/profile.js12
-rw-r--r--frontend/src/sagas/simulations.js51
-rw-r--r--frontend/src/sagas/topology.js434
-rw-r--r--frontend/src/sagas/users.js50
7 files changed, 976 insertions, 0 deletions
diff --git a/frontend/src/sagas/experiments.js b/frontend/src/sagas/experiments.js
new file mode 100644
index 00000000..d9c410f7
--- /dev/null
+++ b/frontend/src/sagas/experiments.js
@@ -0,0 +1,183 @@
+import { call, put, select, delay } from "redux-saga/effects";
+import { addPropToStoreObject, addToStore } from "../actions/objects";
+import { setLastSimulatedTick } from "../actions/simulation/tick";
+import { addBatchToStates } from "../actions/states";
+import {
+ deleteExperiment,
+ getAllMachineStates,
+ getAllRackStates,
+ getAllRoomStates,
+ getAllTaskStates,
+ getExperiment,
+ getLastSimulatedTick
+} from "../api/routes/experiments";
+import { getTasksOfJob } from "../api/routes/jobs";
+import {
+ addExperiment,
+ getExperimentsOfSimulation,
+ getSimulation
+} from "../api/routes/simulations";
+import { getJobsOfTrace } from "../api/routes/traces";
+import {
+ fetchAndStoreAllSchedulers,
+ fetchAndStoreAllTraces,
+ fetchAndStorePathsOfSimulation
+} from "./objects";
+import { fetchAllDatacentersOfExperiment } from "./topology";
+
+export function* onOpenExperimentSucceeded(action) {
+ try {
+ const simulation = yield call(getSimulation, action.simulationId);
+ yield put(addToStore("simulation", simulation));
+
+ const experiment = yield call(getExperiment, action.experimentId);
+ yield put(addToStore("experiment", experiment));
+
+ yield fetchExperimentSpecifications();
+ yield fetchWorkloadOfTrace(experiment.traceId);
+
+ yield fetchAllDatacentersOfExperiment(experiment);
+ yield startStateFetchLoop(action.experimentId);
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+function* startStateFetchLoop(experimentId) {
+ try {
+ while ((yield select(state => state.currentExperimentId)) !== -1) {
+ const lastSimulatedTick = (yield call(getLastSimulatedTick, experimentId))
+ .lastSimulatedTick;
+ if (
+ lastSimulatedTick !== (yield select(state => state.lastSimulatedTick))
+ ) {
+ yield put(setLastSimulatedTick(lastSimulatedTick));
+
+ const taskStates = yield call(getAllTaskStates, experimentId);
+ const machineStates = yield call(getAllMachineStates, experimentId);
+ const rackStates = yield call(getAllRackStates, experimentId);
+ const roomStates = yield call(getAllRoomStates, experimentId);
+
+ yield put(addBatchToStates("task", taskStates));
+ yield put(addBatchToStates("machine", machineStates));
+ yield put(addBatchToStates("rack", rackStates));
+ yield put(addBatchToStates("room", roomStates));
+
+ yield delay(5000);
+ } else {
+ yield delay(10000);
+ }
+ }
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onFetchExperimentsOfSimulation() {
+ try {
+ const currentSimulationId = yield select(
+ state => state.currentSimulationId
+ );
+
+ yield fetchExperimentSpecifications();
+ const experiments = yield call(
+ getExperimentsOfSimulation,
+ currentSimulationId
+ );
+ for (let i in experiments) {
+ yield put(addToStore("experiment", experiments[i]));
+ }
+ yield put(
+ addPropToStoreObject("simulation", currentSimulationId, {
+ experimentIds: experiments.map(experiment => experiment.id)
+ })
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+function* fetchExperimentSpecifications() {
+ try {
+ const currentSimulationId = yield select(
+ state => state.currentSimulationId
+ );
+ yield fetchAndStorePathsOfSimulation(currentSimulationId);
+ yield fetchAndStoreAllTraces();
+ yield fetchAndStoreAllSchedulers();
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+function* fetchWorkloadOfTrace(traceId) {
+ try {
+ const jobs = yield call(getJobsOfTrace, traceId);
+ for (let i in jobs) {
+ const job = jobs[i];
+ const tasks = yield call(getTasksOfJob, job.id);
+ job.taskIds = tasks.map(task => task.id);
+ for (let j in tasks) {
+ yield put(addToStore("task", tasks[j]));
+ }
+ yield put(addToStore("job", job));
+ }
+ yield put(
+ addPropToStoreObject("trace", traceId, {
+ jobIds: jobs.map(job => job.id)
+ })
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onAddExperiment(action) {
+ try {
+ const currentSimulationId = yield select(
+ state => state.currentSimulationId
+ );
+
+ const experiment = yield call(
+ addExperiment,
+ currentSimulationId,
+ Object.assign({}, action.experiment, {
+ id: -1,
+ simulationId: currentSimulationId
+ })
+ );
+ yield put(addToStore("experiment", experiment));
+
+ const experimentIds = yield select(
+ state => state.objects.simulation[currentSimulationId].experimentIds
+ );
+ yield put(
+ addPropToStoreObject("simulation", currentSimulationId, {
+ experimentIds: experimentIds.concat([experiment.id])
+ })
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onDeleteExperiment(action) {
+ try {
+ yield call(deleteExperiment, action.id);
+
+ const currentSimulationId = yield select(
+ state => state.currentSimulationId
+ );
+ const experimentIds = yield select(
+ state => state.objects.simulation[currentSimulationId].experimentIds
+ );
+
+ yield put(
+ addPropToStoreObject("simulation", currentSimulationId, {
+ experimentIds: experimentIds.filter(id => id !== action.id)
+ })
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
diff --git a/frontend/src/sagas/index.js b/frontend/src/sagas/index.js
new file mode 100644
index 00000000..56c8f09b
--- /dev/null
+++ b/frontend/src/sagas/index.js
@@ -0,0 +1,106 @@
+import { takeEvery } from "redux-saga/effects";
+import { LOG_IN } from "../actions/auth";
+import {
+ ADD_EXPERIMENT,
+ DELETE_EXPERIMENT,
+ FETCH_EXPERIMENTS_OF_SIMULATION,
+ OPEN_EXPERIMENT_SUCCEEDED
+} from "../actions/experiments";
+import {
+ ADD_SIMULATION,
+ DELETE_SIMULATION,
+ OPEN_SIMULATION_SUCCEEDED
+} from "../actions/simulations";
+import {
+ ADD_TILE,
+ CANCEL_NEW_ROOM_CONSTRUCTION,
+ DELETE_TILE,
+ START_NEW_ROOM_CONSTRUCTION
+} from "../actions/topology/building";
+import {
+ ADD_UNIT,
+ DELETE_MACHINE,
+ DELETE_UNIT
+} from "../actions/topology/machine";
+import {
+ ADD_MACHINE,
+ DELETE_RACK,
+ EDIT_RACK_NAME
+} from "../actions/topology/rack";
+import {
+ ADD_RACK_TO_TILE,
+ DELETE_ROOM,
+ EDIT_ROOM_NAME
+} from "../actions/topology/room";
+import {
+ DELETE_CURRENT_USER,
+ FETCH_AUTHORIZATIONS_OF_CURRENT_USER
+} from "../actions/users";
+import {
+ onAddExperiment,
+ onDeleteExperiment,
+ onFetchExperimentsOfSimulation,
+ onOpenExperimentSucceeded
+} from "./experiments";
+import { onDeleteCurrentUser } from "./profile";
+import {
+ onOpenSimulationSucceeded,
+ onSimulationAdd,
+ onSimulationDelete
+} from "./simulations";
+import {
+ onAddMachine,
+ onAddRackToTile,
+ onAddTile,
+ onAddUnit,
+ onCancelNewRoomConstruction,
+ onDeleteMachine,
+ onDeleteRack,
+ onDeleteRoom,
+ onDeleteTile,
+ onDeleteUnit,
+ onEditRackName,
+ onEditRoomName,
+ onStartNewRoomConstruction
+} from "./topology";
+import {
+ onFetchAuthorizationsOfCurrentUser,
+ onFetchLoggedInUser
+} from "./users";
+
+export default function* rootSaga() {
+ yield takeEvery(LOG_IN, onFetchLoggedInUser);
+
+ yield takeEvery(
+ FETCH_AUTHORIZATIONS_OF_CURRENT_USER,
+ onFetchAuthorizationsOfCurrentUser
+ );
+ yield takeEvery(ADD_SIMULATION, onSimulationAdd);
+ yield takeEvery(DELETE_SIMULATION, onSimulationDelete);
+
+ yield takeEvery(DELETE_CURRENT_USER, onDeleteCurrentUser);
+
+ yield takeEvery(OPEN_SIMULATION_SUCCEEDED, onOpenSimulationSucceeded);
+ yield takeEvery(OPEN_EXPERIMENT_SUCCEEDED, onOpenExperimentSucceeded);
+
+ yield takeEvery(START_NEW_ROOM_CONSTRUCTION, onStartNewRoomConstruction);
+ yield takeEvery(CANCEL_NEW_ROOM_CONSTRUCTION, onCancelNewRoomConstruction);
+ yield takeEvery(ADD_TILE, onAddTile);
+ yield takeEvery(DELETE_TILE, onDeleteTile);
+ yield takeEvery(EDIT_ROOM_NAME, onEditRoomName);
+ yield takeEvery(DELETE_ROOM, onDeleteRoom);
+ yield takeEvery(EDIT_RACK_NAME, onEditRackName);
+ yield takeEvery(DELETE_RACK, onDeleteRack);
+ yield takeEvery(ADD_RACK_TO_TILE, onAddRackToTile);
+ yield takeEvery(ADD_MACHINE, onAddMachine);
+ yield takeEvery(DELETE_MACHINE, onDeleteMachine);
+ yield takeEvery(ADD_UNIT, onAddUnit);
+ yield takeEvery(DELETE_UNIT, onDeleteUnit);
+
+ yield takeEvery(
+ FETCH_EXPERIMENTS_OF_SIMULATION,
+ onFetchExperimentsOfSimulation
+ );
+ yield takeEvery(ADD_EXPERIMENT, onAddExperiment);
+ yield takeEvery(DELETE_EXPERIMENT, onDeleteExperiment);
+}
diff --git a/frontend/src/sagas/objects.js b/frontend/src/sagas/objects.js
new file mode 100644
index 00000000..3cfd43a6
--- /dev/null
+++ b/frontend/src/sagas/objects.js
@@ -0,0 +1,140 @@
+import { call, put, select } from "redux-saga/effects";
+import { addToStore } from "../actions/objects";
+import { getDatacenter, getRoomsOfDatacenter } from "../api/routes/datacenters";
+import { getPath, getSectionsOfPath } from "../api/routes/paths";
+import { getTilesOfRoom } from "../api/routes/rooms";
+import { getAllSchedulers } from "../api/routes/schedulers";
+import { getSection } from "../api/routes/sections";
+import { getPathsOfSimulation, getSimulation } from "../api/routes/simulations";
+import {
+ getAllCPUs,
+ getAllGPUs,
+ getAllMemories,
+ getAllStorages,
+ getCoolingItem,
+ getCPU,
+ getFailureModel,
+ getGPU,
+ getMemory,
+ getPSU,
+ getStorage
+} from "../api/routes/specifications";
+import { getMachinesOfRackByTile, getRackByTile } from "../api/routes/tiles";
+import { getAllTraces } from "../api/routes/traces";
+import { getUser } from "../api/routes/users";
+
+export const OBJECT_SELECTORS = {
+ simulation: state => state.objects.simulation,
+ user: state => state.objects.user,
+ authorization: state => state.objects.authorization,
+ failureModel: state => state.objects.failureModel,
+ cpu: state => state.objects.cpu,
+ gpu: state => state.objects.gpu,
+ memory: state => state.objects.memory,
+ storage: state => state.objects.storage,
+ machine: state => state.objects.machine,
+ rack: state => state.objects.rack,
+ coolingItem: state => state.objects.coolingItem,
+ psu: state => state.objects.psu,
+ tile: state => state.objects.tile,
+ room: state => state.objects.room,
+ datacenter: state => state.objects.datacenter,
+ section: state => state.objects.section,
+ path: state => state.objects.path
+};
+
+function* fetchAndStoreObject(objectType, id, apiCall) {
+ const objectStore = yield select(OBJECT_SELECTORS[objectType]);
+ let object = objectStore[id];
+ if (!object) {
+ object = yield apiCall;
+ yield put(addToStore(objectType, object));
+ }
+ return object;
+}
+
+function* fetchAndStoreObjects(objectType, apiCall) {
+ const objects = yield apiCall;
+ for (let index in objects) {
+ yield put(addToStore(objectType, objects[index]));
+ }
+ return objects;
+}
+
+export const fetchAndStoreSimulation = id =>
+ fetchAndStoreObject("simulation", id, call(getSimulation, id));
+
+export const fetchAndStoreUser = id =>
+ fetchAndStoreObject("user", id, call(getUser, id));
+
+export const fetchAndStoreFailureModel = id =>
+ fetchAndStoreObject("failureModel", id, call(getFailureModel, id));
+
+export const fetchAndStoreAllCPUs = () =>
+ fetchAndStoreObjects("cpu", call(getAllCPUs));
+
+export const fetchAndStoreCPU = id =>
+ fetchAndStoreObject("cpu", id, call(getCPU, id));
+
+export const fetchAndStoreAllGPUs = () =>
+ fetchAndStoreObjects("gpu", call(getAllGPUs));
+
+export const fetchAndStoreGPU = id =>
+ fetchAndStoreObject("gpu", id, call(getGPU, id));
+
+export const fetchAndStoreAllMemories = () =>
+ fetchAndStoreObjects("memory", call(getAllMemories));
+
+export const fetchAndStoreMemory = id =>
+ fetchAndStoreObject("memory", id, call(getMemory, id));
+
+export const fetchAndStoreAllStorages = () =>
+ fetchAndStoreObjects("storage", call(getAllStorages));
+
+export const fetchAndStoreStorage = id =>
+ fetchAndStoreObject("storage", id, call(getStorage, id));
+
+export const fetchAndStoreMachinesOfTile = tileId =>
+ fetchAndStoreObjects("machine", call(getMachinesOfRackByTile, tileId));
+
+export const fetchAndStoreRackOnTile = (id, tileId) =>
+ fetchAndStoreObject("rack", id, call(getRackByTile, tileId));
+
+export const fetchAndStoreCoolingItem = id =>
+ fetchAndStoreObject("coolingItem", id, call(getCoolingItem, id));
+
+export const fetchAndStorePSU = id =>
+ fetchAndStoreObject("psu", id, call(getPSU, id));
+
+export const fetchAndStoreTilesOfRoom = roomId =>
+ fetchAndStoreObjects("tile", call(getTilesOfRoom, roomId));
+
+export const fetchAndStoreRoomsOfDatacenter = datacenterId =>
+ fetchAndStoreObjects("room", call(getRoomsOfDatacenter, datacenterId));
+
+export const fetchAndStoreDatacenter = id =>
+ fetchAndStoreObject("datacenter", id, call(getDatacenter, id));
+
+export const fetchAndStoreSection = id =>
+ fetchAndStoreObject("section", id, call(getSection, id));
+
+export const fetchAndStoreSectionsOfPath = pathId =>
+ fetchAndStoreObjects("section", call(getSectionsOfPath, pathId));
+
+export const fetchAndStorePath = id =>
+ fetchAndStoreObject("path", id, call(getPath, id));
+
+export const fetchAndStorePathsOfSimulation = simulationId =>
+ fetchAndStoreObjects("path", call(getPathsOfSimulation, simulationId));
+
+export const fetchAndStoreAllTraces = () =>
+ fetchAndStoreObjects("trace", call(getAllTraces));
+
+export const fetchAndStoreAllSchedulers = function*() {
+ const objects = yield call(getAllSchedulers);
+ for (let index in objects) {
+ objects[index].id = objects[index].name;
+ yield put(addToStore("scheduler", objects[index]));
+ }
+ return objects;
+};
diff --git a/frontend/src/sagas/profile.js b/frontend/src/sagas/profile.js
new file mode 100644
index 00000000..31d4dd4f
--- /dev/null
+++ b/frontend/src/sagas/profile.js
@@ -0,0 +1,12 @@
+import { call, put } from "redux-saga/effects";
+import { deleteCurrentUserSucceeded } from "../actions/users";
+import { deleteUser } from "../api/routes/users";
+
+export function* onDeleteCurrentUser(action) {
+ try {
+ yield call(deleteUser, action.userId);
+ yield put(deleteCurrentUserSucceeded());
+ } catch (error) {
+ console.error(error);
+ }
+}
diff --git a/frontend/src/sagas/simulations.js b/frontend/src/sagas/simulations.js
new file mode 100644
index 00000000..9df4e4b5
--- /dev/null
+++ b/frontend/src/sagas/simulations.js
@@ -0,0 +1,51 @@
+import { call, put } from "redux-saga/effects";
+import { addToStore } from "../actions/objects";
+import {
+ addSimulationSucceeded,
+ deleteSimulationSucceeded
+} from "../actions/simulations";
+import {
+ addSimulation,
+ deleteSimulation,
+ getSimulation
+} from "../api/routes/simulations";
+import { fetchLatestDatacenter } from "./topology";
+
+export function* onOpenSimulationSucceeded(action) {
+ try {
+ const simulation = yield call(getSimulation, action.id);
+ yield put(addToStore("simulation", simulation));
+
+ yield fetchLatestDatacenter(action.id);
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onSimulationAdd(action) {
+ try {
+ const simulation = yield call(addSimulation, { name: action.name });
+ yield put(addToStore("simulation", simulation));
+
+ const authorization = {
+ simulationId: simulation.id,
+ userId: action.userId,
+ authorizationLevel: "OWN"
+ };
+ yield put(addToStore("authorization", authorization));
+ yield put(
+ addSimulationSucceeded([authorization.userId, authorization.simulationId])
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onSimulationDelete(action) {
+ try {
+ yield call(deleteSimulation, action.id);
+ yield put(deleteSimulationSucceeded(action.id));
+ } catch (error) {
+ console.error(error);
+ }
+}
diff --git a/frontend/src/sagas/topology.js b/frontend/src/sagas/topology.js
new file mode 100644
index 00000000..13b4ed17
--- /dev/null
+++ b/frontend/src/sagas/topology.js
@@ -0,0 +1,434 @@
+import { call, put, select } from "redux-saga/effects";
+import { goDownOneInteractionLevel } from "../actions/interaction-level";
+import {
+ addIdToStoreObjectListProp,
+ addPropToStoreObject,
+ addToStore,
+ removeIdFromStoreObjectListProp
+} from "../actions/objects";
+import {
+ cancelNewRoomConstructionSucceeded,
+ setCurrentDatacenter,
+ startNewRoomConstructionSucceeded
+} from "../actions/topology/building";
+import { addRoomToDatacenter } from "../api/routes/datacenters";
+import { addTileToRoom, deleteRoom, updateRoom } from "../api/routes/rooms";
+import {
+ addMachineToRackOnTile,
+ addRackToTile,
+ deleteMachineInRackOnTile,
+ deleteRackFromTile,
+ deleteTile,
+ updateMachineInRackOnTile,
+ updateRackOnTile
+} from "../api/routes/tiles";
+import {
+ DEFAULT_RACK_POWER_CAPACITY,
+ DEFAULT_RACK_SLOT_CAPACITY,
+ MAX_NUM_UNITS_PER_MACHINE
+} from "../components/app/map/MapConstants";
+import {
+ fetchAndStoreAllCPUs,
+ fetchAndStoreAllGPUs,
+ fetchAndStoreAllMemories,
+ fetchAndStoreAllStorages,
+ fetchAndStoreCoolingItem,
+ fetchAndStoreCPU,
+ fetchAndStoreDatacenter,
+ fetchAndStoreGPU,
+ fetchAndStoreMachinesOfTile,
+ fetchAndStoreMemory,
+ fetchAndStorePath,
+ fetchAndStorePathsOfSimulation,
+ fetchAndStorePSU,
+ fetchAndStoreRackOnTile,
+ fetchAndStoreRoomsOfDatacenter,
+ fetchAndStoreSectionsOfPath,
+ fetchAndStoreStorage,
+ fetchAndStoreTilesOfRoom
+} from "./objects";
+
+export function* fetchLatestDatacenter(simulationId) {
+ try {
+ const paths = yield fetchAndStorePathsOfSimulation(simulationId);
+ const latestPath = paths[paths.length - 1];
+ const sections = yield fetchAndStoreSectionsOfPath(latestPath.id);
+ const latestSection = sections[sections.length - 1];
+ yield fetchAllUnitSpecifications();
+ yield fetchDatacenter(latestSection.datacenterId);
+ yield put(setCurrentDatacenter(latestSection.datacenterId));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* fetchAllDatacentersOfExperiment(experiment) {
+ try {
+ const path = yield fetchAndStorePath(experiment.pathId);
+ const sections = yield fetchAndStoreSectionsOfPath(path.id);
+ path.sectionIds = sections.map(section => section.id);
+ yield fetchAllUnitSpecifications();
+
+ for (let i in sections) {
+ yield fetchDatacenter(sections[i].datacenterId);
+ }
+ yield put(setCurrentDatacenter(sections[0].datacenterId));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+function* fetchDatacenter(datacenterId) {
+ try {
+ yield fetchAndStoreDatacenter(datacenterId);
+ const rooms = yield fetchAndStoreRoomsOfDatacenter(datacenterId);
+ yield put(
+ addPropToStoreObject("datacenter", datacenterId, {
+ roomIds: rooms.map(room => room.id)
+ })
+ );
+
+ for (let index in rooms) {
+ yield fetchRoom(rooms[index].id);
+ }
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+function* fetchAllUnitSpecifications() {
+ try {
+ yield fetchAndStoreAllCPUs();
+ yield fetchAndStoreAllGPUs();
+ yield fetchAndStoreAllMemories();
+ yield fetchAndStoreAllStorages();
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+function* fetchRoom(roomId) {
+ const tiles = yield fetchAndStoreTilesOfRoom(roomId);
+ yield put(
+ addPropToStoreObject("room", roomId, {
+ tileIds: tiles.map(tile => tile.id)
+ })
+ );
+
+ for (let index in tiles) {
+ yield fetchTile(tiles[index]);
+ }
+}
+
+function* fetchTile(tile) {
+ if (!tile.objectType) {
+ return;
+ }
+
+ switch (tile.objectType) {
+ case "RACK":
+ const rack = yield fetchAndStoreRackOnTile(tile.objectId, tile.id);
+ yield put(addPropToStoreObject("tile", tile.id, { rackId: rack.id }));
+ yield fetchMachinesOfRack(tile.id, rack);
+ break;
+ case "COOLING_ITEM":
+ const coolingItem = yield fetchAndStoreCoolingItem(tile.objectId);
+ yield put(
+ addPropToStoreObject("tile", tile.id, { coolingItemId: coolingItem.id })
+ );
+ break;
+ case "PSU":
+ const psu = yield fetchAndStorePSU(tile.objectId);
+ yield put(addPropToStoreObject("tile", tile.id, { psuId: psu.id }));
+ break;
+ default:
+ console.warn("Unknown rack type encountered while fetching tile objects");
+ }
+}
+
+function* fetchMachinesOfRack(tileId, rack) {
+ const machines = yield fetchAndStoreMachinesOfTile(tileId);
+ const machineIds = new Array(rack.capacity).fill(null);
+ machines.forEach(machine => (machineIds[machine.position - 1] = machine.id));
+
+ yield put(addPropToStoreObject("rack", rack.id, { machineIds }));
+
+ for (let index in machines) {
+ for (let i in machines[index].cpuIds) {
+ yield fetchAndStoreCPU(machines[index].cpuIds[i]);
+ }
+ for (let i in machines[index].gpuIds) {
+ yield fetchAndStoreGPU(machines[index].gpuIds[i]);
+ }
+ for (let i in machines[index].memoryIds) {
+ yield fetchAndStoreMemory(machines[index].memoryIds[i]);
+ }
+ for (let i in machines[index].storageIds) {
+ yield fetchAndStoreStorage(machines[index].storageIds[i]);
+ }
+ }
+}
+
+export function* onStartNewRoomConstruction() {
+ try {
+ const datacenterId = yield select(state => state.currentDatacenterId);
+ const room = yield call(addRoomToDatacenter, {
+ id: -1,
+ datacenterId,
+ roomType: "SERVER"
+ });
+ const roomWithEmptyTileList = Object.assign({}, room, { tileIds: [] });
+ yield put(addToStore("room", roomWithEmptyTileList));
+ yield put(
+ addIdToStoreObjectListProp("datacenter", datacenterId, "roomIds", room.id)
+ );
+ yield put(startNewRoomConstructionSucceeded(room.id));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onCancelNewRoomConstruction() {
+ try {
+ const datacenterId = yield select(state => state.currentDatacenterId);
+ const roomId = yield select(
+ state => state.construction.currentRoomInConstruction
+ );
+ yield call(deleteRoom, roomId);
+ yield put(
+ removeIdFromStoreObjectListProp(
+ "datacenter",
+ datacenterId,
+ "roomIds",
+ roomId
+ )
+ );
+ yield put(cancelNewRoomConstructionSucceeded());
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onAddTile(action) {
+ try {
+ const roomId = yield select(
+ state => state.construction.currentRoomInConstruction
+ );
+ const tile = yield call(addTileToRoom, {
+ roomId,
+ positionX: action.positionX,
+ positionY: action.positionY
+ });
+ yield put(addToStore("tile", tile));
+ yield put(addIdToStoreObjectListProp("room", roomId, "tileIds", tile.id));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onDeleteTile(action) {
+ try {
+ const roomId = yield select(
+ state => state.construction.currentRoomInConstruction
+ );
+ yield call(deleteTile, action.tileId);
+ yield put(
+ removeIdFromStoreObjectListProp("room", roomId, "tileIds", action.tileId)
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onEditRoomName(action) {
+ try {
+ const roomId = yield select(state => state.interactionLevel.roomId);
+ const room = Object.assign(
+ {},
+ yield select(state => state.objects.room[roomId])
+ );
+ room.name = action.name;
+ yield call(updateRoom, room);
+ yield put(addPropToStoreObject("room", roomId, { name: action.name }));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onDeleteRoom() {
+ try {
+ const datacenterId = yield select(state => state.currentDatacenterId);
+ const roomId = yield select(state => state.interactionLevel.roomId);
+ yield call(deleteRoom, roomId);
+ yield put(goDownOneInteractionLevel());
+ yield put(
+ removeIdFromStoreObjectListProp(
+ "datacenter",
+ datacenterId,
+ "roomIds",
+ roomId
+ )
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onEditRackName(action) {
+ try {
+ const tileId = yield select(state => state.interactionLevel.tileId);
+ const rackId = yield select(
+ state => state.objects.tile[state.interactionLevel.tileId].objectId
+ );
+ const rack = Object.assign(
+ {},
+ yield select(state => state.objects.rack[rackId])
+ );
+ rack.name = action.name;
+ yield call(updateRackOnTile, tileId, rack);
+ yield put(addPropToStoreObject("rack", rackId, { name: action.name }));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onDeleteRack() {
+ try {
+ const tileId = yield select(state => state.interactionLevel.tileId);
+ yield call(deleteRackFromTile, tileId);
+ yield put(goDownOneInteractionLevel());
+ yield put(addPropToStoreObject("tile", tileId, { objectType: undefined }));
+ yield put(addPropToStoreObject("tile", tileId, { objectId: undefined }));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onAddRackToTile(action) {
+ try {
+ const rack = yield call(addRackToTile, action.tileId, {
+ id: -1,
+ name: "Rack",
+ capacity: DEFAULT_RACK_SLOT_CAPACITY,
+ powerCapacityW: DEFAULT_RACK_POWER_CAPACITY
+ });
+ rack.machineIds = new Array(rack.capacity).fill(null);
+ yield put(addToStore("rack", rack));
+ yield put(
+ addPropToStoreObject("tile", action.tileId, { objectId: rack.id })
+ );
+ yield put(
+ addPropToStoreObject("tile", action.tileId, { objectType: "RACK" })
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onAddMachine(action) {
+ try {
+ const tileId = yield select(state => state.interactionLevel.tileId);
+ const rackId = yield select(
+ state => state.objects.tile[state.interactionLevel.tileId].objectId
+ );
+ const rack = yield select(state => state.objects.rack[rackId]);
+
+ const machine = yield call(addMachineToRackOnTile, tileId, {
+ id: -1,
+ rackId,
+ position: action.position,
+ tags: [],
+ cpuIds: [],
+ gpuIds: [],
+ memoryIds: [],
+ storageIds: []
+ });
+ yield put(addToStore("machine", machine));
+
+ const machineIds = [...rack.machineIds];
+ machineIds[machine.position - 1] = machine.id;
+ yield put(addPropToStoreObject("rack", rackId, { machineIds }));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onDeleteMachine() {
+ try {
+ const tileId = yield select(state => state.interactionLevel.tileId);
+ const position = yield select(state => state.interactionLevel.position);
+ const rack = yield select(
+ state => state.objects.rack[state.objects.tile[tileId].objectId]
+ );
+ yield call(deleteMachineInRackOnTile, tileId, position);
+ const machineIds = [...rack.machineIds];
+ machineIds[position - 1] = null;
+ yield put(goDownOneInteractionLevel());
+ yield put(addPropToStoreObject("rack", rack.id, { machineIds }));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onAddUnit(action) {
+ try {
+ const tileId = yield select(state => state.interactionLevel.tileId);
+ const position = yield select(state => state.interactionLevel.position);
+ const machine = yield select(
+ state =>
+ state.objects.machine[
+ state.objects.rack[state.objects.tile[tileId].objectId].machineIds[
+ position - 1
+ ]
+ ]
+ );
+
+ if (machine[action.unitType + "Ids"].length >= MAX_NUM_UNITS_PER_MACHINE) {
+ return;
+ }
+
+ const units = [...machine[action.unitType + "Ids"], action.id];
+ const updatedMachine = Object.assign({}, machine, {
+ [action.unitType + "Ids"]: units
+ });
+
+ yield call(updateMachineInRackOnTile, tileId, position, updatedMachine);
+
+ yield put(
+ addPropToStoreObject("machine", machine.id, {
+ [action.unitType + "Ids"]: units
+ })
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onDeleteUnit(action) {
+ try {
+ const tileId = yield select(state => state.interactionLevel.tileId);
+ const position = yield select(state => state.interactionLevel.position);
+ const machine = yield select(
+ state =>
+ state.objects.machine[
+ state.objects.rack[state.objects.tile[tileId].objectId].machineIds[
+ position - 1
+ ]
+ ]
+ );
+ const unitIds = machine[action.unitType + "Ids"].slice();
+ unitIds.splice(action.index, 1);
+ const updatedMachine = Object.assign({}, machine, {
+ [action.unitType + "Ids"]: unitIds
+ });
+
+ yield call(updateMachineInRackOnTile, tileId, position, updatedMachine);
+ yield put(
+ addPropToStoreObject("machine", machine.id, {
+ [action.unitType + "Ids"]: unitIds
+ })
+ );
+ } catch (error) {
+ console.error(error);
+ }
+}
diff --git a/frontend/src/sagas/users.js b/frontend/src/sagas/users.js
new file mode 100644
index 00000000..3825443a
--- /dev/null
+++ b/frontend/src/sagas/users.js
@@ -0,0 +1,50 @@
+import { call, put } from "redux-saga/effects";
+import { logInSucceeded } from "../actions/auth";
+import { addToStore } from "../actions/objects";
+import { fetchAuthorizationsOfCurrentUserSucceeded } from "../actions/users";
+import { performTokenSignIn } from "../api/routes/token-signin";
+import { addUser, getAuthorizationsByUser } from "../api/routes/users";
+import { saveAuthLocalStorage } from "../auth/index";
+import { fetchAndStoreSimulation, fetchAndStoreUser } from "./objects";
+
+export function* onFetchLoggedInUser(action) {
+ try {
+ const tokenResponse = yield call(
+ performTokenSignIn,
+ action.payload.authToken
+ );
+ let userId = tokenResponse.userId;
+
+ if (tokenResponse.isNewUser) {
+ saveAuthLocalStorage({ authToken: action.payload.authToken });
+ const newUser = yield call(addUser, action.payload);
+ userId = newUser.id;
+ }
+
+ yield put(logInSucceeded(Object.assign({ userId }, action.payload)));
+ } catch (error) {
+ console.error(error);
+ }
+}
+
+export function* onFetchAuthorizationsOfCurrentUser(action) {
+ try {
+ const authorizations = yield call(getAuthorizationsByUser, action.userId);
+
+ for (const authorization of authorizations) {
+ yield put(addToStore("authorization", authorization));
+
+ yield fetchAndStoreSimulation(authorization.simulationId);
+ yield fetchAndStoreUser(authorization.userId);
+ }
+
+ const authorizationIds = authorizations.map(authorization => [
+ authorization.userId,
+ authorization.simulationId
+ ]);
+
+ yield put(fetchAuthorizationsOfCurrentUserSucceeded(authorizationIds));
+ } catch (error) {
+ console.error(error);
+ }
+}