diff options
26 files changed, 328 insertions, 75 deletions
diff --git a/package.json b/package.json index 10f9fa7c..25d27b02 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,6 @@ "isomorphic-fetch": "^2.2.1", "konva": "^1.6.7", "node-sass-chokidar": "^0.0.3", - "normalizr": "^3.2.3", "npm-run-all": "^4.0.2", "prop-types": "^15.5.10", "react": "^15.6.1", diff --git a/src/actions/objects.js b/src/actions/objects.js index 0a0e3046..6fdb83cd 100644 --- a/src/actions/objects.js +++ b/src/actions/objects.js @@ -1,4 +1,5 @@ export const ADD_TO_STORE = "ADD_TO_STORE"; +export const ADD_PROP_TO_STORE_OBJECT = "ADD_PROP_TO_STORE_OBJECT"; export function addToStore(objectType, object) { return { @@ -7,3 +8,12 @@ export function addToStore(objectType, object) { object }; } + +export function addPropToStoreObject(objectType, objectId, propObject) { + return { + type: ADD_PROP_TO_STORE_OBJECT, + objectType, + objectId, + propObject + }; +} diff --git a/src/actions/topology.js b/src/actions/topology.js new file mode 100644 index 00000000..de742bb1 --- /dev/null +++ b/src/actions/topology.js @@ -0,0 +1,21 @@ +export const FETCH_TOPOLOGY_OF_DATACENTER = "FETCH_TOPOLOGY_OF_DATACENTER"; +export const FETCH_TOPOLOGY_OF_DATACENTER_SUCCEEDED = "FETCH_TOPOLOGY_OF_DATACENTER_SUCCEEDED"; +export const FETCH_LATEST_DATACENTER = "FETCH_LATEST_DATACENTER"; +export const FETCH_LATEST_DATACENTER_SUCCEEDED = "FETCH_LATEST_DATACENTER_SUCCEEDED"; + +export function fetchLatestDatacenter() { + return (dispatch, getState) => { + const {currentSimulationId} = getState(); + dispatch({ + type: FETCH_LATEST_DATACENTER, + currentSimulationId + }); + }; +} + +export function fetchLatestDatacenterSucceeded(datacenterId) { + return { + type: FETCH_LATEST_DATACENTER_SUCCEEDED, + datacenterId + }; +} diff --git a/src/api/routes/rooms.js b/src/api/routes/rooms.js index e08cc6e7..1a7a7453 100644 --- a/src/api/routes/rooms.js +++ b/src/api/routes/rooms.js @@ -1,4 +1,5 @@ import {sendRequest} from "../index"; +import {deleteById, getById} from "./util"; export function getRoom(roomId) { return getById("/rooms/{roomId}", {roomId}); diff --git a/src/api/sagas/objects.js b/src/api/sagas/objects.js deleted file mode 100644 index 98c766ec..00000000 --- a/src/api/sagas/objects.js +++ /dev/null @@ -1,24 +0,0 @@ -import {call, put, select} from "redux-saga/effects"; -import {addToStore} from "../../actions/objects"; -import {getSimulation} from "../routes/simulations"; -import {getUser} from "../routes/users"; - -const selectors = { - simulation: state => state.objects.simulation, - user: state => state.objects.user, - authorization: state => state.objects.authorization, -}; - -function* fetchAndStoreObject(objectType, id, apiCall) { - const objectStore = yield select(selectors[objectType]); - if (!objectStore[id]) { - const object = yield apiCall; - yield put(addToStore(objectType, object)); - } -} - -export const fetchAndStoreSimulation = (id) => - fetchAndStoreObject("simulation", id, call(getSimulation, id)); - -export const fetchAndStoreUser = (id) => - fetchAndStoreObject("user", id, call(getUser, id),); diff --git a/src/components/map/MapStage.js b/src/components/map/MapStage.js index 0950d5bd..879c7c39 100644 --- a/src/components/map/MapStage.js +++ b/src/components/map/MapStage.js @@ -1,8 +1,8 @@ import React from "react"; import {Group, Layer, Stage} from "react-konva"; +import DatacenterContainer from "../../containers/map/DatacenterContainer"; import jQuery from "../../util/jquery"; import Backdrop from "./elements/Backdrop"; -import DatacenterGroup from "./groups/DatacenterGroup"; import GridGroup from "./groups/GridGroup"; import {MAP_SIZE_IN_PIXELS} from "./MapConstants"; @@ -28,7 +28,7 @@ class MapStage extends React.Component { this.setState({width: jQuery(window).width(), height: jQuery(window).height()}); } - dragBoundHandler(pos) { + dragBoundFunc(pos) { return { x: pos.x > 0 ? 0 : (pos.x < -MAP_SIZE_IN_PIXELS + this.state.width ? -MAP_SIZE_IN_PIXELS + this.state.width : pos.x), @@ -41,9 +41,9 @@ class MapStage extends React.Component { return ( <Stage width={this.state.width} height={this.state.height}> <Layer> - <Group draggable={true} dragBoundFunc={this.dragBoundHandler.bind(this)}> + <Group draggable={true} dragBoundFunc={this.dragBoundFunc.bind(this)}> <Backdrop/> - <DatacenterGroup/> + <DatacenterContainer/> <GridGroup/> </Group> </Layer> diff --git a/src/components/map/elements/RoomTile.js b/src/components/map/elements/RoomTile.js index aa837def..759dcf35 100644 --- a/src/components/map/elements/RoomTile.js +++ b/src/components/map/elements/RoomTile.js @@ -2,6 +2,7 @@ import React from "react"; import {Rect} from "react-konva"; import {ROOM_DEFAULT_COLOR} from "../../../colors/index"; import Shapes from "../../../shapes/index"; +import {TILE_SIZE_IN_PIXELS} from "../MapConstants"; const RoomTile = ({tile}) => ( <Rect diff --git a/src/components/map/elements/TileObject.js b/src/components/map/elements/TileObject.js index 8703011c..1517ef97 100644 --- a/src/components/map/elements/TileObject.js +++ b/src/components/map/elements/TileObject.js @@ -3,7 +3,7 @@ import React from "react"; import {Rect} from "react-konva"; import {OBJECT_BORDER_COLOR, ROOM_DEFAULT_COLOR} from "../../../colors/index"; import Shapes from "../../../shapes/index"; -import {OBJECT_BORDER_WIDTH_IN_PIXELS, OBJECT_MARGIN_IN_PIXELS} from "../MapConstants"; +import {OBJECT_BORDER_WIDTH_IN_PIXELS, OBJECT_MARGIN_IN_PIXELS, TILE_SIZE_IN_PIXELS} from "../MapConstants"; const TileObject = ({tile, color}) => ( <Rect diff --git a/src/components/map/groups/DatacenterGroup.js b/src/components/map/groups/DatacenterGroup.js index 3b7a086b..d7e349be 100644 --- a/src/components/map/groups/DatacenterGroup.js +++ b/src/components/map/groups/DatacenterGroup.js @@ -1,14 +1,20 @@ import React from "react"; import {Group} from "react-konva"; +import Shapes from "../../../shapes/index"; import RoomGroup from "./RoomGroup"; -const DatacenterGroup = ({datacenter}) => ( - <Group> - {datacenter.rooms.map(room => ( - <RoomGroup room={room}/> - ))} - </Group> -); +const DatacenterGroup = ({datacenter}) => { + if (!datacenter) { + return <Group/>; + } + return ( + <Group> + {datacenter.rooms.map(room => ( + <RoomGroup key={room.id} room={room}/> + ))} + </Group> + ); +}; DatacenterGroup.propTypes = { datacenter: Shapes.Datacenter, diff --git a/src/components/map/groups/GridGroup.js b/src/components/map/groups/GridGroup.js index 2651bf19..f50482ce 100644 --- a/src/components/map/groups/GridGroup.js +++ b/src/components/map/groups/GridGroup.js @@ -15,11 +15,12 @@ const VERTICAL_POINT_PAIRS = MAP_COORDINATE_ENTRIES.map(index => [ const GridGroup = () => ( <Group> - {HORIZONTAL_POINT_PAIRS.concat(VERTICAL_POINT_PAIRS).map(points => ( + {HORIZONTAL_POINT_PAIRS.concat(VERTICAL_POINT_PAIRS).map((points, index) => ( <Line points={points} stroke={GRID_COLOR} strokeWidth={GRID_LINE_WIDTH_IN_PIXELS} + key={index} /> ))} </Group> diff --git a/src/components/map/groups/RoomGroup.js b/src/components/map/groups/RoomGroup.js index 90a58767..28240d77 100644 --- a/src/components/map/groups/RoomGroup.js +++ b/src/components/map/groups/RoomGroup.js @@ -1,11 +1,12 @@ import React from "react"; import {Group} from "react-konva"; +import Shapes from "../../../shapes/index"; import TileGroup from "./TileGroup"; const RoomGroup = ({room}) => ( <Group> {room.tiles.map(tile => ( - <TileGroup tile={tile}/> + <TileGroup key={tile.id} tile={tile}/> ))} </Group> ); diff --git a/src/containers/map/DatacenterContainer.js b/src/containers/map/DatacenterContainer.js new file mode 100644 index 00000000..1716f22d --- /dev/null +++ b/src/containers/map/DatacenterContainer.js @@ -0,0 +1,22 @@ +import {connect} from "react-redux"; +import DatacenterGroup from "../../components/map/groups/DatacenterGroup"; +import {denormalize} from "../../store/denormalizer"; + +const mapStateToProps = state => { + if (state.currentDatacenterId === -1) { + return {}; + } + + const datacenter = denormalize(state, "datacenter", state.currentDatacenterId); + console.log(datacenter); + + return { + datacenter + }; +}; + +const DatacenterContainer = connect( + mapStateToProps +)(DatacenterGroup); + +export default DatacenterContainer; diff --git a/src/pages/App.js b/src/pages/App.js index a2a1050b..f34c03cd 100644 --- a/src/pages/App.js +++ b/src/pages/App.js @@ -2,7 +2,7 @@ import PropTypes from "prop-types"; import React from 'react'; import {connect} from "react-redux"; import {openSimulationSucceeded} from "../actions/simulations"; -import {fetchAuthorizationsOfCurrentUser} from "../actions/users"; +import {fetchLatestDatacenter} from "../actions/topology"; import MapStage from "../components/map/MapStage"; import Navbar from "../components/navigation/Navbar"; import Login from "../containers/auth/Login"; @@ -14,7 +14,7 @@ class AppContainer extends React.Component { componentDidMount() { this.props.storeSimulationId(this.props.simulationId); - this.props.fetchAuthorizationsOfCurrentUser(); + this.props.fetchLatestDatacenter(); } render() { @@ -33,7 +33,7 @@ class AppContainer extends React.Component { const mapDispatchToProps = dispatch => { return { storeSimulationId: id => dispatch(openSimulationSucceeded(id)), - fetchAuthorizationsOfCurrentUser: () => dispatch(fetchAuthorizationsOfCurrentUser()), + fetchLatestDatacenter: () => dispatch(fetchLatestDatacenter()), }; }; diff --git a/src/pages/Simulations.js b/src/pages/Simulations.js index c46cb621..8218b1a4 100644 --- a/src/pages/Simulations.js +++ b/src/pages/Simulations.js @@ -15,10 +15,6 @@ class SimulationsContainer extends React.Component { this.props.fetchAuthorizationsOfCurrentUser(); } - onInputSubmission(text) { - this.props.addSimulation(text); - } - render() { return ( <div className="full-height"> diff --git a/src/reducers/index.js b/src/reducers/index.js index 40a51a04..287a9762 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -2,7 +2,8 @@ import {combineReducers} from "redux"; import {auth} from "./auth"; import {modals} from "./modals"; import {objects} from "./objects"; -import {authorizationsOfCurrentUser, authVisibilityFilter} from "./simulations"; +import {authorizationsOfCurrentUser, authVisibilityFilter, currentSimulationId} from "./simulations"; +import {currentDatacenterId} from "./topology"; const rootReducer = combineReducers({ auth, @@ -10,6 +11,8 @@ const rootReducer = combineReducers({ modals, authorizationsOfCurrentUser, authVisibilityFilter, + currentSimulationId, + currentDatacenterId, }); export default rootReducer; diff --git a/src/reducers/objects.js b/src/reducers/objects.js index 60cd2711..4fbffea6 100644 --- a/src/reducers/objects.js +++ b/src/reducers/objects.js @@ -1,5 +1,5 @@ import {combineReducers} from "redux"; -import {ADD_TO_STORE} from "../actions/objects"; +import {ADD_PROP_TO_STORE_OBJECT, ADD_TO_STORE} from "../actions/objects"; export const objects = combineReducers({ simulation: object("simulation"), @@ -27,14 +27,22 @@ function object(type) { function objectWithId(type, getId) { return (state = {}, action) => { - if (action.type === ADD_TO_STORE) { - if (action.objectType === type) { - return Object.assign( - state, - {[getId(action.object)]: action.object} - ); - } + if (action.objectType !== type) { return state; } + + if (action.type === ADD_TO_STORE) { + return Object.assign( + state, + {[getId(action.object)]: action.object} + ); + } else if (action.type === ADD_PROP_TO_STORE_OBJECT) { + return Object.assign( + state, + {[action.objectId]: Object.assign(state[action.objectId], action.propObject)} + ); + } + + return state; }; } diff --git a/src/reducers/topology.js b/src/reducers/topology.js new file mode 100644 index 00000000..caafb7c1 --- /dev/null +++ b/src/reducers/topology.js @@ -0,0 +1,10 @@ +import {FETCH_LATEST_DATACENTER_SUCCEEDED} from "../actions/topology"; + +export function currentDatacenterId(state = -1, action) { + switch (action.type) { + case FETCH_LATEST_DATACENTER_SUCCEEDED: + return action.datacenterId; + default: + return state; + } +} diff --git a/src/api/sagas/index.js b/src/sagas/index.js index 7fe57453..c6177cd4 100644 --- a/src/api/sagas/index.js +++ b/src/sagas/index.js @@ -1,9 +1,11 @@ import {takeEvery} from "redux-saga/effects"; -import {LOG_IN} from "../../actions/auth"; -import {ADD_SIMULATION, DELETE_SIMULATION} from "../../actions/simulations"; -import {DELETE_CURRENT_USER, FETCH_AUTHORIZATIONS_OF_CURRENT_USER} from "../../actions/users"; +import {LOG_IN} from "../actions/auth"; +import {ADD_SIMULATION, DELETE_SIMULATION} from "../actions/simulations"; +import {FETCH_LATEST_DATACENTER} from "../actions/topology"; +import {DELETE_CURRENT_USER, FETCH_AUTHORIZATIONS_OF_CURRENT_USER} from "../actions/users"; import {onDeleteCurrentUser} from "./profile"; import {onSimulationAdd, onSimulationDelete} from "./simulations"; +import {onFetchLatestDatacenter} from "./topology"; import {onFetchAuthorizationsOfCurrentUser, onFetchLoggedInUser} from "./users"; export default function* rootSaga() { @@ -12,4 +14,5 @@ export default function* rootSaga() { yield takeEvery(ADD_SIMULATION, onSimulationAdd); yield takeEvery(DELETE_SIMULATION, onSimulationDelete); yield takeEvery(DELETE_CURRENT_USER, onDeleteCurrentUser); + yield takeEvery(FETCH_LATEST_DATACENTER, onFetchLatestDatacenter); } diff --git a/src/sagas/objects.js b/src/sagas/objects.js new file mode 100644 index 00000000..5fac6c3e --- /dev/null +++ b/src/sagas/objects.js @@ -0,0 +1,109 @@ +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 {getSection} from "../api/routes/sections"; +import {getPathsOfSimulation, getSimulation} from "../api/routes/simulations"; +import { + getCoolingItem, + getCPU, + getFailureModel, + getGPU, + getMemory, + getPSU, + getStorage +} from "../api/routes/specifications"; +import {getMachinesOfRackByTile, getRackByTile} from "../api/routes/tiles"; +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]); + if (!objectStore[id]) { + const object = yield apiCall; + yield put(addToStore(objectType, object)); + } + return objectStore[id]; +} + +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 fetchAndStoreCPU = (id) => + fetchAndStoreObject("cpu", id, call(getCPU, id)); + +export const fetchAndStoreGPU = (id) => + fetchAndStoreObject("gpu", id, call(getGPU, id)); + +export const fetchAndStoreMemory = (id) => + fetchAndStoreObject("memory", id, call(getMemory, id)); + +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)); diff --git a/src/api/sagas/profile.js b/src/sagas/profile.js index 3c4e1825..6a72e7c2 100644 --- a/src/api/sagas/profile.js +++ b/src/sagas/profile.js @@ -1,6 +1,6 @@ import {call, put} from "redux-saga/effects"; -import {deleteCurrentUserSucceeded} from "../../actions/users"; -import {deleteUser} from "../routes/users"; +import {deleteCurrentUserSucceeded} from "../actions/users"; +import {deleteUser} from "../api/routes/users"; export function* onDeleteCurrentUser(action) { try { diff --git a/src/api/sagas/simulations.js b/src/sagas/simulations.js index 6b7471c0..b699002e 100644 --- a/src/api/sagas/simulations.js +++ b/src/sagas/simulations.js @@ -1,7 +1,7 @@ import {call, put} from "redux-saga/effects"; -import {addToStore} from "../../actions/objects"; -import {addSimulationSucceeded, deleteSimulationSucceeded} from "../../actions/simulations"; -import {addSimulation, deleteSimulation} from "../routes/simulations"; +import {addToStore} from "../actions/objects"; +import {addSimulationSucceeded, deleteSimulationSucceeded} from "../actions/simulations"; +import {addSimulation, deleteSimulation} from "../api/routes/simulations"; export function* onSimulationAdd(action) { try { diff --git a/src/sagas/topology.js b/src/sagas/topology.js new file mode 100644 index 00000000..6d359534 --- /dev/null +++ b/src/sagas/topology.js @@ -0,0 +1,71 @@ +import {put} from "redux-saga/effects"; +import {addPropToStoreObject} from "../actions/objects"; +import {fetchLatestDatacenterSucceeded} from "../actions/topology"; +import { + fetchAndStoreCoolingItem, + fetchAndStoreDatacenter, + fetchAndStorePathsOfSimulation, + fetchAndStorePSU, + fetchAndStoreRackOnTile, + fetchAndStoreRoomsOfDatacenter, + fetchAndStoreSectionsOfPath, + fetchAndStoreTilesOfRoom +} from "./objects"; + +export function* onFetchLatestDatacenter(action) { + try { + const paths = yield fetchAndStorePathsOfSimulation(action.currentSimulationId); + const latestPath = paths[paths.length - 1]; + const sections = yield fetchAndStoreSectionsOfPath(latestPath.id); + const latestSection = sections[sections.length - 1]; + yield fetchDatacenter(latestSection.datacenterId); + yield put(fetchLatestDatacenterSucceeded(latestSection.datacenterId)); + } catch (error) { + console.log(error); + } +} + +export function* fetchDatacenter(datacenterId) { + try { + const datacenter = yield fetchAndStoreDatacenter(datacenterId); + datacenter.roomIds = (yield fetchAndStoreRoomsOfDatacenter(datacenterId)).map(room => room.id); + + for (let index in datacenter.roomIds) { + yield fetchRoom(datacenter.roomIds[index]); + } + } catch (error) { + console.log(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; + } + console.log(tile); + switch (tile.objectType) { + case "RACK": + const rack = yield fetchAndStoreRackOnTile(tile.objectId, tile.id); + yield put(addPropToStoreObject("tile", tile.id, {rackId: rack.id})); + 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 object type encountered while fetching tile objects"); + } +} diff --git a/src/api/sagas/users.js b/src/sagas/users.js index d3bc3f5f..5f9bffa1 100644 --- a/src/api/sagas/users.js +++ b/src/sagas/users.js @@ -1,10 +1,10 @@ import {call, put} from "redux-saga/effects"; -import {logInSucceeded} from "../../actions/auth"; -import {addToAuthorizationStore} from "../../actions/objects"; -import {fetchAuthorizationsOfCurrentUserSucceeded} from "../../actions/users"; -import {saveAuthLocalStorage} from "../../auth/index"; -import {performTokenSignIn} from "../routes/token-signin"; -import {addUser, getAuthorizationsByUser} from "../routes/users"; +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) { @@ -29,7 +29,7 @@ export function* onFetchAuthorizationsOfCurrentUser(action) { const authorizations = yield call(getAuthorizationsByUser, action.userId); for (const authorization of authorizations) { - yield put(addToAuthorizationStore(authorization)); + yield put(addToStore("authorization", authorization)); yield fetchAndStoreSimulation(authorization.simulationId); yield fetchAndStoreUser(authorization.userId); diff --git a/src/shapes/index.js b/src/shapes/index.js index 1938032e..bea0f1aa 100644 --- a/src/shapes/index.js +++ b/src/shapes/index.js @@ -101,7 +101,7 @@ Shapes.Tile = PropTypes.shape({ positionX: PropTypes.number.isRequired, positionY: PropTypes.number.isRequired, objectId: PropTypes.number, - objectType: PropTypes.number, + objectType: PropTypes.string, rack: Shapes.Rack, coolingItem: Shapes.CoolingItem, psu: Shapes.PSU, @@ -123,6 +123,7 @@ Shapes.Datacenter = PropTypes.shape({ Shapes.Section = PropTypes.shape({ id: PropTypes.number.isRequired, pathId: PropTypes.number.isRequired, + startTick: PropTypes.number.isRequired, datacenterId: PropTypes.number.isRequired, datacenter: Shapes.Datacenter, }); diff --git a/src/store/configureStore.js b/src/store/configureStore.js index ecd804a2..5bbaf811 100644 --- a/src/store/configureStore.js +++ b/src/store/configureStore.js @@ -3,9 +3,9 @@ import persistState from "redux-localstorage"; import {createLogger} from "redux-logger"; import createSagaMiddleware from 'redux-saga'; import thunk from "redux-thunk"; -import rootSaga from "../api/sagas/index"; import {authRedirectMiddleware} from "../auth/index"; import rootReducer from "../reducers/index"; +import rootSaga from "../sagas/index"; const sagaMiddleware = createSagaMiddleware(); const logger = createLogger(); diff --git a/src/store/denormalizer.js b/src/store/denormalizer.js index fbf15430..e6583ae7 100644 --- a/src/store/denormalizer.js +++ b/src/store/denormalizer.js @@ -1,14 +1,28 @@ +const EXCLUDED_IDENTIFIERS = [ + "objectId", + "googleId", +]; + export function denormalize(state, objectType, id) { + return denormalizeWithRecursionCheck(state, objectType, id, undefined); +} + +function denormalizeWithRecursionCheck(state, objectType, id, previousType) { const object = Object.assign({}, state.objects[objectType][id]); for (let prop in object) { - if (!object.hasOwnProperty(prop)) { + if (prop.indexOf(previousType) !== -1) { continue; } - if (prop.endsWith("Id")) { + if (prop.endsWith("Id") && EXCLUDED_IDENTIFIERS.indexOf(prop) === -1) { const propType = prop.replace("Id", ""); - object[propType] = state.objects[propType][object[prop]]; + object[propType] = denormalizeWithRecursionCheck(state, propType, object[prop], objectType); + } + + if (prop.endsWith("Ids")) { + const propType = prop.replace("Ids", ""); + object[propType + "s"] = object[prop].map(id => denormalizeWithRecursionCheck(state, propType, id, objectType)); } } |
