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 13:15:28 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2021-07-08 13:20:27 +0200
commit29196842447d841d2e21462adcfc8c2ed1d851ad (patch)
treedf2945e6a9f7ec2d32acf9c9c7bcf8e8b4a322d6 /opendc-web/opendc-web-ui/src/redux/sagas
parent1157cc3c56c0f8d36be277bd1ea633f03dd7abbf (diff)
ui: Simplify normalization of topology
This change updates the OpenDC frontend to use the normalizr library for normalizing the user topology.
Diffstat (limited to 'opendc-web/opendc-web-ui/src/redux/sagas')
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/objects.js186
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js11
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/topology.js74
3 files changed, 58 insertions, 213 deletions
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 99082df0..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,124 +1,27 @@
import { call, put, select, getContext } from 'redux-saga/effects'
-import { addToStore } from '../actions/objects'
import { fetchTopology, updateTopology } from '../../api/topologies'
-import { uuid } from 'uuidv4'
-
-export const OBJECT_SELECTORS = {
- 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,
-}
+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(fetchTopology, 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 denormalizeTopology(id, true)
+ const topology = yield denormalizeTopology(id)
const auth = yield getContext('auth')
yield call(updateTopology, auth, topology)
}
@@ -126,73 +29,8 @@ export const updateTopologyOnServer = function* (id) {
/**
* Denormalizes the topology representation in order to be stored on the server.
*/
-export const denormalizeTopology = 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 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/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/topology.js b/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
index 0ed40131..5d9154fd 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
@@ -25,8 +25,8 @@ export function* fetchAndStoreAllTopologiesOfProject(projectId, setTopology = fa
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) {
@@ -43,8 +43,9 @@ export function* onAddTopology(action) {
let topologyToBeCreated
if (duplicateId) {
- topologyToBeCreated = yield denormalizeTopology(duplicateId, false)
+ topologyToBeCreated = yield denormalizeTopology(duplicateId)
topologyToBeCreated = { ...topologyToBeCreated, name }
+ delete topologyToBeCreated._id
} else {
topologyToBeCreated = { name: action.name, rooms: [] }
}
@@ -65,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) {
@@ -80,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())
@@ -100,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)
@@ -111,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)
@@ -136,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)
@@ -146,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 }))
@@ -161,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)
@@ -176,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)
@@ -189,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)
@@ -217,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)
@@ -260,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)