summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src/redux/sagas
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2021-07-08 16:08:02 +0200
committerGitHub <noreply@github.com>2021-07-08 16:08:02 +0200
commit1a2416043f0b877f570e89da74e0d0a4aff1d8ae (patch)
tree1bed18bb62d223be954faca87b0736d2a571b443 /opendc-web/opendc-web-ui/src/redux/sagas
parentdfd2ded56780995cec6d91af37443b710d4ddb3b (diff)
parent2c8d675c2cf140eac05988065a9d20fd2773399a (diff)
ui: Simplify data fetching in frontend
This pull request aims to simplify the data fetching logic in the OpenDC frontend. Previously, the frontend used Redux extensively to sync the server state with the client state, which introduced a lot of unnecessary complexity. With this pull request, we move most of the data fetching logic out of Redux and instead use React Query to perform the logic for fetching and caching API requests. * Move all server data except topologies outside Redux * Use React Query for fetching server data * (De)normalize topology using Normalizr * Remove current ids state from Redux * Combine fetching of project relations
Diffstat (limited to 'opendc-web/opendc-web-ui/src/redux/sagas')
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/index.js26
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/objects.js238
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js136
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js11
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/projects.js44
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js69
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/topology.js126
7 files changed, 77 insertions, 573 deletions
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/index.js b/opendc-web/opendc-web-ui/src/redux/sagas/index.js
index a8f44843..318f0afb 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/index.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/index.js
@@ -1,6 +1,5 @@
import { takeEvery } from 'redux-saga/effects'
-import { ADD_PORTFOLIO, DELETE_PORTFOLIO, OPEN_PORTFOLIO_SUCCEEDED, UPDATE_PORTFOLIO } from '../actions/portfolios'
-import { ADD_PROJECT, DELETE_PROJECT, FETCH_PROJECTS, OPEN_PROJECT_SUCCEEDED } from '../actions/projects'
+import { OPEN_PROJECT_SUCCEEDED } from '../actions/projects'
import {
ADD_TILE,
CANCEL_NEW_ROOM_CONSTRUCTION,
@@ -10,8 +9,7 @@ import {
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 { onAddPortfolio, onDeletePortfolio, onOpenPortfolioSucceeded, onUpdatePortfolio } from './portfolios'
-import { onFetchProjects, onOpenProjectSucceeded, onProjectAdd, onProjectDelete } from './projects'
+import { onOpenProjectSucceeded } from './projects'
import {
onAddMachine,
onAddRackToTile,
@@ -23,29 +21,19 @@ import {
onDeleteRack,
onDeleteRoom,
onDeleteTile,
- onDeleteTopology,
onDeleteUnit,
onEditRackName,
onEditRoomName,
onStartNewRoomConstruction,
} from './topology'
-import { ADD_TOPOLOGY, DELETE_TOPOLOGY } from '../actions/topologies'
-import { ADD_SCENARIO, DELETE_SCENARIO, OPEN_SCENARIO_SUCCEEDED, UPDATE_SCENARIO } from '../actions/scenarios'
-import { onAddScenario, onDeleteScenario, onOpenScenarioSucceeded, onUpdateScenario } from './scenarios'
+import { ADD_TOPOLOGY } from '../actions/topologies'
import { onAddPrefab } from './prefabs'
import { ADD_PREFAB } from '../actions/prefabs'
export default function* rootSaga() {
- yield takeEvery(FETCH_PROJECTS, onFetchProjects)
- yield takeEvery(ADD_PROJECT, onProjectAdd)
- yield takeEvery(DELETE_PROJECT, onProjectDelete)
-
yield takeEvery(OPEN_PROJECT_SUCCEEDED, onOpenProjectSucceeded)
- yield takeEvery(OPEN_PORTFOLIO_SUCCEEDED, onOpenPortfolioSucceeded)
- yield takeEvery(OPEN_SCENARIO_SUCCEEDED, onOpenScenarioSucceeded)
yield takeEvery(ADD_TOPOLOGY, onAddTopology)
- yield takeEvery(DELETE_TOPOLOGY, onDeleteTopology)
yield takeEvery(START_NEW_ROOM_CONSTRUCTION, onStartNewRoomConstruction)
yield takeEvery(CANCEL_NEW_ROOM_CONSTRUCTION, onCancelNewRoomConstruction)
yield takeEvery(ADD_TILE, onAddTile)
@@ -60,13 +48,5 @@ export default function* rootSaga() {
yield takeEvery(ADD_UNIT, onAddUnit)
yield takeEvery(DELETE_UNIT, onDeleteUnit)
- yield takeEvery(ADD_PORTFOLIO, onAddPortfolio)
- yield takeEvery(UPDATE_PORTFOLIO, onUpdatePortfolio)
- yield takeEvery(DELETE_PORTFOLIO, onDeletePortfolio)
-
- yield takeEvery(ADD_SCENARIO, onAddScenario)
- yield takeEvery(UPDATE_SCENARIO, onUpdateScenario)
- yield takeEvery(DELETE_SCENARIO, onDeleteScenario)
-
yield takeEvery(ADD_PREFAB, onAddPrefab)
}
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/objects.js b/opendc-web/opendc-web-ui/src/redux/sagas/objects.js
index e5fd092d..9b4f8094 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/objects.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/objects.js
@@ -1,234 +1,36 @@
import { call, put, select, getContext } from 'redux-saga/effects'
-import { addToStore } from '../actions/objects'
-import { getAllSchedulers } from '../../api/schedulers'
-import { getProject } from '../../api/projects'
-import { getAllTraces } from '../../api/traces'
-import { getTopology, updateTopology } from '../../api/topologies'
-import { uuid } from 'uuidv4'
-
-export const OBJECT_SELECTORS = {
- project: (state) => state.objects.project,
- user: (state) => state.objects.user,
- authorization: (state) => state.objects.authorization,
- portfolio: (state) => state.objects.portfolio,
- scenario: (state) => state.objects.scenario,
- 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,
- tile: (state) => state.objects.tile,
- room: (state) => state.objects.room,
- topology: (state) => state.objects.topology,
-}
-
-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 object of objects) {
- yield put(addToStore(objectType, object))
- }
- return objects
-}
-
-export const fetchAndStoreProject = function* (id) {
- const auth = yield getContext('auth')
- return yield fetchAndStoreObject('project', id, call(getProject, auth, id))
-}
-
+import { fetchTopology, updateTopology } from '../../api/topologies'
+import { Topology } from '../../util/topology-schema'
+import { denormalize, normalize } from 'normalizr'
+import { storeTopology } from '../actions/topologies'
+
+/**
+ * Fetches and normalizes the topology with the specified identifier.
+ */
export const fetchAndStoreTopology = function* (id) {
- const topologyStore = yield select(OBJECT_SELECTORS['topology'])
- const roomStore = yield select(OBJECT_SELECTORS['room'])
- const tileStore = yield select(OBJECT_SELECTORS['tile'])
- const rackStore = yield select(OBJECT_SELECTORS['rack'])
- const machineStore = yield select(OBJECT_SELECTORS['machine'])
const auth = yield getContext('auth')
- let topology = topologyStore[id]
+ let topology = yield select((state) => state.objects.topology[id])
if (!topology) {
- const fullTopology = yield call(getTopology, auth, id)
-
- for (let roomIdx in fullTopology.rooms) {
- const fullRoom = fullTopology.rooms[roomIdx]
-
- generateIdIfNotPresent(fullRoom)
-
- if (!roomStore[fullRoom._id]) {
- for (let tileIdx in fullRoom.tiles) {
- const fullTile = fullRoom.tiles[tileIdx]
-
- generateIdIfNotPresent(fullTile)
-
- if (!tileStore[fullTile._id]) {
- if (fullTile.rack) {
- const fullRack = fullTile.rack
-
- generateIdIfNotPresent(fullRack)
-
- if (!rackStore[fullRack._id]) {
- for (let machineIdx in fullRack.machines) {
- const fullMachine = fullRack.machines[machineIdx]
-
- generateIdIfNotPresent(fullMachine)
-
- if (!machineStore[fullMachine._id]) {
- let machine = (({ _id, position, cpus, gpus, memories, storages }) => ({
- _id,
- rackId: fullRack._id,
- position,
- cpuIds: cpus.map((u) => u._id),
- gpuIds: gpus.map((u) => u._id),
- memoryIds: memories.map((u) => u._id),
- storageIds: storages.map((u) => u._id),
- }))(fullMachine)
- yield put(addToStore('machine', machine))
- }
- }
-
- const filledSlots = new Array(fullRack.capacity).fill(null)
- fullRack.machines.forEach(
- (machine) => (filledSlots[machine.position - 1] = machine._id)
- )
- let rack = (({ _id, name, capacity, powerCapacityW }) => ({
- _id,
- name,
- capacity,
- powerCapacityW,
- machineIds: filledSlots,
- }))(fullRack)
- yield put(addToStore('rack', rack))
- }
- }
-
- let tile = (({ _id, positionX, positionY, rack }) => ({
- _id,
- roomId: fullRoom._id,
- positionX,
- positionY,
- rackId: rack ? rack._id : undefined,
- }))(fullTile)
- yield put(addToStore('tile', tile))
- }
- }
-
- let room = (({ _id, name, tiles }) => ({ _id, name, tileIds: tiles.map((t) => t._id) }))(fullRoom)
- yield put(addToStore('room', room))
- }
- }
-
- topology = (({ _id, name, rooms }) => ({ _id, name, roomIds: rooms.map((r) => r._id) }))(fullTopology)
- yield put(addToStore('topology', topology))
-
- // TODO consider pushing the IDs
+ const newTopology = yield call(fetchTopology, auth, id)
+ const { entities } = normalize(newTopology, Topology)
+ yield put(storeTopology(entities))
}
return topology
}
-const generateIdIfNotPresent = (obj) => {
- if (!obj._id) {
- obj._id = uuid()
- }
-}
-
export const updateTopologyOnServer = function* (id) {
- const topology = yield getTopologyAsObject(id, true)
+ const topology = yield denormalizeTopology(id)
const auth = yield getContext('auth')
yield call(updateTopology, auth, topology)
}
-export const getTopologyAsObject = function* (id, keepIds) {
- const topologyStore = yield select(OBJECT_SELECTORS['topology'])
- const rooms = yield getAllRooms(topologyStore[id].roomIds, keepIds)
- return {
- _id: keepIds ? id : undefined,
- name: topologyStore[id].name,
- rooms: rooms,
- }
-}
-
-export const getAllRooms = function* (roomIds, keepIds) {
- const roomStore = yield select(OBJECT_SELECTORS['room'])
-
- let rooms = []
-
- for (let id of roomIds) {
- let tiles = yield getAllRoomTiles(roomStore[id], keepIds)
- rooms.push({
- _id: keepIds ? id : undefined,
- name: roomStore[id].name,
- tiles: tiles,
- })
- }
- return rooms
-}
-
-export const getAllRoomTiles = function* (roomStore, keepIds) {
- let tiles = []
-
- for (let id of roomStore.tileIds) {
- tiles.push(yield getTileById(id, keepIds))
- }
- return tiles
-}
-
-export const getTileById = function* (id, keepIds) {
- const tileStore = yield select(OBJECT_SELECTORS['tile'])
- return {
- _id: keepIds ? id : undefined,
- positionX: tileStore[id].positionX,
- positionY: tileStore[id].positionY,
- rack: !tileStore[id].rackId ? undefined : yield getRackById(tileStore[id].rackId, keepIds),
- }
-}
-
-export const getRackById = function* (id, keepIds) {
- const rackStore = yield select(OBJECT_SELECTORS['rack'])
- const machineStore = yield select(OBJECT_SELECTORS['machine'])
- const cpuStore = yield select(OBJECT_SELECTORS['cpu'])
- const gpuStore = yield select(OBJECT_SELECTORS['gpu'])
- const memoryStore = yield select(OBJECT_SELECTORS['memory'])
- const storageStore = yield select(OBJECT_SELECTORS['storage'])
-
- return {
- _id: keepIds ? rackStore[id]._id : undefined,
- name: rackStore[id].name,
- capacity: rackStore[id].capacity,
- powerCapacityW: rackStore[id].powerCapacityW,
- machines: rackStore[id].machineIds
- .filter((m) => m !== null)
- .map((machineId) => ({
- _id: keepIds ? machineId : undefined,
- position: machineStore[machineId].position,
- cpus: machineStore[machineId].cpuIds.map((id) => cpuStore[id]),
- gpus: machineStore[machineId].gpuIds.map((id) => gpuStore[id]),
- memories: machineStore[machineId].memoryIds.map((id) => memoryStore[id]),
- storages: machineStore[machineId].storageIds.map((id) => storageStore[id]),
- })),
- }
-}
-
-export const fetchAndStoreAllTraces = function* () {
- const auth = yield getContext('auth')
- return yield fetchAndStoreObjects('trace', call(getAllTraces, auth))
-}
-
-export const fetchAndStoreAllSchedulers = function* () {
- const auth = yield getContext('auth')
- const objects = yield call(getAllSchedulers, auth)
- for (let object of objects) {
- object._id = object.name
- yield put(addToStore('scheduler', object))
- }
- return objects
+/**
+ * Denormalizes the topology representation in order to be stored on the server.
+ */
+export const denormalizeTopology = function* (id) {
+ const objects = yield select((state) => state.objects)
+ const topology = objects.topology[id]
+ return denormalize(topology, Topology, objects)
}
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js b/opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js
deleted file mode 100644
index 340cb490..00000000
--- a/opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js
+++ /dev/null
@@ -1,136 +0,0 @@
-import { call, put, select, delay, getContext } from 'redux-saga/effects'
-import { addPropToStoreObject, addToStore } from '../actions/objects'
-import { addPortfolio, deletePortfolio, getPortfolio, updatePortfolio } from '../../api/portfolios'
-import { getProject } from '../../api/projects'
-import { fetchAndStoreAllSchedulers, fetchAndStoreAllTraces } from './objects'
-import { fetchAndStoreAllTopologiesOfProject } from './topology'
-import { getScenario } from '../../api/scenarios'
-
-export function* onOpenPortfolioSucceeded(action) {
- try {
- const auth = yield getContext('auth')
- const project = yield call(getProject, auth, action.projectId)
- yield put(addToStore('project', project))
- yield fetchAndStoreAllTopologiesOfProject(project._id)
- yield fetchPortfoliosOfProject()
- yield fetchAndStoreAllSchedulers()
- yield fetchAndStoreAllTraces()
-
- yield watchForPortfolioResults()
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* watchForPortfolioResults() {
- try {
- const currentPortfolioId = yield select((state) => state.currentPortfolioId)
- let unfinishedScenarios = yield getCurrentUnfinishedScenarios()
-
- while (unfinishedScenarios.length > 0) {
- yield delay(3000)
- yield fetchPortfolioWithScenarios(currentPortfolioId)
- unfinishedScenarios = yield getCurrentUnfinishedScenarios()
- }
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* getCurrentUnfinishedScenarios() {
- try {
- const currentPortfolioId = yield select((state) => state.currentPortfolioId)
- const scenarioIds = yield select((state) => state.objects.portfolio[currentPortfolioId].scenarioIds)
- const scenarioObjects = yield select((state) => state.objects.scenario)
- const scenarios = scenarioIds.map((s) => scenarioObjects[s])
- return scenarios.filter((s) => !s || s.simulation.state === 'QUEUED' || s.simulation.state === 'RUNNING')
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* fetchPortfoliosOfProject() {
- try {
- const currentProjectId = yield select((state) => state.currentProjectId)
- const currentProject = yield select((state) => state.objects.project[currentProjectId])
-
- yield fetchAndStoreAllSchedulers()
- yield fetchAndStoreAllTraces()
-
- for (let i in currentProject.portfolioIds) {
- yield fetchPortfolioWithScenarios(currentProject.portfolioIds[i])
- }
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* fetchPortfolioWithScenarios(portfolioId) {
- try {
- const auth = yield getContext('auth')
- const portfolio = yield call(getPortfolio, auth, portfolioId)
- yield put(addToStore('portfolio', portfolio))
-
- for (let i in portfolio.scenarioIds) {
- const scenario = yield call(getScenario, auth, portfolio.scenarioIds[i])
- yield put(addToStore('scenario', scenario))
- }
- return portfolio
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onAddPortfolio(action) {
- try {
- const currentProjectId = yield select((state) => state.currentProjectId)
- const auth = yield getContext('auth')
- const portfolio = yield call(
- addPortfolio,
- auth,
- currentProjectId,
- Object.assign({}, action.portfolio, {
- projectId: currentProjectId,
- scenarioIds: [],
- })
- )
- yield put(addToStore('portfolio', portfolio))
-
- const portfolioIds = yield select((state) => state.objects.project[currentProjectId].portfolioIds)
- yield put(
- addPropToStoreObject('project', currentProjectId, {
- portfolioIds: portfolioIds.concat([portfolio._id]),
- })
- )
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onUpdatePortfolio(action) {
- try {
- const auth = yield getContext('auth')
- const portfolio = yield call(updatePortfolio, auth, action.portfolio._id, action.portfolio)
- yield put(addToStore('portfolio', portfolio))
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onDeletePortfolio(action) {
- try {
- const auth = yield getContext('auth')
- yield call(deletePortfolio, auth, action.id)
-
- const currentProjectId = yield select((state) => state.currentProjectId)
- const portfolioIds = yield select((state) => state.objects.project[currentProjectId].portfolioIds)
-
- yield put(
- addPropToStoreObject('project', currentProjectId, {
- portfolioIds: portfolioIds.filter((id) => id !== action.id),
- })
- )
- } catch (error) {
- console.error(error)
- }
-}
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js b/opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js
index ec679391..91b03bf6 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js
@@ -1,14 +1,17 @@
import { call, put, select, getContext } from 'redux-saga/effects'
import { addToStore } from '../actions/objects'
import { addPrefab } from '../../api/prefabs'
-import { getRackById } from './objects'
+import { Rack } from '../../util/topology-schema'
+import { denormalize } from 'normalizr'
export function* onAddPrefab(action) {
try {
- const currentRackId = yield select((state) => state.objects.tile[state.interactionLevel.tileId].rackId)
- const currentRackJson = yield getRackById(currentRackId, false)
+ const interactionLevel = yield select((state) => state.interactionLevel)
+ const objects = yield select((state) => state.objects)
+ const rack = objects.rack[objects.tile[interactionLevel.tileId].rack]
+ const prefabRack = denormalize(rack, Rack, objects)
const auth = yield getContext('auth')
- const prefab = yield call(addPrefab, auth, { name: action.name, rack: currentRackJson })
+ const prefab = yield call(() => addPrefab(auth, { name: action.name, rack: prefabRack }))
yield put(addToStore('prefab', prefab))
} catch (error) {
console.error(error)
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/projects.js b/opendc-web/opendc-web-ui/src/redux/sagas/projects.js
index 506df6ed..5809d4d2 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/projects.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/projects.js
@@ -1,52 +1,8 @@
-import { call, put, getContext } from 'redux-saga/effects'
-import { addToStore } from '../actions/objects'
-import { addProjectSucceeded, deleteProjectSucceeded, fetchProjectsSucceeded } from '../actions/projects'
-import { addProject, deleteProject, getProject, getProjects } from '../../api/projects'
import { fetchAndStoreAllTopologiesOfProject } from './topology'
-import { fetchAndStoreAllSchedulers, fetchAndStoreAllTraces } from './objects'
-import { fetchPortfoliosOfProject } from './portfolios'
export function* onOpenProjectSucceeded(action) {
try {
- const auth = yield getContext('auth')
- const project = yield call(getProject, auth, action.id)
- yield put(addToStore('project', project))
-
yield fetchAndStoreAllTopologiesOfProject(action.id, true)
- yield fetchPortfoliosOfProject()
- yield fetchAndStoreAllSchedulers()
- yield fetchAndStoreAllTraces()
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onProjectAdd(action) {
- try {
- const auth = yield getContext('auth')
- const project = yield call(addProject, auth, { name: action.name })
- yield put(addToStore('project', project))
- yield put(addProjectSucceeded(project))
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onProjectDelete(action) {
- try {
- const auth = yield getContext('auth')
- yield call(deleteProject, auth, action.id)
- yield put(deleteProjectSucceeded(action.id))
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onFetchProjects(action) {
- try {
- const auth = yield getContext('auth')
- const projects = yield call(getProjects, auth)
- yield put(fetchProjectsSucceeded(projects))
} catch (error) {
console.error(error)
}
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js b/opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js
deleted file mode 100644
index bdb7c45d..00000000
--- a/opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js
+++ /dev/null
@@ -1,69 +0,0 @@
-import { call, put, select, getContext } from 'redux-saga/effects'
-import { addPropToStoreObject, addToStore } from '../actions/objects'
-import { getProject } from '../../api/projects'
-import { fetchAndStoreAllSchedulers, fetchAndStoreAllTraces } from './objects'
-import { fetchAndStoreAllTopologiesOfProject } from './topology'
-import { addScenario, deleteScenario, updateScenario } from '../../api/scenarios'
-import { fetchPortfolioWithScenarios, watchForPortfolioResults } from './portfolios'
-
-export function* onOpenScenarioSucceeded(action) {
- try {
- const auth = yield getContext('auth')
- const project = yield call(getProject, auth, action.projectId)
- yield put(addToStore('project', project))
- yield fetchAndStoreAllTopologiesOfProject(project._id)
- yield fetchAndStoreAllSchedulers()
- yield fetchAndStoreAllTraces()
- yield fetchPortfolioWithScenarios(action.portfolioId)
-
- // TODO Fetch scenario-specific metrics
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onAddScenario(action) {
- try {
- const auth = yield getContext('auth')
- const scenario = yield call(addScenario, auth, action.scenario.portfolioId, action.scenario)
- yield put(addToStore('scenario', scenario))
-
- const scenarioIds = yield select((state) => state.objects.portfolio[action.scenario.portfolioId].scenarioIds)
- yield put(
- addPropToStoreObject('portfolio', action.scenario.portfolioId, {
- scenarioIds: scenarioIds.concat([scenario._id]),
- })
- )
- yield watchForPortfolioResults()
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onUpdateScenario(action) {
- try {
- const auth = yield getContext('auth')
- const scenario = yield call(updateScenario, auth, action.scenario._id, action.scenario)
- yield put(addToStore('scenario', scenario))
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onDeleteScenario(action) {
- try {
- const auth = yield getContext('auth')
- yield call(deleteScenario, auth, action.id)
-
- const currentPortfolioId = yield select((state) => state.currentPortfolioId)
- const scenarioIds = yield select((state) => state.objects.portfolio[currentPortfolioId].scenarioIds)
-
- yield put(
- addPropToStoreObject('scenario', currentPortfolioId, {
- scenarioIds: scenarioIds.filter((id) => id !== action.id),
- })
- )
- } catch (error) {
- console.error(error)
- }
-}
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/topology.js b/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
index e5fd3d39..5d9154fd 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
@@ -16,16 +16,17 @@ import {
DEFAULT_RACK_SLOT_CAPACITY,
MAX_NUM_UNITS_PER_MACHINE,
} from '../../components/app/map/MapConstants'
-import { fetchAndStoreTopology, getTopologyAsObject, updateTopologyOnServer } from './objects'
+import { fetchAndStoreTopology, denormalizeTopology, updateTopologyOnServer } from './objects'
import { uuid } from 'uuidv4'
-import { addTopology, deleteTopology } from '../../api/topologies'
+import { addTopology } from '../../api/topologies'
export function* fetchAndStoreAllTopologiesOfProject(projectId, setTopology = false) {
try {
- const project = yield select((state) => state.objects.project[projectId])
+ const queryClient = yield getContext('queryClient')
+ const project = yield call(() => queryClient.fetchQuery(['projects', projectId]))
- for (let i in project.topologyIds) {
- yield fetchAndStoreTopology(project.topologyIds[i])
+ for (const id of project.topologyIds) {
+ yield fetchAndStoreTopology(id)
}
if (setTopology) {
@@ -38,62 +39,26 @@ export function* fetchAndStoreAllTopologiesOfProject(projectId, setTopology = fa
export function* onAddTopology(action) {
try {
- const currentProjectId = yield select((state) => state.currentProjectId)
+ const { projectId, duplicateId, name } = action
let topologyToBeCreated
- if (action.duplicateId) {
- topologyToBeCreated = yield getTopologyAsObject(action.duplicateId, false)
- topologyToBeCreated = Object.assign({}, topologyToBeCreated, {
- name: action.name,
- })
+ if (duplicateId) {
+ topologyToBeCreated = yield denormalizeTopology(duplicateId)
+ topologyToBeCreated = { ...topologyToBeCreated, name }
+ delete topologyToBeCreated._id
} else {
topologyToBeCreated = { name: action.name, rooms: [] }
}
const auth = yield getContext('auth')
- const topology = yield call(
- addTopology,
- auth,
- Object.assign({}, topologyToBeCreated, {
- projectId: currentProjectId,
- })
- )
+ const topology = yield call(addTopology, auth, { ...topologyToBeCreated, projectId })
yield fetchAndStoreTopology(topology._id)
-
- const topologyIds = yield select((state) => state.objects.project[currentProjectId].topologyIds)
- yield put(
- addPropToStoreObject('project', currentProjectId, {
- topologyIds: topologyIds.concat([topology._id]),
- })
- )
yield put(setCurrentTopology(topology._id))
} catch (error) {
console.error(error)
}
}
-export function* onDeleteTopology(action) {
- try {
- const currentProjectId = yield select((state) => state.currentProjectId)
- const topologyIds = yield select((state) => state.objects.project[currentProjectId].topologyIds)
- const currentTopologyId = yield select((state) => state.currentTopologyId)
- if (currentTopologyId === action.id) {
- yield put(setCurrentTopology(topologyIds.filter((t) => t !== action.id)[0]))
- }
-
- const auth = yield getContext('auth')
- yield call(deleteTopology, auth, action.id)
-
- yield put(
- addPropToStoreObject('project', currentProjectId, {
- topologyIds: topologyIds.filter((id) => id !== action.id),
- })
- )
- } catch (error) {
- console.error(error)
- }
-}
-
export function* onStartNewRoomConstruction() {
try {
const topologyId = yield select((state) => state.currentTopologyId)
@@ -101,10 +66,10 @@ export function* onStartNewRoomConstruction() {
_id: uuid(),
name: 'Room',
topologyId,
- tileIds: [],
+ tiles: [],
}
yield put(addToStore('room', room))
- yield put(addIdToStoreObjectListProp('topology', topologyId, 'roomIds', room._id))
+ yield put(addIdToStoreObjectListProp('topology', topologyId, 'rooms', room._id))
yield updateTopologyOnServer(topologyId)
yield put(startNewRoomConstructionSucceeded(room._id))
} catch (error) {
@@ -116,7 +81,7 @@ export function* onCancelNewRoomConstruction() {
try {
const topologyId = yield select((state) => state.currentTopologyId)
const roomId = yield select((state) => state.construction.currentRoomInConstruction)
- yield put(removeIdFromStoreObjectListProp('topology', topologyId, 'roomIds', roomId))
+ yield put(removeIdFromStoreObjectListProp('topology', topologyId, 'rooms', roomId))
// TODO remove room from store, too
yield updateTopologyOnServer(topologyId)
yield put(cancelNewRoomConstructionSucceeded())
@@ -136,7 +101,7 @@ export function* onAddTile(action) {
positionY: action.positionY,
}
yield put(addToStore('tile', tile))
- yield put(addIdToStoreObjectListProp('room', roomId, 'tileIds', tile._id))
+ yield put(addIdToStoreObjectListProp('room', roomId, 'tiles', tile._id))
yield updateTopologyOnServer(topologyId)
} catch (error) {
console.error(error)
@@ -147,7 +112,7 @@ export function* onDeleteTile(action) {
try {
const topologyId = yield select((state) => state.currentTopologyId)
const roomId = yield select((state) => state.construction.currentRoomInConstruction)
- yield put(removeIdFromStoreObjectListProp('room', roomId, 'tileIds', action.tileId))
+ yield put(removeIdFromStoreObjectListProp('room', roomId, 'tiles', action.tileId))
yield updateTopologyOnServer(topologyId)
} catch (error) {
console.error(error)
@@ -172,7 +137,7 @@ export function* onDeleteRoom() {
const topologyId = yield select((state) => state.currentTopologyId)
const roomId = yield select((state) => state.interactionLevel.roomId)
yield put(goDownOneInteractionLevel())
- yield put(removeIdFromStoreObjectListProp('topology', topologyId, 'roomIds', roomId))
+ yield put(removeIdFromStoreObjectListProp('topology', topologyId, 'rooms', roomId))
yield updateTopologyOnServer(topologyId)
} catch (error) {
console.error(error)
@@ -182,7 +147,7 @@ export function* onDeleteRoom() {
export function* onEditRackName(action) {
try {
const topologyId = yield select((state) => state.currentTopologyId)
- const rackId = yield select((state) => state.objects.tile[state.interactionLevel.tileId].rackId)
+ const rackId = yield select((state) => state.objects.tile[state.interactionLevel.tileId].rack)
const rack = Object.assign({}, yield select((state) => state.objects.rack[rackId]))
rack.name = action.name
yield put(addPropToStoreObject('rack', rackId, { name: action.name }))
@@ -197,7 +162,7 @@ export function* onDeleteRack() {
const topologyId = yield select((state) => state.currentTopologyId)
const tileId = yield select((state) => state.interactionLevel.tileId)
yield put(goDownOneInteractionLevel())
- yield put(addPropToStoreObject('tile', tileId, { rackId: undefined }))
+ yield put(addPropToStoreObject('tile', tileId, { rack: undefined }))
yield updateTopologyOnServer(topologyId)
} catch (error) {
console.error(error)
@@ -212,10 +177,10 @@ export function* onAddRackToTile(action) {
name: 'Rack',
capacity: DEFAULT_RACK_SLOT_CAPACITY,
powerCapacityW: DEFAULT_RACK_POWER_CAPACITY,
+ machines: [],
}
- rack.machineIds = new Array(rack.capacity).fill(null)
yield put(addToStore('rack', rack))
- yield put(addPropToStoreObject('tile', action.tileId, { rackId: rack._id }))
+ yield put(addPropToStoreObject('tile', action.tileId, { rack: rack._id }))
yield updateTopologyOnServer(topologyId)
} catch (error) {
console.error(error)
@@ -225,23 +190,21 @@ export function* onAddRackToTile(action) {
export function* onAddMachine(action) {
try {
const topologyId = yield select((state) => state.currentTopologyId)
- const rackId = yield select((state) => state.objects.tile[state.interactionLevel.tileId].rackId)
+ const rackId = yield select((state) => state.objects.tile[state.interactionLevel.tileId].rack)
const rack = yield select((state) => state.objects.rack[rackId])
const machine = {
_id: uuid(),
- rackId,
position: action.position,
- cpuIds: [],
- gpuIds: [],
- memoryIds: [],
- storageIds: [],
+ cpus: [],
+ gpus: [],
+ memories: [],
+ storages: [],
}
yield put(addToStore('machine', machine))
- const machineIds = [...rack.machineIds]
- machineIds[machine.position - 1] = machine._id
- yield put(addPropToStoreObject('rack', rackId, { machineIds }))
+ const machineIds = [...rack.machines, machine._id]
+ yield put(addPropToStoreObject('rack', rackId, { machines: machineIds }))
yield updateTopologyOnServer(topologyId)
} catch (error) {
console.error(error)
@@ -253,35 +216,41 @@ export function* onDeleteMachine() {
const topologyId = yield select((state) => state.currentTopologyId)
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].rackId])
- const machineIds = [...rack.machineIds]
- machineIds[position - 1] = null
+ const rack = yield select((state) => state.objects.rack[state.objects.tile[tileId].rack])
yield put(goDownOneInteractionLevel())
- yield put(addPropToStoreObject('rack', rack._id, { machineIds }))
+ yield put(
+ addPropToStoreObject('rack', rack._id, { machines: rack.machines.filter((_, idx) => idx !== position - 1) })
+ )
yield updateTopologyOnServer(topologyId)
} catch (error) {
console.error(error)
}
}
+const unitMapping = {
+ cpu: 'cpus',
+ gpu: 'gpus',
+ memory: 'memories',
+ storage: 'storages',
+}
+
export function* onAddUnit(action) {
try {
const topologyId = yield select((state) => state.currentTopologyId)
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].rackId].machineIds[position - 1]]
+ (state) => state.objects.machine[state.objects.rack[state.objects.tile[tileId].rack].machines[position - 1]]
)
- if (machine[action.unitType + 'Ids'].length >= MAX_NUM_UNITS_PER_MACHINE) {
+ if (machine[unitMapping[action.unitType]].length >= MAX_NUM_UNITS_PER_MACHINE) {
return
}
- const units = [...machine[action.unitType + 'Ids'], action.id]
+ const units = [...machine[unitMapping[action.unitType]], action.id]
yield put(
addPropToStoreObject('machine', machine._id, {
- [action.unitType + 'Ids']: units,
+ [unitMapping[action.unitType]]: units,
})
)
yield updateTopologyOnServer(topologyId)
@@ -296,15 +265,14 @@ export function* onDeleteUnit(action) {
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].rackId].machineIds[position - 1]]
+ (state) => state.objects.machine[state.objects.rack[state.objects.tile[tileId].rack].machines[position - 1]]
)
- const unitIds = machine[action.unitType + 'Ids'].slice()
+ const unitIds = machine[unitMapping[action.unitType]].slice()
unitIds.splice(action.index, 1)
yield put(
addPropToStoreObject('machine', machine._id, {
- [action.unitType + 'Ids']: unitIds,
+ [unitMapping[action.unitType]]: unitIds,
})
)
yield updateTopologyOnServer(topologyId)