summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web/opendc-web-ui')
-rw-r--r--opendc-web/opendc-web-ui/package.json1
-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
-rw-r--r--opendc-web/opendc-web-ui/yarn.lock85
26 files changed, 181 insertions, 205 deletions
diff --git a/opendc-web/opendc-web-ui/package.json b/opendc-web/opendc-web-ui/package.json
index cbeef049..e9570879 100644
--- a/opendc-web/opendc-web-ui/package.json
+++ b/opendc-web/opendc-web-ui/package.json
@@ -38,6 +38,7 @@
"react-dom": "^17.0.2",
"react-hotkeys": "^2.0.0",
"react-konva": "~17.0.2-0",
+ "react-query": "^3.18.1",
"react-redux": "~7.2.0",
"reactstrap": "^8.9.0",
"recharts": "~2.0.9",
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)
}
diff --git a/opendc-web/opendc-web-ui/yarn.lock b/opendc-web/opendc-web-ui/yarn.lock
index 60a48d32..630f38db 100644
--- a/opendc-web/opendc-web-ui/yarn.lock
+++ b/opendc-web/opendc-web-ui/yarn.lock
@@ -64,6 +64,13 @@
dependencies:
regenerator-runtime "^0.13.4"
+"@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2":
+ version "7.14.6"
+ resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.14.6.tgz#535203bc0892efc7dec60bdc27b2ecf6e409062d"
+ integrity sha512-/PCB2uJ7oM44tz8YhC4Z/6PeOKXp4K588f+5M3clr1M4zbqztlo0XEfJ2LEzj/FgwfgGcIdl8n7YYjTCI0BYwg==
+ dependencies:
+ regenerator-runtime "^0.13.4"
+
"@babel/types@7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c"
@@ -660,6 +667,11 @@ base64-js@^1.0.2:
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
+big-integer@^1.6.16:
+ version "1.6.48"
+ resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
+ integrity sha512-j51egjPa7/i+RdiRuJbPdJ2FIUYYPhvYLjzoYbcMMm62ooO6F94fETG4MTs46zPAF9Brs04OajboA/qTGuz78w==
+
big.js@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
@@ -700,6 +712,20 @@ braces@^3.0.1, braces@~3.0.2:
dependencies:
fill-range "^7.0.1"
+broadcast-channel@^3.4.1:
+ version "3.7.0"
+ resolved "https://registry.yarnpkg.com/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937"
+ integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
+ dependencies:
+ "@babel/runtime" "^7.7.2"
+ detect-node "^2.1.0"
+ js-sha3 "0.8.0"
+ microseconds "0.2.0"
+ nano-time "1.0.0"
+ oblivious-set "1.0.0"
+ rimraf "3.0.2"
+ unload "2.2.0"
+
brorand@^1.0.1, brorand@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
@@ -1260,6 +1286,11 @@ des.js@^1.0.0:
inherits "^2.0.1"
minimalistic-assert "^1.0.0"
+detect-node@^2.0.4, detect-node@^2.1.0:
+ version "2.1.0"
+ resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
+ integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
+
diffie-hellman@^5.0.0:
version "5.0.3"
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
@@ -2268,6 +2299,11 @@ jest-worker@27.0.0-next.5:
merge-stream "^2.0.0"
supports-color "^8.0.0"
+js-sha3@0.8.0:
+ version "0.8.0"
+ resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
+ integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
+
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
@@ -2482,6 +2518,14 @@ make-dir@^3.0.2:
dependencies:
semver "^6.0.0"
+match-sorter@^6.0.2:
+ version "6.3.0"
+ resolved "https://registry.yarnpkg.com/match-sorter/-/match-sorter-6.3.0.tgz#454a1b31ed218cddbce6231a0ecb5fdc549fed01"
+ integrity sha512-efYOf/wUpNb8FgNY+cOD2EIJI1S5I7YPKsw0LBp7wqPh5pmMS6i/wr3ZWwfwrAw1NvqTA2KUReVRWDX84lUcOQ==
+ dependencies:
+ "@babel/runtime" "^7.12.5"
+ remove-accents "0.4.2"
+
mathjs@~7.6.0:
version "7.6.0"
resolved "https://registry.yarnpkg.com/mathjs/-/mathjs-7.6.0.tgz#f0b7579e0756b13422995d0c4f29bd17d65d4dcc"
@@ -2523,6 +2567,11 @@ micromatch@^4.0.2:
braces "^3.0.1"
picomatch "^2.2.3"
+microseconds@0.2.0:
+ version "0.2.0"
+ resolved "https://registry.yarnpkg.com/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
+ integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
+
miller-rabin@^4.0.0:
version "4.0.1"
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
@@ -2573,6 +2622,13 @@ ms@^2.1.1:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+nano-time@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
+ integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
+ dependencies:
+ big-integer "^1.6.16"
+
nanoid@^3.1.22:
version "3.1.22"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.1.22.tgz#b35f8fb7d151990a8aebd5aa5015c03cf726f844"
@@ -2775,6 +2831,11 @@ object.values@^1.1.3, object.values@^1.1.4:
define-properties "^1.1.3"
es-abstract "^1.18.2"
+oblivious-set@1.0.0:
+ version "1.0.0"
+ resolved "https://registry.yarnpkg.com/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
+ integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
+
once@^1.3.0, once@^1.3.1, once@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
@@ -3226,6 +3287,15 @@ react-popper@^1.3.6:
typed-styles "^0.0.7"
warning "^4.0.2"
+react-query@^3.18.1:
+ version "3.18.1"
+ resolved "https://registry.yarnpkg.com/react-query/-/react-query-3.18.1.tgz#893b5475a7b4add099e007105317446f7a2cd310"
+ integrity sha512-17lv3pQxU9n+cB5acUv0/cxNTjo9q8G+RsedC6Ax4V9D8xEM7Q5xf9xAbCPdEhDrrnzPjTls9fQEABKRSi7OJA==
+ dependencies:
+ "@babel/runtime" "^7.5.5"
+ broadcast-channel "^3.4.1"
+ match-sorter "^6.0.2"
+
react-reconciler@~0.26.1:
version "0.26.2"
resolved "https://registry.yarnpkg.com/react-reconciler/-/react-reconciler-0.26.2.tgz#bbad0e2d1309423f76cf3c3309ac6c96e05e9d91"
@@ -3432,6 +3502,11 @@ regexpp@^3.1.0:
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
+remove-accents@0.4.2:
+ version "0.4.2"
+ resolved "https://registry.yarnpkg.com/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
+ integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=
+
require-from-string@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909"
@@ -3476,7 +3551,7 @@ reusify@^1.0.4:
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76"
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==
-rimraf@^3.0.2:
+rimraf@3.0.2, rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
@@ -4066,6 +4141,14 @@ unfetch@^4.2.0:
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==
+unload@2.2.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
+ integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
+ dependencies:
+ "@babel/runtime" "^7.6.2"
+ detect-node "^2.0.4"
+
unpipe@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"