summaryrefslogtreecommitdiff
path: root/opendc-web
diff options
context:
space:
mode:
Diffstat (limited to 'opendc-web')
-rw-r--r--opendc-web/opendc-web-ui/package.json3
-rw-r--r--opendc-web/opendc-web-ui/src/api/index.js20
-rw-r--r--opendc-web/opendc-web-ui/src/api/portfolios.js16
-rw-r--r--opendc-web/opendc-web-ui/src/api/prefabs.js16
-rw-r--r--opendc-web/opendc-web-ui/src/api/projects.js20
-rw-r--r--opendc-web/opendc-web-ui/src/api/scenarios.js16
-rw-r--r--opendc-web/opendc-web-ui/src/api/schedulers.js4
-rw-r--r--opendc-web/opendc-web-ui/src/api/token-signin.js32
-rw-r--r--opendc-web/opendc-web-ui/src/api/topologies.js16
-rw-r--r--opendc-web/opendc-web-ui/src/api/traces.js4
-rw-r--r--opendc-web/opendc-web-ui/src/auth.js124
-rw-r--r--opendc-web/opendc-web-ui/src/components/navigation/Navbar.js16
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/ProjectList.js12
-rw-r--r--opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js31
-rw-r--r--opendc-web/opendc-web-ui/src/containers/app/sidebars/project/ScenarioListContainer.js2
-rw-r--r--opendc-web/opendc-web-ui/src/containers/auth/Login.js35
-rw-r--r--opendc-web/opendc-web-ui/src/containers/auth/Logout.js7
-rw-r--r--opendc-web/opendc-web-ui/src/containers/auth/ProfileName.js10
-rw-r--r--opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js34
-rw-r--r--opendc-web/opendc-web-ui/src/containers/projects/VisibleProjectAuthList.js37
-rw-r--r--opendc-web/opendc-web-ui/src/data/project.js7
-rw-r--r--opendc-web/opendc-web-ui/src/pages/_app.js20
-rw-r--r--opendc-web/opendc-web-ui/src/pages/logout.js (renamed from opendc-web/opendc-web-ui/src/api/users.js)28
-rw-r--r--opendc-web/opendc-web-ui/src/pages/profile.js76
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/index.js8
-rw-r--r--opendc-web/opendc-web-ui/src/redux/actions/auth.js23
-rw-r--r--opendc-web/opendc-web-ui/src/redux/actions/projects.js29
-rw-r--r--opendc-web/opendc-web-ui/src/redux/actions/users.js37
-rw-r--r--opendc-web/opendc-web-ui/src/redux/index.js26
-rw-r--r--opendc-web/opendc-web-ui/src/redux/reducers/auth.js12
-rw-r--r--opendc-web/opendc-web-ui/src/redux/reducers/index.js6
-rw-r--r--opendc-web/opendc-web-ui/src/redux/reducers/project-list.js18
-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.js14
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/objects.js25
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js19
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js5
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/profile.js12
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/projects.js35
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js14
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/topology.js7
-rw-r--r--opendc-web/opendc-web-ui/src/redux/sagas/users.js44
-rw-r--r--opendc-web/opendc-web-ui/src/shapes.js22
-rw-r--r--opendc-web/opendc-web-ui/yarn.lock74
44 files changed, 423 insertions, 607 deletions
diff --git a/opendc-web/opendc-web-ui/package.json b/opendc-web/opendc-web-ui/package.json
index f6917398..9c41c2e2 100644
--- a/opendc-web/opendc-web-ui/package.json
+++ b/opendc-web/opendc-web-ui/package.json
@@ -17,6 +17,7 @@
"license": "MIT",
"private": true,
"dependencies": {
+ "@auth0/auth0-react": "^1.5.0",
"@sentry/react": "^5.30.0",
"@sentry/tracing": "^5.30.0",
"approximate-number": "~2.0.0",
@@ -32,14 +33,12 @@
"react": "~17.0.2",
"react-dom": "~17.0.2",
"react-fontawesome": "~1.7.1",
- "react-google-login": "~5.1.14",
"react-hotkeys": "^2.0.0",
"react-konva": "~17.0.2-0",
"react-redux": "~7.2.0",
"reactstrap": "^8.9.0",
"recharts": "~2.0.9",
"redux": "~4.0.5",
- "redux-localstorage": "^0.4.1",
"redux-logger": "~3.0.6",
"redux-saga": "~1.1.3",
"redux-thunk": "~2.3.0",
diff --git a/opendc-web/opendc-web-ui/src/api/index.js b/opendc-web/opendc-web-ui/src/api/index.js
index 65358745..680d49ce 100644
--- a/opendc-web/opendc-web-ui/src/api/index.js
+++ b/opendc-web/opendc-web-ui/src/api/index.js
@@ -20,30 +20,32 @@
* SOFTWARE.
*/
-import { getAuthToken } from '../auth'
-
const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
/**
* Send the specified request to the OpenDC API.
+ *
+ * @param auth The authentication context.
* @param path Relative path for the API.
* @param method The method to use for the request.
* @param body The body of the request.
*/
-export async function request(path, method = 'GET', body) {
- const res = await fetch(`${apiUrl}/v2/${path}`, {
+export async function request(auth, path, method = 'GET', body) {
+ const { getAccessTokenSilently } = auth
+ const token = await getAccessTokenSilently()
+ const response = await fetch(`${apiUrl}/${path}`, {
method: method,
headers: {
- 'auth-token': getAuthToken(),
+ Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
},
body: body && JSON.stringify(body),
})
- const { status, content } = await res.json()
+ const json = await response.json()
- if (status.code !== 200) {
- throw status
+ if (!response.ok) {
+ throw response.message
}
- return content
+ return json.data
}
diff --git a/opendc-web/opendc-web-ui/src/api/portfolios.js b/opendc-web/opendc-web-ui/src/api/portfolios.js
index 6202e702..28898e6a 100644
--- a/opendc-web/opendc-web-ui/src/api/portfolios.js
+++ b/opendc-web/opendc-web-ui/src/api/portfolios.js
@@ -22,18 +22,18 @@
import { request } from './index'
-export function addPortfolio(projectId, portfolio) {
- return request(`projects/${projectId}/portfolios`, 'POST', { portfolio })
+export function addPortfolio(auth, projectId, portfolio) {
+ return request(auth, `projects/${projectId}/portfolios`, 'POST', { portfolio })
}
-export function getPortfolio(portfolioId) {
- return request(`portfolios/${portfolioId}`)
+export function getPortfolio(auth, portfolioId) {
+ return request(auth, `portfolios/${portfolioId}`)
}
-export function updatePortfolio(portfolioId, portfolio) {
- return request(`portfolios/${portfolioId}`, 'PUT', { portfolio })
+export function updatePortfolio(auth, portfolioId, portfolio) {
+ return request(auth, `portfolios/${portfolioId}`, 'PUT', { portfolio })
}
-export function deletePortfolio(portfolioId) {
- return request(`portfolios/${portfolioId}`, 'DELETE')
+export function deletePortfolio(auth, portfolioId) {
+ return request(auth, `portfolios/${portfolioId}`, 'DELETE')
}
diff --git a/opendc-web/opendc-web-ui/src/api/prefabs.js b/opendc-web/opendc-web-ui/src/api/prefabs.js
index a8bd3f3b..eb9aa23c 100644
--- a/opendc-web/opendc-web-ui/src/api/prefabs.js
+++ b/opendc-web/opendc-web-ui/src/api/prefabs.js
@@ -22,18 +22,18 @@
import { request } from './index'
-export function getPrefab(prefabId) {
- return request(`prefabs/${prefabId}`)
+export function getPrefab(auth, prefabId) {
+ return request(auth, `prefabs/${prefabId}`)
}
-export function addPrefab(prefab) {
- return request('prefabs', 'POST', { prefab })
+export function addPrefab(auth, prefab) {
+ return request(auth, 'prefabs/', 'POST', { prefab })
}
-export function updatePrefab(prefab) {
- return request(`prefabs/${prefab._id}`, 'PUT', { prefab })
+export function updatePrefab(auth, prefab) {
+ return request(auth, `prefabs/${prefab._id}`, 'PUT', { prefab })
}
-export function deletePrefab(prefabId) {
- return request(`prefabs/${prefabId}`, 'DELETE')
+export function deletePrefab(auth, prefabId) {
+ return request(auth, `prefabs/${prefabId}`, 'DELETE')
}
diff --git a/opendc-web/opendc-web-ui/src/api/projects.js b/opendc-web/opendc-web-ui/src/api/projects.js
index 9ff7deda..93052080 100644
--- a/opendc-web/opendc-web-ui/src/api/projects.js
+++ b/opendc-web/opendc-web-ui/src/api/projects.js
@@ -22,18 +22,22 @@
import { request } from './index'
-export function getProject(projectId) {
- return request(`projects/${projectId}`)
+export function getProjects(auth) {
+ return request(auth, `projects/`)
}
-export function addProject(project) {
- return request('projects', 'POST', { project })
+export function getProject(auth, projectId) {
+ return request(auth, `projects/${projectId}`)
}
-export function updateProject(project) {
- return request(`projects/${project._id}`, 'PUT', { project })
+export function addProject(auth, project) {
+ return request(auth, 'projects/', 'POST', { project })
}
-export function deleteProject(projectId) {
- return request(`projects/${projectId}`, 'DELETE')
+export function updateProject(auth, project) {
+ return request(auth, `projects/${project._id}`, 'PUT', { project })
+}
+
+export function deleteProject(auth, projectId) {
+ return request(auth, `projects/${projectId}`, 'DELETE')
}
diff --git a/opendc-web/opendc-web-ui/src/api/scenarios.js b/opendc-web/opendc-web-ui/src/api/scenarios.js
index 9f8c717b..095aa788 100644
--- a/opendc-web/opendc-web-ui/src/api/scenarios.js
+++ b/opendc-web/opendc-web-ui/src/api/scenarios.js
@@ -22,18 +22,18 @@
import { request } from './index'
-export function addScenario(portfolioId, scenario) {
- return request(`portfolios/${portfolioId}/scenarios`, 'POST', { scenario })
+export function addScenario(auth, portfolioId, scenario) {
+ return request(auth, `portfolios/${portfolioId}/scenarios`, 'POST', { scenario })
}
-export function getScenario(scenarioId) {
- return request(`scenarios/${scenarioId}`)
+export function getScenario(auth, scenarioId) {
+ return request(auth, `scenarios/${scenarioId}`)
}
-export function updateScenario(scenarioId, scenario) {
- return request(`scenarios/${scenarioId}`, 'PUT', { scenario })
+export function updateScenario(auth, scenarioId, scenario) {
+ return request(auth, `scenarios/${scenarioId}`, 'PUT', { scenario })
}
-export function deleteScenario(scenarioId) {
- return request(`scenarios/${scenarioId}`, 'DELETE')
+export function deleteScenario(auth, scenarioId) {
+ return request(auth, `scenarios/${scenarioId}`, 'DELETE')
}
diff --git a/opendc-web/opendc-web-ui/src/api/schedulers.js b/opendc-web/opendc-web-ui/src/api/schedulers.js
index 7791e51e..1b69f1a1 100644
--- a/opendc-web/opendc-web-ui/src/api/schedulers.js
+++ b/opendc-web/opendc-web-ui/src/api/schedulers.js
@@ -22,6 +22,6 @@
import { request } from './index'
-export function getAllSchedulers() {
- return request('schedulers')
+export function getAllSchedulers(auth) {
+ return request(auth, 'schedulers/')
}
diff --git a/opendc-web/opendc-web-ui/src/api/token-signin.js b/opendc-web/opendc-web-ui/src/api/token-signin.js
deleted file mode 100644
index a3761fa1..00000000
--- a/opendc-web/opendc-web-ui/src/api/token-signin.js
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-export function performTokenSignIn(token) {
- const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
-
- return fetch(`${apiUrl}/tokensignin`, {
- method: 'POST',
- body: new URLSearchParams({
- idtoken: token,
- }),
- }).then((res) => res.json())
-}
diff --git a/opendc-web/opendc-web-ui/src/api/topologies.js b/opendc-web/opendc-web-ui/src/api/topologies.js
index e6df73c7..c8744e6c 100644
--- a/opendc-web/opendc-web-ui/src/api/topologies.js
+++ b/opendc-web/opendc-web-ui/src/api/topologies.js
@@ -22,18 +22,18 @@
import { request } from './index'
-export function addTopology(topology) {
- return request(`projects/${topology.projectId}/topologies`, 'POST', { topology })
+export function addTopology(auth, topology) {
+ return request(auth, `projects/${topology.projectId}/topologies`, 'POST', { topology })
}
-export function getTopology(topologyId) {
- return request(`topologies/${topologyId}`)
+export function getTopology(auth, topologyId) {
+ return request(auth, `topologies/${topologyId}`)
}
-export function updateTopology(topology) {
- return request(`topologies/${topology._id}`, 'PUT', { topology })
+export function updateTopology(auth, topology) {
+ return request(auth, `topologies/${topology._id}`, 'PUT', { topology })
}
-export function deleteTopology(topologyId) {
- return request(`topologies/${topologyId}`, 'DELETE')
+export function deleteTopology(auth, topologyId) {
+ return request(auth, `topologies/${topologyId}`, 'DELETE')
}
diff --git a/opendc-web/opendc-web-ui/src/api/traces.js b/opendc-web/opendc-web-ui/src/api/traces.js
index 1c5cfa1d..df03a2dd 100644
--- a/opendc-web/opendc-web-ui/src/api/traces.js
+++ b/opendc-web/opendc-web-ui/src/api/traces.js
@@ -22,6 +22,6 @@
import { request } from './index'
-export function getAllTraces() {
- return request('traces')
+export function getAllTraces(auth) {
+ return request(auth, 'traces/')
}
diff --git a/opendc-web/opendc-web-ui/src/auth.js b/opendc-web/opendc-web-ui/src/auth.js
index faed9829..706151bf 100644
--- a/opendc-web/opendc-web-ui/src/auth.js
+++ b/opendc-web/opendc-web-ui/src/auth.js
@@ -1,83 +1,65 @@
-import { LOG_IN_SUCCEEDED, LOG_OUT } from './redux/actions/auth'
-import { DELETE_CURRENT_USER_SUCCEEDED } from './redux/actions/users'
-import { useEffect, useState } from 'react'
-import { useRouter } from 'next/router'
-import { useSelector } from 'react-redux'
-
-const getAuthObject = () => {
- const authItem = global.localStorage && localStorage.getItem('auth')
- if (!authItem || authItem === '{}') {
- return undefined
- }
- return JSON.parse(authItem)
-}
-
-export const userIsLoggedIn = () => {
- const authObj = getAuthObject()
-
- if (!authObj || !authObj.googleId) {
- return false
- }
-
- const currentTime = new Date().getTime()
- return parseInt(authObj.expiresAt, 10) - currentTime > 0
-}
-
-export const getAuthToken = () => {
- const authObj = getAuthObject()
- if (!authObj) {
- return undefined
- }
-
- return authObj.authToken
-}
-
-export const saveAuthLocalStorage = (payload) => {
- localStorage.setItem('auth', JSON.stringify(payload))
-}
-
-export const clearAuthLocalStorage = () => {
- localStorage.setItem('auth', '')
-}
-
-export const authRedirectMiddleware = (store) => (next) => (action) => {
- switch (action.type) {
- case LOG_IN_SUCCEEDED:
- saveAuthLocalStorage(action.payload)
- window.location.href = '/projects'
- break
- case LOG_OUT:
- case DELETE_CURRENT_USER_SUCCEEDED:
- clearAuthLocalStorage()
- window.location.href = '/'
- break
- default:
- next(action)
- return
- }
-
- next(action)
-}
+/*
+ * Copyright (c) 2021 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
-export function useIsLoggedIn() {
- const [isLoggedIn, setLoggedIn] = useState(false)
-
- useEffect(() => {
- setLoggedIn(userIsLoggedIn())
- }, [])
+import { Auth0Provider, useAuth0 } from '@auth0/auth0-react'
+import { useEffect } from 'react'
+import { useRouter } from 'next/router'
- return isLoggedIn
+/**
+ * Obtain the authentication context.
+ */
+export function useAuth() {
+ return useAuth0()
}
+/**
+ * Force the user to be authenticated or redirect to the homepage.
+ */
export function useRequireAuth() {
+ const auth = useAuth()
const router = useRouter()
+ const { isLoading, isAuthenticated } = auth
+
useEffect(() => {
- if (!userIsLoggedIn()) {
+ if (!isLoading && !isAuthenticated) {
router.replace('/')
}
- })
+ }, [isLoading, isAuthenticated])
+
+ return auth
}
-export function useUser() {
- return useSelector((state) => state.auth)
+/**
+ * AuthProvider which provides an authentication context.
+ */
+export function AuthProvider({ children }) {
+ return (
+ <Auth0Provider
+ domain={process.env.NEXT_PUBLIC_AUTH0_DOMAIN}
+ clientId={process.env.NEXT_PUBLIC_AUTH0_CLIENT_ID}
+ redirectUri={global.window && global.window.location.origin}
+ audience={process.env.NEXT_PUBLIC_AUTH0_AUDIENCE}
+ >
+ {children}
+ </Auth0Provider>
+ )
}
diff --git a/opendc-web/opendc-web-ui/src/components/navigation/Navbar.js b/opendc-web/opendc-web-ui/src/components/navigation/Navbar.js
index f16a3feb..5c9ea1b8 100644
--- a/opendc-web/opendc-web-ui/src/components/navigation/Navbar.js
+++ b/opendc-web/opendc-web-ui/src/components/navigation/Navbar.js
@@ -15,7 +15,7 @@ import Login from '../../containers/auth/Login'
import Logout from '../../containers/auth/Logout'
import ProfileName from '../../containers/auth/ProfileName'
import { login, navbar, opendcBrand } from './Navbar.module.scss'
-import { useIsLoggedIn } from '../../auth'
+import { useAuth } from '../../auth'
export const NAVBAR_HEIGHT = 60
@@ -44,10 +44,10 @@ export const NavItem = ({ route, children }) => {
export const LoggedInSection = () => {
const router = useRouter()
- const isLoggedIn = useIsLoggedIn()
+ const { isAuthenticated } = useAuth()
return (
<Nav navbar className="auth-links">
- {isLoggedIn ? (
+ {isAuthenticated ? (
[
router.asPath === '/' ? (
<NavItem route="/projects" key="projects">
@@ -58,12 +58,10 @@ export const LoggedInSection = () => {
</Link>
</NavItem>
) : (
- <NavItem route="/profile" key="profile">
- <Link href="/profile">
- <NavLink title="My Profile">
- <ProfileName />
- </NavLink>
- </Link>
+ <NavItem key="profile">
+ <NavLink title="My Profile">
+ <ProfileName />
+ </NavLink>
</NavItem>
),
<NavItem route="logout" key="logout">
diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js
index 90d42326..cb17b835 100644
--- a/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js
+++ b/opendc-web/opendc-web-ui/src/components/projects/ProjectList.js
@@ -1,12 +1,12 @@
import PropTypes from 'prop-types'
import React from 'react'
-import { Authorization } from '../../shapes'
+import { Project } from '../../shapes'
import ProjectRow from './ProjectRow'
-const ProjectList = ({ authorizations }) => {
+const ProjectList = ({ projects }) => {
return (
<div className="vertically-expanding-container">
- {authorizations.length === 0 ? (
+ {projects.length === 0 ? (
<div className="alert alert-info">
<span className="info-icon fa fa-question-circle mr-2" />
<strong>No projects here yet...</strong> Add some with the 'New Project' button!
@@ -22,8 +22,8 @@ const ProjectList = ({ authorizations }) => {
</tr>
</thead>
<tbody>
- {authorizations.map((authorization) => (
- <ProjectRow projectAuth={authorization} key={authorization.project._id} />
+ {projects.map((project) => (
+ <ProjectRow project={project} key={project._id} />
))}
</tbody>
</table>
@@ -33,7 +33,7 @@ const ProjectList = ({ authorizations }) => {
}
ProjectList.propTypes = {
- authorizations: PropTypes.arrayOf(Authorization).isRequired,
+ projects: PropTypes.arrayOf(Project).isRequired,
}
export default ProjectList
diff --git a/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js b/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js
index a0aac098..9a95b777 100644
--- a/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js
+++ b/opendc-web/opendc-web-ui/src/components/projects/ProjectRow.js
@@ -1,24 +1,29 @@
import classNames from 'classnames'
import React from 'react'
import ProjectActions from '../../containers/projects/ProjectActions'
-import { Authorization } from '../../shapes'
+import { Project } from '../../shapes'
import { AUTH_DESCRIPTION_MAP, AUTH_ICON_MAP } from '../../util/authorizations'
import { parseAndFormatDateTime } from '../../util/date-time'
+import { useAuth } from '../../auth'
-const ProjectRow = ({ projectAuth }) => (
- <tr>
- <td className="pt-3">{projectAuth.project.name}</td>
- <td className="pt-3">{parseAndFormatDateTime(projectAuth.project.datetimeLastEdited)}</td>
- <td className="pt-3">
- <span className={classNames('fa', 'fa-' + AUTH_ICON_MAP[projectAuth.authorizationLevel], 'mr-2')} />
- {AUTH_DESCRIPTION_MAP[projectAuth.authorizationLevel]}
- </td>
- <ProjectActions projectId={projectAuth.project._id} />
- </tr>
-)
+const ProjectRow = ({ project }) => {
+ const { user } = useAuth()
+ const { level } = project.authorizations.find((auth) => auth.userId === user.sub)
+ return (
+ <tr>
+ <td className="pt-3">{project.name}</td>
+ <td className="pt-3">{parseAndFormatDateTime(project.datetimeLastEdited)}</td>
+ <td className="pt-3">
+ <span className={classNames('fa', 'fa-' + AUTH_ICON_MAP[level], 'mr-2')} />
+ {AUTH_DESCRIPTION_MAP[level]}
+ </td>
+ <ProjectActions projectId={project._id} />
+ </tr>
+ )
+}
ProjectRow.propTypes = {
- projectAuth: Authorization.isRequired,
+ project: Project.isRequired,
}
export default ProjectRow
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 7aaa1886..e1be51dc 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
@@ -60,7 +60,7 @@ const ScenarioListContainer = ({ portfolioId }) => {
/>
<NewScenarioModalComponent
show={isVisible}
- currentPortfolioId={currentProjectId}
+ currentPortfolioId={portfolioId}
currentPortfolioScenarioIds={scenarios.map((s) => s._id)}
traces={traces}
schedulers={schedulers}
diff --git a/opendc-web/opendc-web-ui/src/containers/auth/Login.js b/opendc-web/opendc-web-ui/src/containers/auth/Login.js
index b0da0d0e..8459ef5f 100644
--- a/opendc-web/opendc-web-ui/src/containers/auth/Login.js
+++ b/opendc-web/opendc-web-ui/src/containers/auth/Login.js
@@ -1,43 +1,18 @@
import React from 'react'
-import GoogleLogin from 'react-google-login'
-import { useDispatch } from 'react-redux'
-import { logIn } from '../../redux/actions/auth'
import { Button } from 'reactstrap'
+import { useAuth } from '../../auth'
function Login({ visible, className }) {
- const dispatch = useDispatch()
-
- const onLogin = (payload) => dispatch(logIn(payload))
- const onAuthResponse = (response) => {
- onLogin({
- email: response.getBasicProfile().getEmail(),
- givenName: response.getBasicProfile().getGivenName(),
- familyName: response.getBasicProfile().getFamilyName(),
- googleId: response.googleId,
- authToken: response.getAuthResponse().id_token,
- expiresAt: response.getAuthResponse().expires_at,
- })
- }
- const onAuthFailure = (error) => {
- // TODO Show error alert
- console.error(error)
- }
+ const { loginWithRedirect } = useAuth()
if (!visible) {
return <span />
}
return (
- <GoogleLogin
- clientId={process.env.NEXT_PUBLIC_OAUTH_CLIENT_ID}
- onSuccess={onAuthResponse}
- onFailure={onAuthFailure}
- render={(renderProps) => (
- <Button color="primary" onClick={renderProps.onClick} className={className}>
- <span aria-hidden className="fa fa-google" /> Login with Google
- </Button>
- )}
- />
+ <Button color="primary" onClick={() => loginWithRedirect()} className={className}>
+ <span aria-hidden className="fa fa-sign-in" /> Sign In
+ </Button>
)
}
diff --git a/opendc-web/opendc-web-ui/src/containers/auth/Logout.js b/opendc-web/opendc-web-ui/src/containers/auth/Logout.js
index 94d4d061..37705c5d 100644
--- a/opendc-web/opendc-web-ui/src/containers/auth/Logout.js
+++ b/opendc-web/opendc-web-ui/src/containers/auth/Logout.js
@@ -1,11 +1,10 @@
import React from 'react'
-import { useDispatch } from 'react-redux'
-import { logOut } from '../../redux/actions/auth'
import LogoutButton from '../../components/navigation/LogoutButton'
+import { useAuth } from '../../auth'
const Logout = (props) => {
- const dispatch = useDispatch()
- return <LogoutButton {...props} onLogout={() => dispatch(logOut())} />
+ const { logout } = useAuth()
+ return <LogoutButton {...props} onLogout={() => logout({ returnTo: window.location.origin })} />
}
export default Logout
diff --git a/opendc-web/opendc-web-ui/src/containers/auth/ProfileName.js b/opendc-web/opendc-web-ui/src/containers/auth/ProfileName.js
index 3992c00f..70f5b884 100644
--- a/opendc-web/opendc-web-ui/src/containers/auth/ProfileName.js
+++ b/opendc-web/opendc-web-ui/src/containers/auth/ProfileName.js
@@ -1,13 +1,9 @@
import React from 'react'
-import { useUser } from '../../auth'
+import { useAuth } from '../../auth'
function ProfileName() {
- const user = useUser()
- return (
- <span>
- {user.givenName} {user.familyName}
- </span>
- )
+ const { isLoading, user } = useAuth()
+ return isLoading ? <span>Loading...</span> : <span>{user.name}</span>
}
export default ProfileName
diff --git a/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js b/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js
new file mode 100644
index 00000000..6632a8b5
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/containers/projects/ProjectListContainer.js
@@ -0,0 +1,34 @@
+import React from 'react'
+import PropTypes from 'prop-types'
+import ProjectList from '../../components/projects/ProjectList'
+import { useAuth } from '../../auth'
+import { useProjects } from '../../data/project'
+
+const getVisibleProjects = (projects, filter, userId) => {
+ switch (filter) {
+ case 'SHOW_ALL':
+ return projects
+ case 'SHOW_OWN':
+ return projects.filter((project) =>
+ project.authorizations.some((a) => a.userId === userId && a.level === 'OWN')
+ )
+ case 'SHOW_SHARED':
+ return projects.filter((project) =>
+ project.authorizations.some((a) => a.userId === userId && a.level !== 'OWN')
+ )
+ default:
+ return projects
+ }
+}
+
+const ProjectListContainer = ({ filter }) => {
+ const { user } = useAuth()
+ const projects = useProjects()
+ return <ProjectList projects={getVisibleProjects(projects, filter, user?.sub)} />
+}
+
+ProjectListContainer.propTypes = {
+ filter: PropTypes.string.isRequired,
+}
+
+export default ProjectListContainer
diff --git a/opendc-web/opendc-web-ui/src/containers/projects/VisibleProjectAuthList.js b/opendc-web/opendc-web-ui/src/containers/projects/VisibleProjectAuthList.js
deleted file mode 100644
index 8e1d063b..00000000
--- a/opendc-web/opendc-web-ui/src/containers/projects/VisibleProjectAuthList.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import { useSelector } from 'react-redux'
-import ProjectList from '../../components/projects/ProjectList'
-
-const getVisibleProjectAuths = (projectAuths, filter) => {
- switch (filter) {
- case 'SHOW_ALL':
- return projectAuths
- case 'SHOW_OWN':
- return projectAuths.filter((projectAuth) => projectAuth.authorizationLevel === 'OWN')
- case 'SHOW_SHARED':
- return projectAuths.filter((projectAuth) => projectAuth.authorizationLevel !== 'OWN')
- default:
- return projectAuths
- }
-}
-
-const VisibleProjectAuthList = ({ filter }) => {
- const authorizations = useSelector((state) => {
- const denormalizedAuthorizations = state.projectList.authorizationsOfCurrentUser.map((authorizationIds) => {
- const authorization = state.objects.authorization[authorizationIds]
- authorization.user = state.objects.user[authorization.userId]
- authorization.project = state.objects.project[authorization.projectId]
- return authorization
- })
-
- return getVisibleProjectAuths(denormalizedAuthorizations, filter)
- })
- return <ProjectList authorizations={authorizations} />
-}
-
-VisibleProjectAuthList.propTypes = {
- filter: PropTypes.string.isRequired,
-}
-
-export default VisibleProjectAuthList
diff --git a/opendc-web/opendc-web-ui/src/data/project.js b/opendc-web/opendc-web-ui/src/data/project.js
index 0db49fdd..de2bc0d3 100644
--- a/opendc-web/opendc-web-ui/src/data/project.js
+++ b/opendc-web/opendc-web-ui/src/data/project.js
@@ -23,6 +23,13 @@
import { useSelector } from 'react-redux'
/**
+ * Return the available projects.
+ */
+export function useProjects() {
+ return useSelector((state) => state.projects)
+}
+
+/**
* Return the current active project.
*/
export function useActiveProject() {
diff --git a/opendc-web/opendc-web-ui/src/pages/_app.js b/opendc-web/opendc-web-ui/src/pages/_app.js
index 761ae0cd..f9727a7a 100644
--- a/opendc-web/opendc-web-ui/src/pages/_app.js
+++ b/opendc-web/opendc-web-ui/src/pages/_app.js
@@ -24,19 +24,29 @@ import Head from 'next/head'
import { Provider } from 'react-redux'
import { useStore } from '../redux'
import '../index.scss'
+import { AuthProvider, useAuth } from '../auth'
-export default function App({ Component, pageProps }) {
- const store = useStore(pageProps.initialReduxState)
+// 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 })
+ return (
+ <Provider store={store}>
+ <Component {...pageProps} />
+ </Provider>
+ )
+}
+export default function App(props) {
return (
<>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
<meta name="theme-color" content="#00A6D6" />
</Head>
- <Provider store={store}>
- <Component {...pageProps} />
- </Provider>
+ <AuthProvider>
+ <Inner {...props} />
+ </AuthProvider>
</>
)
}
diff --git a/opendc-web/opendc-web-ui/src/api/users.js b/opendc-web/opendc-web-ui/src/pages/logout.js
index 3da030ad..e96e0605 100644
--- a/opendc-web/opendc-web-ui/src/api/users.js
+++ b/opendc-web/opendc-web-ui/src/pages/logout.js
@@ -20,20 +20,20 @@
* SOFTWARE.
*/
-import { request } from './index'
+import React from 'react'
+import Head from 'next/head'
+import AppNavbarContainer from '../containers/navigation/AppNavbarContainer'
-export function getUserByEmail(email) {
- return request(`users` + new URLSearchParams({ email }))
+function Logout() {
+ return (
+ <>
+ <Head>
+ <title>Logged Out - OpenDC</title>
+ </Head>
+ <AppNavbarContainer fullWidth={false} />
+ <span>Logged out successfully</span>
+ </>
+ )
}
-export function addUser(user) {
- return request('users', 'POST', { user })
-}
-
-export function getUser(userId) {
- return request(`users/${userId}`)
-}
-
-export function deleteUser(userId) {
- return request(`users/${userId}`, 'DELETE')
-}
+export default Logout
diff --git a/opendc-web/opendc-web-ui/src/pages/profile.js b/opendc-web/opendc-web-ui/src/pages/profile.js
deleted file mode 100644
index 97afc4d9..00000000
--- a/opendc-web/opendc-web-ui/src/pages/profile.js
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright (c) 2021 AtLarge Research
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-import React, { useState } from 'react'
-import Head from 'next/head'
-import { useDispatch } from 'react-redux'
-import AppNavbarContainer from '../containers/navigation/AppNavbarContainer'
-import ConfirmationModal from '../components/modals/ConfirmationModal'
-import { deleteCurrentUser } from '../redux/actions/users'
-import { useRequireAuth } from '../auth'
-
-const Profile = () => {
- useRequireAuth()
-
- const dispatch = useDispatch()
-
- const [isDeleteModalOpen, setDeleteModalOpen] = useState(false)
- const onDelete = (isConfirmed) => {
- if (isConfirmed) {
- dispatch(deleteCurrentUser())
- }
- setDeleteModalOpen(false)
- }
-
- return (
- <>
- <Head>
- <title>My Profile - OpenDC</title>
- </Head>
- <div className="full-height">
- <AppNavbarContainer fullWidth={false} />
- <div className="container text-page-container full-height">
- <button
- className="btn btn-danger mb-2 ml-auto mr-auto"
- style={{ maxWidth: 300 }}
- onClick={() => setDeleteModalOpen(true)}
- >
- Delete my account on OpenDC
- </button>
- <p className="text-muted text-center">
- This does not delete your Google account, but simply disconnects it from the OpenDC platform and
- deletes any project info that is associated with you (projects you own and any authorizations
- you may have on other projects).
- </p>
- </div>
- <ConfirmationModal
- title="Delete my account"
- message="Are you sure you want to delete your OpenDC account?"
- show={isDeleteModalOpen}
- callback={onDelete}
- />
- </div>
- </>
- )
-}
-
-export default Profile
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 05e9514f..8603c228 100644
--- a/opendc-web/opendc-web-ui/src/pages/projects/index.js
+++ b/opendc-web/opendc-web-ui/src/pages/projects/index.js
@@ -1,13 +1,13 @@
import React, { useEffect, useState } from 'react'
import Head from 'next/head'
import { useDispatch } from 'react-redux'
-import { fetchAuthorizationsOfCurrentUser } from '../../redux/actions/users'
import ProjectFilterPanel from '../../components/projects/FilterPanel'
import NewProjectContainer from '../../containers/projects/NewProjectContainer'
-import VisibleProjectList from '../../containers/projects/VisibleProjectAuthList'
+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()
@@ -15,7 +15,7 @@ function Projects() {
const dispatch = useDispatch()
const [filter, setFilter] = useState('SHOW_ALL')
- useEffect(() => dispatch(fetchAuthorizationsOfCurrentUser()), [])
+ useEffect(() => dispatch(fetchProjects()), [])
return (
<>
@@ -26,7 +26,7 @@ function Projects() {
<AppNavbarContainer fullWidth={false} />
<Container className="text-page-container">
<ProjectFilterPanel onSelect={setFilter} activeFilter={filter} />
- <VisibleProjectList filter={filter} />
+ <ProjectListContainer filter={filter} />
<NewProjectContainer />
</Container>
</div>
diff --git a/opendc-web/opendc-web-ui/src/redux/actions/auth.js b/opendc-web/opendc-web-ui/src/redux/actions/auth.js
deleted file mode 100644
index 38c1a782..00000000
--- a/opendc-web/opendc-web-ui/src/redux/actions/auth.js
+++ /dev/null
@@ -1,23 +0,0 @@
-export const LOG_IN = 'LOG_IN'
-export const LOG_IN_SUCCEEDED = 'LOG_IN_SUCCEEDED'
-export const LOG_OUT = 'LOG_OUT'
-
-export function logIn(payload) {
- return {
- type: LOG_IN,
- payload,
- }
-}
-
-export function logInSucceeded(payload) {
- return {
- type: LOG_IN_SUCCEEDED,
- payload,
- }
-}
-
-export function logOut() {
- return {
- type: LOG_OUT,
- }
-}
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 15158164..a6324c43 100644
--- a/opendc-web/opendc-web-ui/src/redux/actions/projects.js
+++ b/opendc-web/opendc-web-ui/src/redux/actions/projects.js
@@ -1,24 +1,35 @@
+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 (dispatch, getState) => {
- const { auth } = getState()
- dispatch({
- type: ADD_PROJECT,
- name,
- userId: auth.userId,
- })
+ return {
+ type: ADD_PROJECT,
+ name,
}
}
-export function addProjectSucceeded(authorization) {
+export function addProjectSucceeded(project) {
return {
type: ADD_PROJECT_SUCCEEDED,
- authorization,
+ project,
}
}
diff --git a/opendc-web/opendc-web-ui/src/redux/actions/users.js b/opendc-web/opendc-web-ui/src/redux/actions/users.js
deleted file mode 100644
index 4868ac34..00000000
--- a/opendc-web/opendc-web-ui/src/redux/actions/users.js
+++ /dev/null
@@ -1,37 +0,0 @@
-export const FETCH_AUTHORIZATIONS_OF_CURRENT_USER = 'FETCH_AUTHORIZATIONS_OF_CURRENT_USER'
-export const FETCH_AUTHORIZATIONS_OF_CURRENT_USER_SUCCEEDED = 'FETCH_AUTHORIZATIONS_OF_CURRENT_USER_SUCCEEDED'
-export const DELETE_CURRENT_USER = 'DELETE_CURRENT_USER'
-export const DELETE_CURRENT_USER_SUCCEEDED = 'DELETE_CURRENT_USER_SUCCEEDED'
-
-export function fetchAuthorizationsOfCurrentUser() {
- return (dispatch, getState) => {
- const { auth } = getState()
- dispatch({
- type: FETCH_AUTHORIZATIONS_OF_CURRENT_USER,
- userId: auth.userId,
- })
- }
-}
-
-export function fetchAuthorizationsOfCurrentUserSucceeded(authorizationsOfCurrentUser) {
- return {
- type: FETCH_AUTHORIZATIONS_OF_CURRENT_USER_SUCCEEDED,
- authorizationsOfCurrentUser,
- }
-}
-
-export function deleteCurrentUser() {
- return (dispatch, getState) => {
- const { auth } = getState()
- dispatch({
- type: DELETE_CURRENT_USER,
- userId: auth.userId,
- })
- }
-}
-
-export function deleteCurrentUserSucceeded() {
- return {
- type: DELETE_CURRENT_USER_SUCCEEDED,
- }
-}
diff --git a/opendc-web/opendc-web-ui/src/redux/index.js b/opendc-web/opendc-web-ui/src/redux/index.js
index c706752b..ee6ca3f5 100644
--- a/opendc-web/opendc-web-ui/src/redux/index.js
+++ b/opendc-web/opendc-web-ui/src/redux/index.js
@@ -1,40 +1,32 @@
import { useMemo } from 'react'
-import { applyMiddleware, compose, createStore } from 'redux'
+import { applyMiddleware, createStore } from 'redux'
import { createLogger } from 'redux-logger'
-import persistState from 'redux-localstorage'
import createSagaMiddleware from 'redux-saga'
import thunk from 'redux-thunk'
-import { authRedirectMiddleware } from '../auth'
import rootReducer from './reducers'
import rootSaga from './sagas'
import { viewportAdjustmentMiddleware } from './middleware/viewport-adjustment'
let store
-function initStore(initialState) {
- const sagaMiddleware = createSagaMiddleware()
+function initStore(initialState, ctx) {
+ const sagaMiddleware = createSagaMiddleware({ context: ctx })
- const middlewares = [thunk, sagaMiddleware, authRedirectMiddleware, viewportAdjustmentMiddleware]
+ const middlewares = [thunk, sagaMiddleware, viewportAdjustmentMiddleware]
if (process.env.NODE_ENV !== 'production') {
middlewares.push(createLogger())
}
- let enhancer = applyMiddleware(...middlewares)
-
- if (global.localStorage) {
- enhancer = compose(persistState('auth'), enhancer)
- }
-
- const configuredStore = createStore(rootReducer, enhancer)
+ const configuredStore = createStore(rootReducer, initialState, applyMiddleware(...middlewares))
sagaMiddleware.run(rootSaga)
store = configuredStore
return configuredStore
}
-export const initializeStore = (preloadedState) => {
- let _store = store ?? initStore(preloadedState)
+export const initializeStore = (preloadedState, ctx) => {
+ let _store = store ?? initStore(preloadedState, ctx)
// After navigating to a page with an initial Redux state, merge that state
// with the current state in the store, and create a new store
@@ -55,6 +47,6 @@ export const initializeStore = (preloadedState) => {
return _store
}
-export function useStore(initialState) {
- return useMemo(() => initializeStore(initialState), [initialState])
+export function useStore(initialState, ctx) {
+ return useMemo(() => initializeStore(initialState, ctx), [initialState, ctx])
}
diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/auth.js b/opendc-web/opendc-web-ui/src/redux/reducers/auth.js
deleted file mode 100644
index 399a4b10..00000000
--- a/opendc-web/opendc-web-ui/src/redux/reducers/auth.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { LOG_IN_SUCCEEDED, LOG_OUT } from '../actions/auth'
-
-export function auth(state = {}, action) {
- switch (action.type) {
- case LOG_IN_SUCCEEDED:
- return action.payload
- case LOG_OUT:
- return {}
- default:
- return state
- }
-}
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 9dff379b..b143d417 100644
--- a/opendc-web/opendc-web-ui/src/redux/reducers/index.js
+++ b/opendc-web/opendc-web-ui/src/redux/reducers/index.js
@@ -1,15 +1,14 @@
import { combineReducers } from 'redux'
-import { auth } from './auth'
import { construction } from './construction-mode'
import { currentPortfolioId, currentProjectId, currentScenarioId, currentTopologyId } from './current-ids'
import { interactionLevel } from './interaction-level'
import { map } from './map'
import { objects } from './objects'
-import { projectList } from './project-list'
+import { projects } from './projects'
const rootReducer = combineReducers({
objects,
- projectList,
+ projects,
construction,
map,
currentProjectId,
@@ -17,7 +16,6 @@ const rootReducer = combineReducers({
currentPortfolioId,
currentScenarioId,
interactionLevel,
- auth,
})
export default rootReducer
diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/project-list.js b/opendc-web/opendc-web-ui/src/redux/reducers/project-list.js
deleted file mode 100644
index ad803db0..00000000
--- a/opendc-web/opendc-web-ui/src/redux/reducers/project-list.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { combineReducers } from 'redux'
-import { ADD_PROJECT_SUCCEEDED, DELETE_PROJECT_SUCCEEDED } from '../actions/projects'
-import { FETCH_AUTHORIZATIONS_OF_CURRENT_USER_SUCCEEDED } from '../actions/users'
-
-export function authorizationsOfCurrentUser(state = [], action) {
- switch (action.type) {
- case FETCH_AUTHORIZATIONS_OF_CURRENT_USER_SUCCEEDED:
- return action.authorizationsOfCurrentUser
- case ADD_PROJECT_SUCCEEDED:
- return [...state, action.authorization]
- case DELETE_PROJECT_SUCCEEDED:
- return state.filter((authorization) => authorization[1] !== action.id)
- default:
- return state
- }
-}
-
-export const projectList = combineReducers({ authorizationsOfCurrentUser })
diff --git a/opendc-web/opendc-web-ui/src/redux/reducers/projects.js b/opendc-web/opendc-web-ui/src/redux/reducers/projects.js
new file mode 100644
index 00000000..a920e47f
--- /dev/null
+++ b/opendc-web/opendc-web-ui/src/redux/reducers/projects.js
@@ -0,0 +1,14 @@
+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 6332b2fb..a8f44843 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/index.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/index.js
@@ -1,7 +1,6 @@
import { takeEvery } from 'redux-saga/effects'
-import { LOG_IN } from '../actions/auth'
import { ADD_PORTFOLIO, DELETE_PORTFOLIO, OPEN_PORTFOLIO_SUCCEEDED, UPDATE_PORTFOLIO } from '../actions/portfolios'
-import { ADD_PROJECT, DELETE_PROJECT, OPEN_PROJECT_SUCCEEDED } from '../actions/projects'
+import { ADD_PROJECT, DELETE_PROJECT, FETCH_PROJECTS, OPEN_PROJECT_SUCCEEDED } from '../actions/projects'
import {
ADD_TILE,
CANCEL_NEW_ROOM_CONSTRUCTION,
@@ -11,10 +10,8 @@ import {
import { ADD_UNIT, DELETE_MACHINE, DELETE_UNIT } from '../actions/topology/machine'
import { ADD_MACHINE, DELETE_RACK, EDIT_RACK_NAME } from '../actions/topology/rack'
import { ADD_RACK_TO_TILE, DELETE_ROOM, EDIT_ROOM_NAME } from '../actions/topology/room'
-import { DELETE_CURRENT_USER, FETCH_AUTHORIZATIONS_OF_CURRENT_USER } from '../actions/users'
import { onAddPortfolio, onDeletePortfolio, onOpenPortfolioSucceeded, onUpdatePortfolio } from './portfolios'
-import { onDeleteCurrentUser } from './profile'
-import { onOpenProjectSucceeded, onProjectAdd, onProjectDelete } from './projects'
+import { onFetchProjects, onOpenProjectSucceeded, onProjectAdd, onProjectDelete } from './projects'
import {
onAddMachine,
onAddRackToTile,
@@ -32,7 +29,6 @@ import {
onEditRoomName,
onStartNewRoomConstruction,
} from './topology'
-import { onFetchAuthorizationsOfCurrentUser, onFetchLoggedInUser } from './users'
import { ADD_TOPOLOGY, DELETE_TOPOLOGY } from '../actions/topologies'
import { ADD_SCENARIO, DELETE_SCENARIO, OPEN_SCENARIO_SUCCEEDED, UPDATE_SCENARIO } from '../actions/scenarios'
import { onAddScenario, onDeleteScenario, onOpenScenarioSucceeded, onUpdateScenario } from './scenarios'
@@ -40,14 +36,10 @@ import { onAddPrefab } from './prefabs'
import { ADD_PREFAB } from '../actions/prefabs'
export default function* rootSaga() {
- yield takeEvery(LOG_IN, onFetchLoggedInUser)
-
- yield takeEvery(FETCH_AUTHORIZATIONS_OF_CURRENT_USER, onFetchAuthorizationsOfCurrentUser)
+ yield takeEvery(FETCH_PROJECTS, onFetchProjects)
yield takeEvery(ADD_PROJECT, onProjectAdd)
yield takeEvery(DELETE_PROJECT, onProjectDelete)
- yield takeEvery(DELETE_CURRENT_USER, onDeleteCurrentUser)
-
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 82dbb935..e5fd092d 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/objects.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/objects.js
@@ -1,9 +1,8 @@
-import { call, put, select } from 'redux-saga/effects'
+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 { getUser } from '../../api/users'
import { getTopology, updateTopology } from '../../api/topologies'
import { uuid } from 'uuidv4'
@@ -42,9 +41,10 @@ function* fetchAndStoreObjects(objectType, apiCall) {
return objects
}
-export const fetchAndStoreProject = (id) => fetchAndStoreObject('project', id, call(getProject, id))
-
-export const fetchAndStoreUser = (id) => fetchAndStoreObject('user', id, call(getUser, id))
+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'])
@@ -52,10 +52,11 @@ export const fetchAndStoreTopology = function* (id) {
const tileStore = yield select(OBJECT_SELECTORS['tile'])
const rackStore = yield select(OBJECT_SELECTORS['rack'])
const machineStore = yield select(OBJECT_SELECTORS['machine'])
+ const auth = yield getContext('auth')
let topology = topologyStore[id]
if (!topology) {
- const fullTopology = yield call(getTopology, id)
+ const fullTopology = yield call(getTopology, auth, id)
for (let roomIdx in fullTopology.rooms) {
const fullRoom = fullTopology.rooms[roomIdx]
@@ -142,8 +143,8 @@ const generateIdIfNotPresent = (obj) => {
export const updateTopologyOnServer = function* (id) {
const topology = yield getTopologyAsObject(id, true)
-
- yield call(updateTopology, topology)
+ const auth = yield getContext('auth')
+ yield call(updateTopology, auth, topology)
}
export const getTopologyAsObject = function* (id, keepIds) {
@@ -217,10 +218,14 @@ export const getRackById = function* (id, keepIds) {
}
}
-export const fetchAndStoreAllTraces = () => fetchAndStoreObjects('trace', call(getAllTraces))
+export const fetchAndStoreAllTraces = function* () {
+ const auth = yield getContext('auth')
+ return yield fetchAndStoreObjects('trace', call(getAllTraces, auth))
+}
export const fetchAndStoreAllSchedulers = function* () {
- const objects = yield call(getAllSchedulers)
+ const auth = yield getContext('auth')
+ const objects = yield call(getAllSchedulers, auth)
for (let object of objects) {
object._id = object.name
yield put(addToStore('scheduler', object))
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 8ddf888d..340cb490 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/portfolios.js
@@ -1,4 +1,4 @@
-import { call, put, select, delay } from 'redux-saga/effects'
+import { call, put, select, delay, getContext } from 'redux-saga/effects'
import { addPropToStoreObject, addToStore } from '../actions/objects'
import { addPortfolio, deletePortfolio, getPortfolio, updatePortfolio } from '../../api/portfolios'
import { getProject } from '../../api/projects'
@@ -8,7 +8,8 @@ import { getScenario } from '../../api/scenarios'
export function* onOpenPortfolioSucceeded(action) {
try {
- const project = yield call(getProject, action.projectId)
+ const auth = yield getContext('auth')
+ const project = yield call(getProject, auth, action.projectId)
yield put(addToStore('project', project))
yield fetchAndStoreAllTopologiesOfProject(project._id)
yield fetchPortfoliosOfProject()
@@ -66,11 +67,12 @@ export function* fetchPortfoliosOfProject() {
export function* fetchPortfolioWithScenarios(portfolioId) {
try {
- const portfolio = yield call(getPortfolio, portfolioId)
+ const auth = yield getContext('auth')
+ const portfolio = yield call(getPortfolio, auth, portfolioId)
yield put(addToStore('portfolio', portfolio))
for (let i in portfolio.scenarioIds) {
- const scenario = yield call(getScenario, portfolio.scenarioIds[i])
+ const scenario = yield call(getScenario, auth, portfolio.scenarioIds[i])
yield put(addToStore('scenario', scenario))
}
return portfolio
@@ -82,9 +84,10 @@ export function* fetchPortfolioWithScenarios(portfolioId) {
export function* onAddPortfolio(action) {
try {
const currentProjectId = yield select((state) => state.currentProjectId)
-
+ const auth = yield getContext('auth')
const portfolio = yield call(
addPortfolio,
+ auth,
currentProjectId,
Object.assign({}, action.portfolio, {
projectId: currentProjectId,
@@ -106,7 +109,8 @@ export function* onAddPortfolio(action) {
export function* onUpdatePortfolio(action) {
try {
- const portfolio = yield call(updatePortfolio, action.portfolio._id, action.portfolio)
+ const auth = yield getContext('auth')
+ const portfolio = yield call(updatePortfolio, auth, action.portfolio._id, action.portfolio)
yield put(addToStore('portfolio', portfolio))
} catch (error) {
console.error(error)
@@ -115,7 +119,8 @@ export function* onUpdatePortfolio(action) {
export function* onDeletePortfolio(action) {
try {
- yield call(deletePortfolio, action.id)
+ const auth = yield getContext('auth')
+ yield call(deletePortfolio, auth, action.id)
const currentProjectId = yield select((state) => state.currentProjectId)
const portfolioIds = yield select((state) => state.objects.project[currentProjectId].portfolioIds)
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js b/opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js
index 3158a219..ec679391 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/prefabs.js
@@ -1,4 +1,4 @@
-import { call, put, select } from 'redux-saga/effects'
+import { call, put, select, getContext } from 'redux-saga/effects'
import { addToStore } from '../actions/objects'
import { addPrefab } from '../../api/prefabs'
import { getRackById } from './objects'
@@ -7,7 +7,8 @@ export function* onAddPrefab(action) {
try {
const currentRackId = yield select((state) => state.objects.tile[state.interactionLevel.tileId].rackId)
const currentRackJson = yield getRackById(currentRackId, false)
- const prefab = yield call(addPrefab, { name: action.name, rack: currentRackJson })
+ const auth = yield getContext('auth')
+ const prefab = yield call(addPrefab, auth, { name: action.name, rack: currentRackJson })
yield put(addToStore('prefab', prefab))
} catch (error) {
console.error(error)
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/profile.js b/opendc-web/opendc-web-ui/src/redux/sagas/profile.js
deleted file mode 100644
index e187b765..00000000
--- a/opendc-web/opendc-web-ui/src/redux/sagas/profile.js
+++ /dev/null
@@ -1,12 +0,0 @@
-import { call, put } from 'redux-saga/effects'
-import { deleteCurrentUserSucceeded } from '../actions/users'
-import { deleteUser } from '../../api/users'
-
-export function* onDeleteCurrentUser(action) {
- try {
- yield call(deleteUser, action.userId)
- yield put(deleteCurrentUserSucceeded())
- } 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 ecd9a7c9..506df6ed 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/projects.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/projects.js
@@ -1,14 +1,15 @@
-import { call, put } from 'redux-saga/effects'
+import { call, put, getContext } from 'redux-saga/effects'
import { addToStore } from '../actions/objects'
-import { addProjectSucceeded, deleteProjectSucceeded } from '../actions/projects'
-import { addProject, deleteProject, getProject } from '../../api/projects'
+import { addProjectSucceeded, deleteProjectSucceeded, fetchProjectsSucceeded } from '../actions/projects'
+import { addProject, deleteProject, getProject, getProjects } from '../../api/projects'
import { fetchAndStoreAllTopologiesOfProject } from './topology'
import { fetchAndStoreAllSchedulers, fetchAndStoreAllTraces } from './objects'
import { fetchPortfoliosOfProject } from './portfolios'
export function* onOpenProjectSucceeded(action) {
try {
- const project = yield call(getProject, action.id)
+ const auth = yield getContext('auth')
+ const project = yield call(getProject, auth, action.id)
yield put(addToStore('project', project))
yield fetchAndStoreAllTopologiesOfProject(action.id, true)
@@ -22,17 +23,10 @@ export function* onOpenProjectSucceeded(action) {
export function* onProjectAdd(action) {
try {
- const project = yield call(addProject, { name: action.name })
+ const auth = yield getContext('auth')
+ const project = yield call(addProject, auth, { name: action.name })
yield put(addToStore('project', project))
-
- const authorization = {
- projectId: project._id,
- userId: action.userId,
- authorizationLevel: 'OWN',
- project,
- }
- yield put(addToStore('authorization', authorization))
- yield put(addProjectSucceeded([authorization.userId, authorization.projectId]))
+ yield put(addProjectSucceeded(project))
} catch (error) {
console.error(error)
}
@@ -40,9 +34,20 @@ export function* onProjectAdd(action) {
export function* onProjectDelete(action) {
try {
- yield call(deleteProject, action.id)
+ 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 a5463fa6..bdb7c45d 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/scenarios.js
@@ -1,4 +1,4 @@
-import { call, put, select } from 'redux-saga/effects'
+import { call, put, select, getContext } from 'redux-saga/effects'
import { addPropToStoreObject, addToStore } from '../actions/objects'
import { getProject } from '../../api/projects'
import { fetchAndStoreAllSchedulers, fetchAndStoreAllTraces } from './objects'
@@ -8,7 +8,8 @@ import { fetchPortfolioWithScenarios, watchForPortfolioResults } from './portfol
export function* onOpenScenarioSucceeded(action) {
try {
- const project = yield call(getProject, action.projectId)
+ const auth = yield getContext('auth')
+ const project = yield call(getProject, auth, action.projectId)
yield put(addToStore('project', project))
yield fetchAndStoreAllTopologiesOfProject(project._id)
yield fetchAndStoreAllSchedulers()
@@ -23,7 +24,8 @@ export function* onOpenScenarioSucceeded(action) {
export function* onAddScenario(action) {
try {
- const scenario = yield call(addScenario, action.scenario.portfolioId, action.scenario)
+ const auth = yield getContext('auth')
+ const scenario = yield call(addScenario, auth, action.scenario.portfolioId, action.scenario)
yield put(addToStore('scenario', scenario))
const scenarioIds = yield select((state) => state.objects.portfolio[action.scenario.portfolioId].scenarioIds)
@@ -40,7 +42,8 @@ export function* onAddScenario(action) {
export function* onUpdateScenario(action) {
try {
- const scenario = yield call(updateScenario, action.scenario._id, action.scenario)
+ const auth = yield getContext('auth')
+ const scenario = yield call(updateScenario, auth, action.scenario._id, action.scenario)
yield put(addToStore('scenario', scenario))
} catch (error) {
console.error(error)
@@ -49,7 +52,8 @@ export function* onUpdateScenario(action) {
export function* onDeleteScenario(action) {
try {
- yield call(deleteScenario, action.id)
+ const auth = yield getContext('auth')
+ yield call(deleteScenario, auth, action.id)
const currentPortfolioId = yield select((state) => state.currentPortfolioId)
const scenarioIds = yield select((state) => state.objects.portfolio[currentPortfolioId].scenarioIds)
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 65f97cc9..e5fd3d39 100644
--- a/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
+++ b/opendc-web/opendc-web-ui/src/redux/sagas/topology.js
@@ -1,4 +1,4 @@
-import { call, put, select } from 'redux-saga/effects'
+import { call, put, select, getContext } from 'redux-saga/effects'
import { goDownOneInteractionLevel } from '../actions/interaction-level'
import {
addIdToStoreObjectListProp,
@@ -50,8 +50,10 @@ export function* onAddTopology(action) {
topologyToBeCreated = { name: action.name, rooms: [] }
}
+ const auth = yield getContext('auth')
const topology = yield call(
addTopology,
+ auth,
Object.assign({}, topologyToBeCreated, {
projectId: currentProjectId,
})
@@ -79,7 +81,8 @@ export function* onDeleteTopology(action) {
yield put(setCurrentTopology(topologyIds.filter((t) => t !== action.id)[0]))
}
- yield call(deleteTopology, action.id)
+ const auth = yield getContext('auth')
+ yield call(deleteTopology, auth, action.id)
yield put(
addPropToStoreObject('project', currentProjectId, {
diff --git a/opendc-web/opendc-web-ui/src/redux/sagas/users.js b/opendc-web/opendc-web-ui/src/redux/sagas/users.js
deleted file mode 100644
index 88c424b5..00000000
--- a/opendc-web/opendc-web-ui/src/redux/sagas/users.js
+++ /dev/null
@@ -1,44 +0,0 @@
-import { call, put } from 'redux-saga/effects'
-import { logInSucceeded } from '../actions/auth'
-import { addToStore } from '../actions/objects'
-import { fetchAuthorizationsOfCurrentUserSucceeded } from '../actions/users'
-import { performTokenSignIn } from '../../api/token-signin'
-import { addUser } from '../../api/users'
-import { saveAuthLocalStorage } from '../../auth'
-import { fetchAndStoreProject, fetchAndStoreUser } from './objects'
-
-export function* onFetchLoggedInUser(action) {
- try {
- const tokenResponse = yield call(performTokenSignIn, action.payload.authToken)
-
- let userId = tokenResponse.userId
-
- if (tokenResponse.isNewUser) {
- saveAuthLocalStorage({ authToken: action.payload.authToken })
- const newUser = yield call(addUser, action.payload)
- userId = newUser._id
- }
-
- yield put(logInSucceeded(Object.assign({ userId }, action.payload)))
- } catch (error) {
- console.error(error)
- }
-}
-
-export function* onFetchAuthorizationsOfCurrentUser(action) {
- try {
- const user = yield call(fetchAndStoreUser, action.userId)
-
- for (const authorization of user.authorizations) {
- authorization.userId = action.userId
- yield put(addToStore('authorization', authorization))
- yield fetchAndStoreProject(authorization.projectId)
- }
-
- const authorizationIds = user.authorizations.map((authorization) => [action.userId, authorization.projectId])
-
- yield put(fetchAuthorizationsOfCurrentUserSucceeded(authorizationIds))
- } catch (error) {
- console.error(error)
- }
-}
diff --git a/opendc-web/opendc-web-ui/src/shapes.js b/opendc-web/opendc-web-ui/src/shapes.js
index 9aeb99d8..6c29eab0 100644
--- a/opendc-web/opendc-web-ui/src/shapes.js
+++ b/opendc-web/opendc-web-ui/src/shapes.js
@@ -1,3 +1,25 @@
+/*
+ * Copyright (c) 2021 AtLarge Research
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
import PropTypes from 'prop-types'
export const User = PropTypes.shape({
diff --git a/opendc-web/opendc-web-ui/yarn.lock b/opendc-web/opendc-web-ui/yarn.lock
index 9aed2596..f8bfc180 100644
--- a/opendc-web/opendc-web-ui/yarn.lock
+++ b/opendc-web/opendc-web-ui/yarn.lock
@@ -2,6 +2,26 @@
# yarn lockfile v1
+"@auth0/auth0-react@^1.5.0":
+ version "1.5.0"
+ resolved "https://registry.yarnpkg.com/@auth0/auth0-react/-/auth0-react-1.5.0.tgz#f2dcfef1cab59f8555ade4e2b5ec13befce67395"
+ integrity sha512-LFJUd3V6aKCKxblJvrz3JIHzyBCz2X3ax5RdSjxZwGr5XxPwdhogeBWcyijnuB8moKD2ncl56OpOslbVGLIJ/w==
+ dependencies:
+ "@auth0/auth0-spa-js" "^1.15.0"
+
+"@auth0/auth0-spa-js@^1.15.0":
+ version "1.15.0"
+ resolved "https://registry.yarnpkg.com/@auth0/auth0-spa-js/-/auth0-spa-js-1.15.0.tgz#9fa563b7b2e49dc4c6a465a0c240078322e80159"
+ integrity sha512-d/crchAbhncl9irIMuw1zNSZgX+id0U7mzASQr2htMJ73JCYaAvBSdGXL0WcYS4yBm1Xsx1JYm3b5tEZ5p/ncg==
+ dependencies:
+ abortcontroller-polyfill "^1.7.1"
+ browser-tabs-lock "^1.2.13"
+ core-js "^3.11.0"
+ es-cookie "^1.3.2"
+ fast-text-encoding "^1.0.3"
+ promise-polyfill "^8.2.0"
+ unfetch "^4.2.0"
+
"@babel/code-frame@7.12.11":
version "7.12.11"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.12.11.tgz#f4ad435aa263db935b8f10f2c552d23fb716a63f"
@@ -325,6 +345,11 @@
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.0.0.tgz#165aae4819ad2174a17476dbe66feebd549556c0"
integrity sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==
+abortcontroller-polyfill@^1.7.1:
+ version "1.7.3"
+ resolved "https://registry.yarnpkg.com/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.3.tgz#1b5b487bd6436b5b764fd52a612509702c3144b5"
+ integrity sha512-zetDJxd89y3X99Kvo4qFx8GKlt6GsvN3UcRZHwU6iFA/0KiOmhkTVhe8oRoTBiTVPZu09x3vCra47+w8Yz1+2Q==
+
aggregate-error@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.1.0.tgz#92670ff50f5359bdb7a3e0d40d0ec30c5737687a"
@@ -479,6 +504,13 @@ brorand@^1.0.1, brorand@^1.1.0:
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=
+browser-tabs-lock@^1.2.13:
+ version "1.2.14"
+ resolved "https://registry.yarnpkg.com/browser-tabs-lock/-/browser-tabs-lock-1.2.14.tgz#f4ba30810d20199a1858c102da1f91e339250728"
+ integrity sha512-ssSpCRcvFe4vc098LDnrJOQDfZiG35KhQGB9hthTbwJk5mmUkePwhcMlW61NH3YuIE2Y9uGLqf9yxEBKbaDlaw==
+ dependencies:
+ lodash ">=4.17.21"
+
browserify-aes@^1.0.0, browserify-aes@^1.0.4:
version "1.2.0"
resolved "https://registry.yarnpkg.com/browserify-aes/-/browserify-aes-1.2.0.tgz#326734642f403dabc3003209853bb70ad428ef48"
@@ -755,6 +787,11 @@ convert-source-map@1.7.0:
dependencies:
safe-buffer "~5.1.1"
+core-js@^3.11.0:
+ version "3.12.1"
+ resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.12.1.tgz#6b5af4ff55616c08a44d386f1f510917ff204112"
+ integrity sha512-Ne9DKPHTObRuB09Dru5AjwKjY4cJHVGu+y5f7coGn1E9Grkc3p2iBwE9AI/nJzsE29mQF7oq+mhYYRqOMFN1Bw==
+
core-util-is@~1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
@@ -1104,6 +1141,11 @@ es-abstract@^1.18.0-next.1, es-abstract@^1.18.0-next.2:
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.0"
+es-cookie@^1.3.2:
+ version "1.3.2"
+ resolved "https://registry.yarnpkg.com/es-cookie/-/es-cookie-1.3.2.tgz#80e831597f72a25721701bdcb21d990319acd831"
+ integrity sha512-UTlYYhXGLOy05P/vKVT2Ui7WtC7NiRzGtJyAKKn32g5Gvcjn7KAClLPWlipCtxIus934dFg9o9jXiBL0nP+t9Q==
+
es-to-primitive@^1.2.1:
version "1.2.1"
resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
@@ -1181,6 +1223,11 @@ fast-equals@^2.0.0:
resolved "https://registry.yarnpkg.com/fast-equals/-/fast-equals-2.0.2.tgz#f4029b437d9e8184b807233bdbcd371e3bc73cf7"
integrity sha512-ehHsR38w6sqy5E0QrWQxclb+zl3ulNsgCVWt1cMoZ6QBFgtkr4lKZWpQP1kfEFn6bWnm78pmiDGak+zUvQ2/DQ==
+fast-text-encoding@^1.0.3:
+ version "1.0.3"
+ resolved "https://registry.yarnpkg.com/fast-text-encoding/-/fast-text-encoding-1.0.3.tgz#ec02ac8e01ab8a319af182dae2681213cfe9ce53"
+ integrity sha512-dtm4QZH9nZtcDt8qJiOH9fcQd1NAgi+K1O2DbE6GG1PPCK/BWfOH3idCTRQ4ImXRUOyopDEgDEnVEE7Y/2Wrig==
+
figures@^3.2.0:
version "3.2.0"
resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
@@ -1702,7 +1749,7 @@ lodash.throttle@^4.1.1:
resolved "https://registry.yarnpkg.com/lodash.throttle/-/lodash.throttle-4.1.1.tgz#c23e91b710242ac70c37f1e1cda9274cc39bf2f4"
integrity sha1-wj6RtxAkKscMN/HhzaknTMOb8vQ=
-lodash@^4.17.13, lodash@^4.17.19:
+lodash@>=4.17.21, lodash@^4.17.13, lodash@^4.17.19:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -2168,7 +2215,12 @@ process@0.11.10, process@^0.11.10:
resolved "https://registry.yarnpkg.com/process/-/process-0.11.10.tgz#7332300e840161bda3e69a1d1d91a7d4bc16f182"
integrity sha1-czIwDoQBYb2j5podHZGn1LwW8YI=
-prop-types@15.7.2, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@~15.7.2:
+promise-polyfill@^8.2.0:
+ version "8.2.0"
+ resolved "https://registry.yarnpkg.com/promise-polyfill/-/promise-polyfill-8.2.0.tgz#367394726da7561457aba2133c9ceefbd6267da0"
+ integrity sha512-k/TC0mIcPVF6yHhUvwAp7cvL6I2fFV7TzF1DuGPI8mBh4QQazf36xCKEHKTZKRysEoTQoQdKyP25J8MPJp7j5g==
+
+prop-types@15.7.2, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@~15.7.2:
version "15.7.2"
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5"
integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==
@@ -2275,14 +2327,6 @@ react-fontawesome@~1.7.1:
dependencies:
prop-types "^15.5.6"
-react-google-login@~5.1.14:
- version "5.1.25"
- resolved "https://registry.yarnpkg.com/react-google-login/-/react-google-login-5.1.25.tgz#6bf5b4c8ce4e9e64a4c80eb14b80c9c6bd07bf9b"
- integrity sha512-N7SZkjTEX9NsC3hywXs68SPJWAHo6M19Bs1OIFPirG0yomGF7KnbKjSqoiIfxz1V7fKAt8bkfBAzogwnGWYTeQ==
- dependencies:
- "@types/react" "*"
- prop-types "^15.6.0"
-
react-hotkeys@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/react-hotkeys/-/react-hotkeys-2.0.0.tgz#a7719c7340cbba888b0e9184f806a9ec0ac2c53f"
@@ -2463,11 +2507,6 @@ reduce-css-calc@^2.1.8:
css-unit-converter "^1.1.1"
postcss-value-parser "^3.3.0"
-redux-localstorage@^0.4.1:
- version "0.4.1"
- resolved "https://registry.yarnpkg.com/redux-localstorage/-/redux-localstorage-0.4.1.tgz#faf6d719c581397294d811473ffcedee065c933c"
- integrity sha1-+vbXGcWBOXKU2BFHP/zt7gZckzw=
-
redux-logger@~3.0.6:
version "3.0.6"
resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf"
@@ -2977,6 +3016,11 @@ unbox-primitive@^1.0.0:
has-symbols "^1.0.2"
which-boxed-primitive "^1.0.2"
+unfetch@^4.2.0:
+ version "4.2.0"
+ resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be"
+ integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==
+
unpipe@1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"