summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2021-07-07 16:27:49 +0200
committerFabian Mastenbroek <mail.fabianm@gmail.com>2021-07-07 16:28:25 +0200
commite5e5d2c65e583493870bc0b62fb185c5e757c13f (patch)
treedc9bc25517ce36e155932212f598bf2375519d34 /opendc-web/opendc-web-ui/src
parentaa788a3ad18badfac8beaabdaffc88b9e52f9306 (diff)
ui: Migrate project APIs to React Query
This change updates the OpenDC frontend to use React Query for fetching and mutating project data. Previously, this state was tracked and synchronized via Redux. Migrating to React Query greatly simplifies the state synchronization logic necessary in the frontend.
Diffstat (limited to 'opendc-web/opendc-web-ui/src')
-rw-r--r--opendc-web/opendc-web-ui/src/api/projects.js4
-rw-r--r--opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js2
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js5
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js6
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js4
-rw-r--r--opendc-web/opendc-web-ui/src/containers/navigation/AppNavbarContainer.js5
-rw-r--r--opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/projects/ProjectActions.js13
-rw-r--r--opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js5
-rw-r--r--opendc-web/opendc-web-ui/src/data/project.js22
-rw-r--r--opendc-web/opendc-web-ui/src/data/topology.js10
-rw-r--r--opendc-web/opendc-web-ui/src/pages/_app.js13
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js4
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/index.js7
-rw-r--r--opendc-web/opendc-web-ui/src/redux/actions/projects.js47
-rw-r--r--opendc-web/opendc-web-ui/src/redux/reducers/index.js2
-rw-r--r--opendc-web/opendc-web-ui/src/redux/reducers/interaction-level.js2
-rw-r--r--opendc-web/opendc-web-ui/src/redux/reducers/projects.js14
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/index.js6
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/objects.js7
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js29
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/projects.js43
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js7
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/topology.js30
24 files changed, 96 insertions, 204 deletions
diff --git a/opendc-web/opendc-web-ui/src/api/projects.js b/opendc-web/opendc-web-ui/src/api/projects.js
index 93052080..4123b371 100644
--- a/opendc-web/opendc-web-ui/src/api/projects.js
+++ b/opendc-web/opendc-web-ui/src/api/projects.js
@@ -22,11 +22,11 @@
import { request } from './index'
-export function getProjects(auth) {
+export function fetchProjects(auth) {
return request(auth, `projects/`)
}
-export function getProject(auth, projectId) {
+export function fetchProject(auth, projectId) {
return request(auth, `projects/${projectId}`)
}
diff --git a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js
index b948b747..d61ff24e 100644
--- a/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js
+++ b/opendc-web/opendc-web-ui/src/components/app/sidebars/project/PortfolioListComponent.js
@@ -25,7 +25,7 @@ function PortfolioListComponent({
</Button>
</h2>
- {portfolios.map((portfolio, idx) => (
+ {portfolios.map((portfolio) => (
<div key={portfolio._id}>
<Row className="row mb-1">
<Col
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js
index b5bade98..1b539b8f 100644
--- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/PortfolioListContainer.js
@@ -6,11 +6,12 @@ import { addPortfolio, deletePortfolio } from '../../../../redux/actions/portfol
import { getState } from '../../../../util/state-utils'
import { setCurrentTopology } from '../../../../redux/actions/topology/building'
import NewPortfolioModalComponent from '../../../../components/modals/custom-components/NewPortfolioModalComponent'
-import { usePortfolios } from '../../../../data/project'
+import { useActivePortfolioId, useActiveProjectId, usePortfolios, useProject } from '../../../../data/project'
const PortfolioListContainer = () => {
const router = useRouter()
const { project: currentProjectId, portfolio: currentPortfolioId } = router.query
+ const { data: currentProject } = useProject(currentProjectId)
const portfolios = usePortfolios(currentProjectId)
const dispatch = useDispatch()
@@ -24,7 +25,7 @@ const PortfolioListContainer = () => {
if (id) {
const state = await getState(dispatch)
dispatch(deletePortfolio(id))
- dispatch(setCurrentTopology(state.objects.project[currentProjectId].topologyIds[0]))
+ dispatch(setCurrentTopology(currentProject.topologyIds[0]))
await router.push(`/projects/${currentProjectId}`)
}
},
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js
index 0eb61026..c474c56e 100644
--- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js
@@ -7,11 +7,8 @@ import NewScenarioModalComponent from '../../../../components/modals/custom-comp
import { useProjectTopologies } from '../../../../data/topology'
import { useScenarios } from '../../../../data/project'
import { useSchedulers, useTraces } from '../../../../data/experiments'
-import { useRouter } from 'next/router'
const ScenarioListContainer = ({ portfolioId }) => {
- const router = useRouter()
- const { project: currentProjectId } = router.query
const scenarios = useScenarios(portfolioId)
const topologies = useProjectTopologies()
const traces = useTraces()
@@ -23,7 +20,6 @@ const ScenarioListContainer = ({ portfolioId }) => {
const onNewScenario = (currentPortfolioId) => {
setVisible(true)
}
- const onChooseScenario = (portfolioId, scenarioId) => {}
const onDeleteScenario = (id) => {
if (id) {
dispatch(deleteScenario(id))
@@ -67,7 +63,7 @@ const ScenarioListContainer = ({ portfolioId }) => {
}
ScenarioListContainer.propTypes = {
- portfolioId: PropTypes.string.isRequired,
+ portfolioId: PropTypes.string,
}
export default ScenarioListContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js
index 69367b5f..55f8bd00 100644
--- a/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js
+++ b/opendc-web/opendc-web-ui/src/containers/app/sidebars/project/TopologyListContainer.js
@@ -6,11 +6,13 @@ import { useRouter } from 'next/router'
import { addTopology, deleteTopology } from '../../../../redux/actions/topologies'
import NewTopologyModalComponent from '../../../../components/modals/custom-components/NewTopologyModalComponent'
import { useActiveTopology, useProjectTopologies } from '../../../../data/topology'
+import { useProject } from '../../../../data/project'
const TopologyListContainer = () => {
const dispatch = useDispatch()
const router = useRouter()
const { project: currentProjectId } = router.query
+ const { data: currentProject } = useProject(currentProjectId)
const topologies = useProjectTopologies()
const currentTopologyId = useActiveTopology()?._id
const [isVisible, setVisible] = useState(false)
@@ -22,7 +24,7 @@ const TopologyListContainer = () => {
const onDeleteTopology = async (id) => {
if (id) {
dispatch(deleteTopology(id))
- dispatch(setCurrentTopology(state.objects.project[currentProjectId].topologyIds[0]))
+ dispatch(setCurrentTopology(currentProject.topologyIds[0]))
await router.push(`/projects/${currentProjectId}`)
}
}
diff --git a/opendc-web/opendc-web-ui/src/containers/navigation/AppNavbarContainer.js b/opendc-web/opendc-web-ui/src/containers/navigation/AppNavbarContainer.js
index 6742bc26..ff9f9fe7 100644
--- a/opendc-web/opendc-web-ui/src/containers/navigation/AppNavbarContainer.js
+++ b/opendc-web/opendc-web-ui/src/containers/navigation/AppNavbarContainer.js
@@ -1,9 +1,10 @@
import React from 'react'
import AppNavbarComponent from '../../components/navigation/AppNavbarComponent'
-import { useActiveProject } from '../../data/project'
+import { useActiveProjectId, useProject } from '../../data/project'
const AppNavbarContainer = (props) => {
- const project = useActiveProject()
+ const projectId = useActiveProjectId()
+ const { data: project } = useProject(projectId)
return <AppNavbarComponent {...props} project={project} />
}
diff --git a/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js b/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js
index e03b5c07..c844fe2d 100644
--- a/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js
+++ b/opendc-web/opendc-web-ui/src/containers/projects/NewProjectContainer.js
@@ -1,20 +1,25 @@
import React, { useState } from 'react'
-import { useDispatch } from 'react-redux'
-import { addProject } from '../../redux/actions/projects'
import TextInputModal from '../../components/modals/TextInputModal'
import { Button } from 'reactstrap'
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faPlus } from '@fortawesome/free-solid-svg-icons'
+import { useMutation, useQueryClient } from 'react-query'
+import { addProject } from '../../api/projects'
+import { useAuth } from '../../auth'
/**
* A container for creating a new project.
*/
const NewProjectContainer = () => {
const [isVisible, setVisible] = useState(false)
- const dispatch = useDispatch()
+ const auth = useAuth()
+ const queryClient = useQueryClient()
+ const mutation = useMutation((data) => addProject(auth, data), {
+ onSuccess: (result) => queryClient.setQueryData('projects', (old) => [...(old || []), result]),
+ })
const callback = (text) => {
if (text) {
- dispatch(addProject(text))
+ mutation.mutate({ name: text })
}
setVisible(false)
}
diff --git a/opendc-web/opendc-web-ui/src/containers/projects/ProjectActions.js b/opendc-web/opendc-web-ui/src/containers/projects/ProjectActions.js
index bdb422dc..eba388d6 100644
--- a/opendc-web/opendc-web-ui/src/containers/projects/ProjectActions.js
+++ b/opendc-web/opendc-web-ui/src/containers/projects/ProjectActions.js
@@ -1,13 +1,18 @@
import React from 'react'
-import { useDispatch } from 'react-redux'
-import { deleteProject } from '../../redux/actions/projects'
import ProjectActionButtons from '../../components/projects/ProjectActionButtons'
+import { useMutation, useQueryClient } from 'react-query'
+import { useAuth } from '../../auth'
+import { deleteProject } from '../../api/projects'
const ProjectActions = (props) => {
- const dispatch = useDispatch()
+ const auth = useAuth()
+ const queryClient = useQueryClient()
+ const mutation = useMutation((projectId) => deleteProject(auth, projectId), {
+ onSuccess: () => queryClient.invalidateQueries('projects'),
+ })
const actions = {
onViewUsers: (id) => {}, // TODO implement user viewing
- onDelete: (id) => dispatch(deleteProject(id)),
+ onDelete: (id) => mutation.mutate(id),
}
return <ProjectActionButtons {...props} {...actions} />
}
diff --git a/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js b/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js
index 6632a8b5..91e8ac5a 100644
--- a/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js
+++ b/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js
@@ -3,6 +3,7 @@ import PropTypes from 'prop-types'
import ProjectList from '../../components/projects/ProjectList'
import { useAuth } from '../../auth'
import { useProjects } from '../../data/project'
+import { useQueryClient } from 'react-query'
const getVisibleProjects = (projects, filter, userId) => {
switch (filter) {
@@ -23,8 +24,8 @@ const getVisibleProjects = (projects, filter, userId) => {
const ProjectListContainer = ({ filter }) => {
const { user } = useAuth()
- const projects = useProjects()
- return <ProjectList projects={getVisibleProjects(projects, filter, user?.sub)} />
+ const { data: projects } = useProjects()
+ return <ProjectList projects={getVisibleProjects(projects ?? [], filter, user?.sub)} />
}
ProjectListContainer.propTypes = {
diff --git a/opendc-web/opendc-web-ui/src/data/project.js b/opendc-web/opendc-web-ui/src/data/project.js
index 30b36efa..308930e5 100644
--- a/opendc-web/opendc-web-ui/src/data/project.js
+++ b/opendc-web/opendc-web-ui/src/data/project.js
@@ -21,39 +21,43 @@
*/
import { useSelector } from 'react-redux'
+import { useQuery } from 'react-query'
+import { fetchProject, fetchProjects } from '../api/projects'
+import { useAuth } from '../auth'
import { useRouter } from 'next/router'
/**
* Return the available projects.
*/
export function useProjects() {
- return useSelector((state) => state.projects)
+ const auth = useAuth()
+ return useQuery('projects', () => fetchProjects(auth))
}
/**
* Return the project with the specified identifier.
*/
export function useProject(projectId) {
- return useSelector((state) => state.projects[projectId])
+ const auth = useAuth()
+ return useQuery(`projects/${projectId}`, () => fetchProject(auth, projectId), { enabled: !!projectId })
}
/**
- * Return the current active project.
+ * Return the current active project identifier.
*/
-export function useActiveProject() {
+export function useActiveProjectId() {
const router = useRouter()
- const { project: projectId } = router.query
- return useSelector((state) => state.objects.project[projectId])
+ const { project } = router.query
+ return project
}
/**
* Return the portfolios for the specified project id.
*/
export function usePortfolios(projectId) {
+ const { data: project } = useProject(projectId)
return useSelector((state) => {
- let portfolios = state.objects.project[projectId]
- ? state.objects.project[projectId].portfolioIds.map((t) => state.objects.portfolio[t])
- : []
+ let portfolios = project?.portfolioIds?.map((t) => state.objects.portfolio[t]) ?? []
if (portfolios.filter((t) => !t).length > 0) {
portfolios = []
}
diff --git a/opendc-web/opendc-web-ui/src/data/topology.js b/opendc-web/opendc-web-ui/src/data/topology.js
index f6ce1672..4c746a7e 100644
--- a/opendc-web/opendc-web-ui/src/data/topology.js
+++ b/opendc-web/opendc-web-ui/src/data/topology.js
@@ -21,7 +21,7 @@
*/
import { useSelector } from 'react-redux'
-import { useRouter } from 'next/router'
+import { useActiveProjectId, useProject } from './project'
/**
* Return the current active topology.
@@ -34,14 +34,14 @@ export function useActiveTopology() {
* Return the topologies for the active project.
*/
export function useProjectTopologies() {
- const router = useRouter()
- const { project: currentProjectId } = router.query
+ const projectId = useActiveProjectId()
+ const { data: project } = useProject(projectId)
return useSelector(({ objects }) => {
- if (!currentProjectId || !objects.project[currentProjectId]) {
+ if (!project) {
return []
}
- const topologies = objects.project[currentProjectId].topologyIds.map((t) => objects.topology[t])
+ const topologies = project.topologyIds.map((t) => objects.topology[t])
if (topologies.filter((t) => !t).length > 0) {
return []
diff --git a/opendc-web/opendc-web-ui/src/pages/_app.js b/opendc-web/opendc-web-ui/src/pages/_app.js
index c1adbd6e..7b4dcb3e 100644
--- a/opendc-web/opendc-web-ui/src/pages/_app.js
+++ b/opendc-web/opendc-web-ui/src/pages/_app.js
@@ -28,15 +28,20 @@ import '../index.scss'
import { AuthProvider, useAuth } from '../auth'
import * as Sentry from '@sentry/react'
import { Integrations } from '@sentry/tracing'
+import { QueryClient, QueryClientProvider } from 'react-query'
+import { useMemo } from 'react'
// This setup is necessary to forward the Auth0 context to the Redux context
const Inner = ({ Component, pageProps }) => {
const auth = useAuth()
- const store = useStore(pageProps.initialReduxState, { auth })
+ const queryClient = useMemo(() => new QueryClient(), [])
+ const store = useStore(pageProps.initialReduxState, { auth, queryClient })
return (
- <Provider store={store}>
- <Component {...pageProps} />
- </Provider>
+ <QueryClientProvider client={queryClient}>
+ <Provider store={store}>
+ <Component {...pageProps} />
+ </Provider>
+ </QueryClientProvider>
)
}
diff --git a/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js b/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js
index 28db1531..a9dfdb19 100644
--- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js
+++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js
@@ -24,7 +24,6 @@ import { useRouter } from 'next/router'
import { useProject } from '../../../../data/project'
import { useDispatch, useSelector } from 'react-redux'
import React, { useEffect } from 'react'
-import { openProjectSucceeded } from '../../../../redux/actions/projects'
import { HotKeys } from 'react-hotkeys'
import { KeymapConfiguration } from '../../../../hotkeys'
import Head from 'next/head'
@@ -35,6 +34,7 @@ import ScaleIndicatorContainer from '../../../../containers/app/map/controls/Sca
import ToolPanelComponent from '../../../../components/app/map/controls/ToolPanelComponent'
import ProjectSidebarContainer from '../../../../containers/app/sidebars/project/ProjectSidebarContainer'
import TopologySidebarContainer from '../../../../containers/app/sidebars/topology/TopologySidebarContainer'
+import { openProjectSucceeded } from '../../../../redux/actions/projects'
/**
* Page that displays a datacenter topology.
@@ -43,7 +43,7 @@ function Topology() {
const router = useRouter()
const { project: projectId, topology: topologyId } = router.query
- const project = useProject(projectId)
+ const { data: project } = useProject(projectId)
const title = project?.name ? project?.name + ' - OpenDC' : 'Simulation - OpenDC'
const dispatch = useDispatch()
diff --git a/opendc-web/opendc-web-ui/src/pages/projects/index.js b/opendc-web/opendc-web-ui/src/pages/projects/index.js
index 958ca622..2d8e6de7 100644
--- a/opendc-web/opendc-web-ui/src/pages/projects/index.js
+++ b/opendc-web/opendc-web-ui/src/pages/projects/index.js
@@ -1,22 +1,17 @@
-import React, { useEffect, useState } from 'react'
+import React, { useState } from 'react'
import Head from 'next/head'
-import { useDispatch } from 'react-redux'
import ProjectFilterPanel from '../../components/projects/FilterPanel'
import NewProjectContainer from '../../containers/projects/NewProjectContainer'
import ProjectListContainer from '../../containers/projects/ProjectListContainer'
import AppNavbarContainer from '../../containers/navigation/AppNavbarContainer'
import { useRequireAuth } from '../../auth'
import { Container } from 'reactstrap'
-import { fetchProjects } from '../../redux/actions/projects'
function Projects() {
useRequireAuth()
- const dispatch = useDispatch()
const [filter, setFilter] = useState('SHOW_ALL')
- useEffect(() => dispatch(fetchProjects()), [dispatch])
-
return (
<>
<Head>
diff --git a/opendc-web/opendc-web-ui/src/redux/actions/projects.js b/opendc-web/opendc-web-ui/src/redux/actions/projects.js
index a6324c43..4fe6f6a8 100644
--- a/opendc-web/opendc-web-ui/src/redux/actions/projects.js
+++ b/opendc-web/opendc-web-ui/src/redux/actions/projects.js
@@ -1,52 +1,5 @@
-export const FETCH_PROJECTS = 'FETCH_PROJECTS'
-export const FETCH_PROJECTS_SUCCEEDED = 'FETCH_PROJECTS_SUCCEEDED'
-export const ADD_PROJECT = 'ADD_PROJECT'
-export const ADD_PROJECT_SUCCEEDED = 'ADD_PROJECT_SUCCEEDED'
-export const DELETE_PROJECT = 'DELETE_PROJECT'
-export const DELETE_PROJECT_SUCCEEDED = 'DELETE_PROJECT_SUCCEEDED'
export const OPEN_PROJECT_SUCCEEDED = 'OPEN_PROJECT_SUCCEEDED'
-export function fetchProjects() {
- return {
- type: FETCH_PROJECTS,
- }
-}
-
-export function fetchProjectsSucceeded(projects) {
- return {
- type: FETCH_PROJECTS_SUCCEEDED,
- projects,
- }
-}
-
-export function addProject(name) {
- return {
- type: ADD_PROJECT,
- name,
- }
-}
-
-export function addProjectSucceeded(project) {
- return {
- type: ADD_PROJECT_SUCCEEDED,
- project,
- }
-}
-
-export function deleteProject(id) {
- return {
- type: DELETE_PROJECT,
- id,
- }
-}
-
-export function deleteProjectSucceeded(id) {
- return {
- type: DELETE_PROJECT_SUCCEEDED,
- id,
- }
-}
-
export function openProjectSucceeded(id) {
return {
type: OPEN_PROJECT_SUCCEEDED,
diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/index.js b/opendc-web/opendc-web-ui/src/redux/reducers/index.js
index 9f556d18..1b17a206 100644
--- a/opendc-web/opendc-web-ui/src/redux/reducers/index.js
+++ b/opendc-web/opendc-web-ui/src/redux/reducers/index.js
@@ -4,11 +4,9 @@ import { currentTopologyId } from './current-ids'
import { interactionLevel } from './interaction-level'
import { map } from './map'
import { objects } from './objects'
-import { projects } from './projects'
const rootReducer = combineReducers({
objects,
- projects,
construction,
map,
currentTopologyId,
diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/interaction-level.js b/opendc-web/opendc-web-ui/src/redux/reducers/interaction-level.js
index eafcb269..8bf81b98 100644
--- a/opendc-web/opendc-web-ui/src/redux/reducers/interaction-level.js
+++ b/opendc-web/opendc-web-ui/src/redux/reducers/interaction-level.js
@@ -5,7 +5,6 @@ import {
GO_FROM_RACK_TO_MACHINE,
GO_FROM_ROOM_TO_RACK,
} from '../actions/interaction-level'
-import { OPEN_PROJECT_SUCCEEDED } from '../actions/projects'
import { SET_CURRENT_TOPOLOGY } from '../actions/topology/building'
import { OPEN_SCENARIO_SUCCEEDED } from '../actions/scenarios'
@@ -13,7 +12,6 @@ export function interactionLevel(state = { mode: 'BUILDING' }, action) {
switch (action.type) {
case OPEN_PORTFOLIO_SUCCEEDED:
case OPEN_SCENARIO_SUCCEEDED:
- case OPEN_PROJECT_SUCCEEDED:
case SET_CURRENT_TOPOLOGY:
return {
mode: 'BUILDING',
diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/projects.js b/opendc-web/opendc-web-ui/src/redux/reducers/projects.js
deleted file mode 100644
index a920e47f..00000000
--- a/opendc-web/opendc-web-ui/src/redux/reducers/projects.js
+++ /dev/null
@@ -1,14 +0,0 @@
-import { ADD_PROJECT_SUCCEEDED, DELETE_PROJECT_SUCCEEDED, FETCH_PROJECTS_SUCCEEDED } from '../actions/projects'
-
-export function projects(state = [], action) {
- switch (action.type) {
- case FETCH_PROJECTS_SUCCEEDED:
- return action.projects
- case ADD_PROJECT_SUCCEEDED:
- return [...state, action.project]
- case DELETE_PROJECT_SUCCEEDED:
- return state.filter((project) => project._id !== action.id)
- default:
- return state
- }
-}
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..939be691 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/index.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/index.js
@@ -11,7 +11,7 @@ import { ADD_UNIT, DELETE_MACHINE, DELETE_UNIT } from '../actions/topology/machi
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,
@@ -36,10 +36,6 @@ 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)
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..5523dd57 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/objects.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/objects.js
@@ -1,13 +1,11 @@
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,
@@ -41,11 +39,6 @@ function* fetchAndStoreObjects(objectType, apiCall) {
return objects
}
-export const fetchAndStoreProject = function* (id) {
- const auth = yield getContext('auth')
- return yield fetchAndStoreObject('project', id, call(getProject, auth, id))
-}
-
export const fetchAndStoreTopology = function* (id) {
const topologyStore = yield select(OBJECT_SELECTORS['topology'])
const roomStore = yield select(OBJECT_SELECTORS['room'])
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js b/opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js
index 48d1ad3e..c32fcdc0 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js
@@ -1,7 +1,7 @@
import { call, put, select, delay, getContext } from 'redux-saga/effects'
-import { addPropToStoreObject, addToStore } from '../actions/objects'
+import { addToStore } from '../actions/objects'
import { addPortfolio, deletePortfolio, getPortfolio, updatePortfolio } from '../../api/portfolios'
-import { getProject } from '../../api/projects'
+import { fetchProject } from '../../api/projects'
import { fetchAndStoreAllSchedulers, fetchAndStoreAllTraces } from './objects'
import { fetchAndStoreAllTopologiesOfProject } from './topology'
import { getScenario } from '../../api/scenarios'
@@ -9,9 +9,11 @@ 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)
+ const queryClient = yield getContext('queryClient')
+ const project = yield call(() =>
+ queryClient.fetchQuery(`projects/${action.projectId}`, () => fetchProject(auth, action.projectId))
+ )
+ yield fetchAndStoreAllTopologiesOfProject(action.projectId)
yield fetchPortfoliosOfProject(project)
yield fetchAndStoreAllSchedulers()
yield fetchAndStoreAllTraces()
@@ -94,13 +96,6 @@ export function* onAddPortfolio(action) {
})
)
yield put(addToStore('portfolio', portfolio))
-
- const portfolioIds = yield select((state) => state.objects.project[projectId].portfolioIds)
- yield put(
- addPropToStoreObject('project', projectId, {
- portfolioIds: portfolioIds.concat([portfolio._id]),
- })
- )
} catch (error) {
console.error(error)
}
@@ -119,17 +114,7 @@ export function* onUpdatePortfolio(action) {
export function* onDeletePortfolio(action) {
try {
const auth = yield getContext('auth')
- const portfolio = yield select((state) => state.objects.portfolio[action.id])
-
yield call(deletePortfolio, auth, action.id)
-
- const portfolioIds = yield select((state) => state.objects.project[portfolio.projectId].portfolioIds)
-
- yield put(
- addPropToStoreObject('project', portfolio.projectId, {
- portfolioIds: portfolioIds.filter((id) => id !== action.id),
- })
- )
} 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 0689090a..96a4323c 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/projects.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/projects.js
@@ -1,16 +1,16 @@
-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 { call, getContext } from 'redux-saga/effects'
import { fetchAndStoreAllTopologiesOfProject } from './topology'
import { fetchAndStoreAllSchedulers, fetchAndStoreAllTraces } from './objects'
import { fetchPortfoliosOfProject } from './portfolios'
+import { fetchProject } from '../../api/projects'
export function* onOpenProjectSucceeded(action) {
try {
const auth = yield getContext('auth')
- const project = yield call(getProject, auth, action.id)
- yield put(addToStore('project', project))
+ const queryClient = yield getContext('queryClient')
+ const project = yield call(() =>
+ queryClient.fetchQuery(`projects/${action.id}`, () => fetchProject(auth, action.id))
+ )
yield fetchAndStoreAllTopologiesOfProject(action.id, true)
yield fetchPortfoliosOfProject(project)
@@ -20,34 +20,3 @@ export function* onOpenProjectSucceeded(action) {
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
index b2979636..3fe12981 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js
@@ -1,6 +1,6 @@
import { call, put, select, getContext } from 'redux-saga/effects'
import { addPropToStoreObject, addToStore } from '../actions/objects'
-import { getProject } from '../../api/projects'
+import { fetchProject } from '../../api/projects'
import { fetchAndStoreAllSchedulers, fetchAndStoreAllTraces } from './objects'
import { fetchAndStoreAllTopologiesOfProject } from './topology'
import { addScenario, deleteScenario, updateScenario } from '../../api/scenarios'
@@ -9,7 +9,10 @@ import { fetchPortfolioWithScenarios, watchForPortfolioResults } from './portfol
export function* onOpenScenarioSucceeded(action) {
try {
const auth = yield getContext('auth')
- const project = yield call(getProject, auth, action.projectId)
+ const queryClient = yield getContext('queryClient')
+ const project = yield call(() =>
+ queryClient.fetchQuery(`projects/${action.projectId}`, () => fetchProject(auth, action.projectId))
+ )
yield put(addToStore('project', project))
yield fetchAndStoreAllTopologiesOfProject(project._id)
yield fetchAndStoreAllSchedulers()
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 4f7bc8db..3f41e1d4 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
@@ -19,10 +19,15 @@ import {
import { fetchAndStoreTopology, getTopologyAsObject, updateTopologyOnServer } from './objects'
import { uuid } from 'uuidv4'
import { addTopology, deleteTopology } from '../../api/topologies'
+import { fetchProject } from '../../api/projects'
export function* fetchAndStoreAllTopologiesOfProject(projectId, setTopology = false) {
try {
- const project = yield select((state) => state.objects.project[projectId])
+ const auth = yield getContext('auth')
+ const queryClient = yield getContext('queryClient')
+ const project = yield call(() =>
+ queryClient.fetchQuery(`projects/${projectId}`, () => fetchProject(auth, projectId))
+ )
for (let i in project.topologyIds) {
yield fetchAndStoreTopology(project.topologyIds[i])
@@ -51,13 +56,6 @@ export function* onAddTopology(action) {
const auth = yield getContext('auth')
const topology = yield call(addTopology, auth, { ...topologyToBeCreated, projectId })
yield fetchAndStoreTopology(topology._id)
-
- const topologyIds = yield select((state) => state.objects.project[projectId].topologyIds)
- yield put(
- addPropToStoreObject('project', projectId, {
- topologyIds: topologyIds.concat([topology._id]),
- })
- )
yield put(setCurrentTopology(topology._id))
} catch (error) {
console.error(error)
@@ -66,21 +64,19 @@ export function* onAddTopology(action) {
export function* onDeleteTopology(action) {
try {
- const topology = yield select((state) => state.objects.topologies[action.id])
- const topologyIds = yield select((state) => state.objects.project[topology.projectId].topologyIds)
+ const auth = yield getContext('auth')
+ const queryClient = yield getContext('queryClient')
+ const project = yield call(() =>
+ queryClient.fetchQuery(`projects/${action.projectId}`, () => fetchProject(auth, action.projectId))
+ )
+ const topologyIds = project?.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', topology.projectId, {
- topologyIds: topologyIds.filter((id) => id !== action.id),
- })
- )
} catch (error) {
console.error(error)
}