summaryrefslogtreecommitdiff
path: root/opendc-web/opendc-web-ui/src/pages
diff options
context:
space:
mode:
authorFabian Mastenbroek <mail.fabianm@gmail.com>2022-04-04 17:00:31 +0200
committerGitHub <noreply@github.com>2022-04-04 17:00:31 +0200
commit38769373c7e89783d33849283586bfa0b62e8251 (patch)
tree4fda128ee6b30018c1aa14c584cc53ade80e67f7 /opendc-web/opendc-web-ui/src/pages
parent6021aa4278bebb34bf5603ead4b5daeabcdc4c19 (diff)
parent527ae2230f5c2dd22f496f45d5d8e3bd4acdb854 (diff)
merge: Migrate to Quarkus-based web API
This pull request changes the web API to a Quarkus-based version. Currently, the OpenDC web API is written in Python (using Flask). Although Python is a powerful language to develop web services, having another language next to Kotlin/Java and JavaScript introduces some challenges. For instance, the web API and UI lack integration with our Gradle-based build pipeline and require additional steps from the developer to start working with. Furthermore, deploying OpenDC requires having Python installed in addition to the JVM. By converting the web API into a Quarkus application, we can enjoy further integration with our Gradle-based build pipeline and simplify the development/deployment process of OpenDC, by requiring only the JVM and Node to work with OpenDC. ## Implementation Notes :hammer_and_pick: * Move build dependencies into version catalog * Design unified communication protocol * Add Quarkus API implementation * Add new web client implementation * Update runner to use new web client * Fix compatibility with React.js UI * Remove Python build steps from CI pipeline * Update Docker deployment for new web API * Remove obsolete database configuration ## External Dependencies :four_leaf_clover: * Quarkus ## Breaking API Changes :warning: * The new web API only supports SQL-based databases for storing user-data, as opposed to MongoDB currently. We intend to use H2 for development and Postgres for production.
Diffstat (limited to 'opendc-web/opendc-web-ui/src/pages')
-rw-r--r--opendc-web/opendc-web-ui/src/pages/_app.js14
-rw-r--r--opendc-web/opendc-web-ui/src/pages/_document.js13
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js6
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js25
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/[project]/topologies/[topology].js36
-rw-r--r--opendc-web/opendc-web-ui/src/pages/projects/index.js16
6 files changed, 66 insertions, 44 deletions
diff --git a/opendc-web/opendc-web-ui/src/pages/_app.js b/opendc-web/opendc-web-ui/src/pages/_app.js
index 900ff405..4861f5c1 100644
--- a/opendc-web/opendc-web-ui/src/pages/_app.js
+++ b/opendc-web/opendc-web-ui/src/pages/_app.js
@@ -22,6 +22,7 @@
import PropTypes from 'prop-types'
import Head from 'next/head'
+import Script from 'next/script'
import { Provider } from 'react-redux'
import { useNewQueryClient } from '../data/query'
import { useStore } from '../redux'
@@ -91,6 +92,19 @@ export default function App(props) {
<Inner {...props} />
</AuthProvider>
</Sentry.ErrorBoundary>
+ {/* Google Analytics */}
+ <Script async src="https://www.googletagmanager.com/gtag/js?id=UA-84285092-3" />
+ <Script
+ id="gtag"
+ dangerouslySetInnerHTML={{
+ __html: `
+ window.dataLayer = window.dataLayer || [];
+ function gtag(){dataLayer.push(arguments);}
+ gtag('js', new Date());
+ gtag('config', 'UA-84285092-3');
+ `,
+ }}
+ />
</>
)
}
diff --git a/opendc-web/opendc-web-ui/src/pages/_document.js b/opendc-web/opendc-web-ui/src/pages/_document.js
index 51d8d3e0..011bf4da 100644
--- a/opendc-web/opendc-web-ui/src/pages/_document.js
+++ b/opendc-web/opendc-web-ui/src/pages/_document.js
@@ -69,19 +69,6 @@ class OpenDCDocument extends Document {
href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap"
rel="stylesheet"
/>
-
- {/* Google Analytics */}
- <script async src="https://www.googletagmanager.com/gtag/js?id=UA-84285092-3" />
- <script
- dangerouslySetInnerHTML={{
- __html: `
- window.dataLayer = window.dataLayer || [];
- function gtag(){dataLayer.push(arguments);}
- gtag('js', new Date());
- gtag('config', 'UA-84285092-3');
- `,
- }}
- />
</Head>
<body>
<Main />
diff --git a/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js b/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js
index c07a2c31..39fcb4f3 100644
--- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js
+++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/index.js
@@ -40,9 +40,9 @@ import BreadcrumbLink from '../../../components/util/BreadcrumbLink'
function Project() {
const router = useRouter()
- const { project: projectId } = router.query
+ const projectId = +router.query['project']
- const { data: project } = useProject(projectId)
+ const { data: project } = useProject(+projectId)
const breadcrumb = (
<Breadcrumb>
@@ -57,7 +57,7 @@ function Project() {
const contextSelectors = (
<ContextSelectionSection>
- <ProjectSelector projectId={projectId} />
+ <ProjectSelector activeProject={project} />
</ContextSelectionSection>
)
diff --git a/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js b/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js
index d1533d98..68345d0b 100644
--- a/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js
+++ b/opendc-web/opendc-web-ui/src/pages/projects/[project]/portfolios/[portfolio].js
@@ -20,6 +20,7 @@
* SOFTWARE.
*/
+import dynamic from 'next/dynamic'
import { useRouter } from 'next/router'
import Head from 'next/head'
import React, { useRef } from 'react'
@@ -42,18 +43,24 @@ import PortfolioSelector from '../../../../components/context/PortfolioSelector'
import ProjectSelector from '../../../../components/context/ProjectSelector'
import BreadcrumbLink from '../../../../components/util/BreadcrumbLink'
import PortfolioOverview from '../../../../components/portfolios/PortfolioOverview'
-import PortfolioResults from '../../../../components/portfolios/PortfolioResults'
+import { usePortfolio } from '../../../../data/project'
+
+const PortfolioResults = dynamic(() => import('../../../../components/portfolios/PortfolioResults'))
/**
* Page that displays the results in a portfolio.
*/
function Portfolio() {
const router = useRouter()
- const { project: projectId, portfolio: portfolioId } = router.query
+ const projectId = +router.query['project']
+ const portfolioNumber = +router.query['portfolio']
const overviewRef = useRef(null)
const resultsRef = useRef(null)
+ const { data: portfolio } = usePortfolio(projectId, portfolioNumber)
+ const project = portfolio?.project
+
const breadcrumb = (
<Breadcrumb>
<BreadcrumbItem to="/projects" component={BreadcrumbLink}>
@@ -62,7 +69,11 @@ function Portfolio() {
<BreadcrumbItem to={`/projects/${projectId}`} component={BreadcrumbLink}>
Project details
</BreadcrumbItem>
- <BreadcrumbItem to={`/projects/${projectId}/portfolios/${portfolioId}`} component={BreadcrumbLink} isActive>
+ <BreadcrumbItem
+ to={`/projects/${projectId}/portfolios/${portfolioNumber}`}
+ component={BreadcrumbLink}
+ isActive
+ >
Portfolio
</BreadcrumbItem>
</Breadcrumb>
@@ -70,8 +81,8 @@ function Portfolio() {
const contextSelectors = (
<ContextSelectionSection>
- <ProjectSelector projectId={projectId} />
- <PortfolioSelector projectId={projectId} portfolioId={portfolioId} />
+ <ProjectSelector activeProject={project} />
+ <PortfolioSelector activePortfolio={portfolio} />
</ContextSelectionSection>
)
@@ -104,10 +115,10 @@ function Portfolio() {
</PageSection>
<PageSection isFilled>
<TabContent eventKey={0} id="overview" ref={overviewRef} aria-label="Overview tab">
- <PortfolioOverview portfolioId={portfolioId} />
+ <PortfolioOverview projectId={projectId} portfolioId={portfolioNumber} />
</TabContent>
<TabContent eventKey={1} id="results" ref={resultsRef} aria-label="Results tab" hidden>
- <PortfolioResults portfolioId={portfolioId} />
+ <PortfolioResults projectId={projectId} portfolioId={portfolioNumber} />
</TabContent>
</PageSection>
</AppPage>
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 f7188d9f..6297b8c3 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
@@ -26,7 +26,6 @@ import ContextSelectionSection from '../../../../components/context/ContextSelec
import ProjectSelector from '../../../../components/context/ProjectSelector'
import TopologySelector from '../../../../components/context/TopologySelector'
import TopologyOverview from '../../../../components/topologies/TopologyOverview'
-import { useProject } from '../../../../data/project'
import { useDispatch } from 'react-redux'
import React, { useEffect, useState } from 'react'
import Head from 'next/head'
@@ -45,6 +44,7 @@ import {
TextContent,
} from '@patternfly/react-core'
import BreadcrumbLink from '../../../../components/util/BreadcrumbLink'
+import { useTopology } from '../../../../data/topology'
import { goToRoom } from '../../../../redux/actions/interaction-level'
import { openTopology } from '../../../../redux/actions/topology'
@@ -55,16 +55,18 @@ const TopologyMap = dynamic(() => import('../../../../components/topologies/Topo
*/
function Topology() {
const router = useRouter()
- const { project: projectId, topology: topologyId } = router.query
+ const projectId = +router.query['project']
+ const topologyNumber = +router.query['topology']
- const { data: project } = useProject(projectId)
+ const { data: topology } = useTopology(projectId, topologyNumber)
+ const project = topology?.project
const dispatch = useDispatch()
useEffect(() => {
- if (topologyId) {
- dispatch(openTopology(topologyId))
+ if (topologyNumber) {
+ dispatch(openTopology(projectId, topologyNumber))
}
- }, [topologyId, dispatch])
+ }, [projectId, topologyNumber, dispatch])
const [activeTab, setActiveTab] = useState('overview')
@@ -76,7 +78,11 @@ function Topology() {
<BreadcrumbItem to={`/projects/${projectId}`} component={BreadcrumbLink}>
Project details
</BreadcrumbItem>
- <BreadcrumbItem to={`/projects/${projectId}/topologies/${topologyId}`} component={BreadcrumbLink} isActive>
+ <BreadcrumbItem
+ to={`/projects/${projectId}/topologies/${topologyNumber}`}
+ component={BreadcrumbLink}
+ isActive
+ >
Topology
</BreadcrumbItem>
</Breadcrumb>
@@ -84,8 +90,8 @@ function Topology() {
const contextSelectors = (
<ContextSelectionSection>
- <ProjectSelector projectId={projectId} />
- <TopologySelector projectId={projectId} topologyId={topologyId} />
+ <ProjectSelector activeProject={project} />
+ <TopologySelector activeTopology={topology} />
</ContextSelectionSection>
)
@@ -117,16 +123,22 @@ function Topology() {
<PageSection padding={activeTab === 'floor-plan' && { default: 'noPadding' }} isFilled>
<TabContent id="overview" aria-label="Overview tab" hidden={activeTab !== 'overview'}>
<TopologyOverview
- topologyId={topologyId}
+ projectId={projectId}
+ topologyNumber={topologyNumber}
onSelect={(type, obj) => {
if (type === 'room') {
- dispatch(goToRoom(obj._id))
+ dispatch(goToRoom(obj.id))
setActiveTab('floor-plan')
}
}}
/>
</TabContent>
- <TabContent id="floor-plan" aria-label="Floor Plan tab" className="pf-u-h-100" hidden={activeTab !== 'floor-plan'}>
+ <TabContent
+ id="floor-plan"
+ aria-label="Floor Plan tab"
+ className="pf-u-h-100"
+ hidden={activeTab !== 'floor-plan'}
+ >
<TopologyMap />
</TabContent>
</PageSection>
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 eb77701e..bb1fbd69 100644
--- a/opendc-web/opendc-web-ui/src/pages/projects/index.js
+++ b/opendc-web/opendc-web-ui/src/pages/projects/index.js
@@ -26,9 +26,8 @@ import ProjectFilterPanel from '../../components/projects/FilterPanel'
import { useAuth } from '../../auth'
import { AppPage } from '../../components/AppPage'
import { PageSection, PageSectionVariants, Text, TextContent } from '@patternfly/react-core'
-import { useProjects } from '../../data/project'
+import { useProjects, useDeleteProject } from '../../data/project'
import ProjectTable from '../../components/projects/ProjectTable'
-import { useMutation } from 'react-query'
import NewProject from '../../components/projects/NewProject'
const getVisibleProjects = (projects, filter, userId) => {
@@ -52,13 +51,12 @@ function Projects() {
const { user } = useAuth()
const { status, data: projects } = useProjects()
const [filter, setFilter] = useState('SHOW_ALL')
- const visibleProjects = useMemo(() => getVisibleProjects(projects ?? [], filter, user?.sub), [
- projects,
- filter,
- user?.sub,
- ])
+ const visibleProjects = useMemo(
+ () => getVisibleProjects(projects ?? [], filter, user?.sub),
+ [projects, filter, user?.sub]
+ )
- const { mutate: deleteProject } = useMutation('deleteProject')
+ const { mutate: deleteProject } = useDeleteProject()
return (
<AppPage>
@@ -76,7 +74,7 @@ function Projects() {
status={status}
isFiltering={filter !== 'SHOW_ALL'}
projects={visibleProjects}
- onDelete={(project) => deleteProject(project._id)}
+ onDelete={(project) => deleteProject(project.id)}
/>
<NewProject />
</PageSection>