From 4280191e49d14e09bd67af5799315ee55e09f52f Mon Sep 17 00:00:00 2001 From: John Wu <76966357+jwu-fisker@users.noreply.github.com> Date: Fri, 30 Apr 2021 12:58:31 -0700 Subject: [PATCH] CEC-179 Car download progress (#32) * Display download progress * Change default * Fix * Fix * Update readme * Update readme and defaults Fix Dockerfile --- .dockerignore | 1 - .env.template | 6 +- Dockerfile | 1 + README.md | 17 ++-- src/components/App/App.test.js | 8 +- .../App/__snapshots__/App.test.js.snap | 75 ++++++++++------- src/components/CarUpdates/Deploy/index.jsx | 11 ++- src/components/CarUpdates/Status/index.jsx | 30 ++++++- src/components/Cars/List/index.jsx | 2 +- src/components/Contexts/FileUploadContext.jsx | 15 ++-- .../Contexts/FileUploadContext.test.jsx | 12 ++- src/components/Contexts/UpdatesContext.jsx | 84 +++++++++++++++++++ .../Contexts/__mocks__/UpdatesContext.jsx | 2 + src/components/Layouts/SideMenu.jsx | 4 +- .../__snapshots__/SideMenu.test.jsx.snap | 4 +- .../Create/__snapshots__/Create.test.js.snap | 24 +++++- .../UpdatePackages/Create/index.jsx | 21 ++++- src/components/UpdatePackages/Edit/index.jsx | 2 +- src/components/UpdatePackages/List/index.jsx | 4 +- src/services/__mocks__/updates.js | 8 +- src/services/updates.js | 9 ++ 21 files changed, 268 insertions(+), 72 deletions(-) diff --git a/.dockerignore b/.dockerignore index 7363dc7..d95674b 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,6 +1,5 @@ **/.classpath **/.dockerignore -**/.env **/.git **/.gitignore **/.project diff --git a/.env.template b/.env.template index 86292db..e3ba686 100644 --- a/.env.template +++ b/.env.template @@ -1,3 +1,3 @@ -REACT_APP_AUTH_SERVICE_URL = https://dev-auth.fiskerdps.com -REACT_APP_UPLOAD_SERVICE_URL = https://gw-dev.fiskerdps.com -REACT_APP_AUTH_CALLBACK_URL = https://dev-ota-admin.fiskerdps.com/ \ No newline at end of file +REACT_APP_AUTH_SERVICE_URL = http://localhost/compute_auth +REACT_APP_UPLOAD_SERVICE_URL = http://localhost/ota_update +REACT_APP_AUTH_CALLBACK_URL = http://localhost:3000 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 561d10b..b08537a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,4 +8,5 @@ RUN npm run build FROM nginx:alpine COPY --from=builder build /usr/share/nginx/html +COPY .env /usr/share/nginx/html/.env COPY nginx.conf /etc/nginx/conf.d/default.conf \ No newline at end of file diff --git a/README.md b/README.md index b97dc56..f9c3bd2 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,23 @@ # Fisker Admin Portal -Front-end web application for administarting OTA services +Front-end web application for administrating services # Setup -Run `./run.sh` from the terminal or +Running locally 1. Install Node 12 2. Run `npm install` -3. Setup environment variables listed in .env.template -4. Or copy .env.template to .env -5. Edit .env with the service urls for authentication and api services +3. Copy .env.template to .env and edit the service urls for authentication and api services +4. Run `./run.sh` from the terminal +5. Access portal at localhost:3000 + +Running Docker container + +1. Copy .env.template to .env and edit the service urls for authentication and api services +2. Build the image `docker build -t fiskerinc/portal .` +3. Start the container `docker run -p 3000:80 fiskerinc/portal` +4. Access portal at localhost:3000 ## Available Scripts diff --git a/src/components/App/App.test.js b/src/components/App/App.test.js index b66a74a..f819255 100644 --- a/src/components/App/App.test.js +++ b/src/components/App/App.test.js @@ -99,7 +99,7 @@ describe("App", () => { it("Route /package-upload authenticated", async () => { setToken(TEST_AUTH_OBJECT); - await check("/package-upload", "h1", "Upload Update Package"); + await check("/package-upload", "h1", "Create Update Package"); }); it("Route /vehicle-add authenticated", async () => { @@ -109,17 +109,17 @@ describe("App", () => { it("Route /updates authenticated", async () => { setToken(TEST_AUTH_OBJECT); - await check("/updates", "h1", "Updates"); + await check("/updates", "h1", "Update Packages"); }); it("Route /update authenticated", async () => { setToken(TEST_AUTH_OBJECT); - await check("/update/1", "h1", "Update Package 1"); + await check("/update/1", "h1", "Edit Update Package 1"); }); it("Route /carupdate-deploy authenticated", async () => { setToken(TEST_AUTH_OBJECT); - await check("/carupdate-deploy/1", "h1", "[1] "); + await check("/carupdate-deploy/1", "h1", "Deploy [1]"); }); it("Route /carupdate-status authenticated", async () => { diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index aa5601e..e01a2c4 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -134,7 +134,7 @@ exports[`App Route / authenticated 1`] = ` - View Updates + View Packages - Create Updates + Create Packages - View Updates + View Packages - Create Updates + Create Packages - [1] + Deploy + [1]
- View Updates + View Packages - Create Updates + Create Packages - View Updates + View Packages - Create Updates + Create Packages - View Updates + View Packages - Create Updates + Create Packages - Upload Update Package + Create Update Package
Description +  *
@@ -1884,12 +1894,19 @@ exports[`App Route /package-upload authenticated 1`] = ` class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal MuiFormControl-fullWidth" >
@@ -1913,6 +1931,7 @@ exports[`App Route /package-upload authenticated 1`] = ` > Release Notes URL +  * @@ -2164,7 +2183,7 @@ exports[`App Route /page-not-found authenticated 1`] = ` - View Updates + View Packages
- Create Updates + Create Packages
- View Updates + View Packages - Create Updates + Create Packages - Update Package + Edit Update Package 1 - View Updates + View Packages - Create Updates + Create Packages - Updates + Update Packages
- View Updates + View Packages
- Create Updates + Create Packages - View Updates + View Packages - Create Updates + Create Packages - View Updates + View Packages - Create Updates + Create Packages { const [releaseNotesLink, setReleaseNotesLink] = useState(""); const [createDate, setCreateDate] = useState(""); const [selectedVehicles, setSelectedVehicles] = useState([]); + const [redirect, setRedirect] = useState(""); const classes = useStyles(); const theme = useTheme(); const handleVehiclesChange = (event) => { @@ -60,7 +61,7 @@ const MainForm = () => { setMessage( `Deployed ${packageName} ${version} to ${selectedVehicles.length} cars` ); - setSelectedVehicles([]); + setRedirect(`/carupdate-status/${packageid}`); } catch (e) { setMessage(e.message); } @@ -95,10 +96,14 @@ const MainForm = () => { setCreateDate(tsLocalDateTimeString(data.timestamp)); }, [packages]); + if (redirect.length > 0) { + return ; + } + return (
- {`[${packageid}] ${packageName} ${version}`} + Deploy {`${packageName} ${version} [${packageid}]`} { const { packageid } = useParams(); const classes = useStyles(); - const [pageSize, setPageSize] = useState(10); + const [pageSize, setPageSize] = useState(25); const [pageIndex, setPageIndex] = useState(0); const [viewVIN, setViewVIN] = useState(null); - const { getCarUpdates, carUpdates, totalCarUpdates, getPackages, packages, + startMonitor, + stopMonitor, } = useUpdatesContext(); const { setMessage } = useStatusContext(); const { @@ -54,6 +56,7 @@ const MainForm = () => { useEffect(() => { try { + stopMonitor(); getCarUpdates( { packageid, @@ -68,6 +71,19 @@ const MainForm = () => { // eslint-disable-next-line }, [pageIndex, pageSize, token]); + useEffect(() => { + try { + if (carUpdates.length === 0) return; + startMonitor(token); + } catch (e) { + setMessage(e.message); + } + return () => { + stopMonitor(); + }; + // eslint-disable-next-line + }, [carUpdates]); + const handleChangePageIndex = (event, newIndex) => { setPageIndex(newIndex); }; @@ -113,7 +129,15 @@ const MainForm = () => { {row.vin} - {row.status} + + {row.status} + {row.progress > 0 && ( + + )} + {LocalDateTimeString(row.created)} diff --git a/src/components/Cars/List/index.jsx b/src/components/Cars/List/index.jsx index 933e1da..cabb49b 100644 --- a/src/components/Cars/List/index.jsx +++ b/src/components/Cars/List/index.jsx @@ -23,7 +23,7 @@ import { LocalDateTimeString } from "../../../utils/dates"; const MainForm = () => { const classes = useStyles(); - const [pageSize, setPageSize] = useState(5); + const [pageSize, setPageSize] = useState(25); const [pageIndex, setPageIndex] = useState(0); const { getVehicles, vehicles, totalVehicles } = useVehicleContext(); const { setMessage } = useStatusContext(); diff --git a/src/components/Contexts/FileUploadContext.jsx b/src/components/Contexts/FileUploadContext.jsx index d021205..61da7ba 100644 --- a/src/components/Contexts/FileUploadContext.jsx +++ b/src/components/Contexts/FileUploadContext.jsx @@ -8,7 +8,6 @@ export const FileUploadProvider = ({ children }) => { const [progress, setProgress] = useState(0); const [status, setStatus] = useState(null); const [cancelUpload, setCancelUpload] = useState(null); - const [linkURL, setLinkURL] = useState(null); const [files, setFiles] = useState(null); const done = () => { @@ -45,6 +44,14 @@ export const FileUploadProvider = ({ children }) => { if (!accessToken || accessToken.length === 0) { throw new Error("Access token required"); } + + if (!formData.description || formData.description.length === 0) { + throw new Error("Package update description required"); + } + + if (!formData.releasenotes || formData.releasenotes.length === 0) { + throw new Error("Package update release notes link required"); + } }; const upload = async (formData, accessToken, uploadFiles) => { @@ -55,7 +62,6 @@ export const FileUploadProvider = ({ children }) => { const filename = file.name; setUploading(true); - setLinkURL(null); setProgress(0); setStatus(`Uploading ${filename}`); setCancelUpload(getCancelToken()); @@ -70,11 +76,11 @@ export const FileUploadProvider = ({ children }) => { if (data.message) { throw new Error(`${data.error}. ${data.message}`); } - const url = data && data.link ? data.link : "No URL available"; - setLinkURL(url); setStatus(`Uploaded ${filename}`); setCancelUpload(null); setProgress(100); + + return data; } catch (e) { setUploading(true); setStatus(`Error occured: ${e.message}`); @@ -88,7 +94,6 @@ export const FileUploadProvider = ({ children }) => { uploading, progress, status, - linkURL, files, upload, cancel, diff --git a/src/components/Contexts/FileUploadContext.test.jsx b/src/components/Contexts/FileUploadContext.test.jsx index 60cbfc3..d87b36b 100644 --- a/src/components/Contexts/FileUploadContext.test.jsx +++ b/src/components/Contexts/FileUploadContext.test.jsx @@ -7,6 +7,7 @@ import { fireEvent, waitFor, } from "@testing-library/react"; +import { useState } from "react"; import { setUploadFileDelay } from "../../services/uploadFile"; import { @@ -30,21 +31,26 @@ describe("FileUploadContext", () => { progress, uploading, status, - linkURL, upload, cancel, } = useFileUploadContext(); const { message, setMessage } = useStatusContext(); + const [link, setLink] = useState(null); const TEST_FILE = [{ name: "test.jpg", size: 0 }]; const TEST_ACCESSTOKEN = "ACCESSTOKEN"; const TEST_FORMDATA = { packagename: "TEST", version: "VERSION", vehicles: ["VIN"], + description: "TEST DESC", + releasenotes: "http://releasenotes.com", }; const exec = async (form, token, file) => { try { - await upload(form, token, file); + const data = await upload(form, token, file); + if (data.link) { + setLink(data.link); + } } catch (e) { setMessage(e.message); } @@ -56,7 +62,7 @@ describe("FileUploadContext", () => {
{progress.toString()}
{status}
{message}
-
{linkURL}
+
{link}
- Create Updates + Create Packages - Upload Update Package + Create Update Package
Description +  *
@@ -151,12 +160,19 @@ exports[`File Upload Form Should render 1`] = ` class="MuiFormControl-root MuiTextField-root MuiFormControl-marginNormal MuiFormControl-fullWidth" >
@@ -180,6 +197,7 @@ exports[`File Upload Form Should render 1`] = ` > Release Notes URL +  * diff --git a/src/components/UpdatePackages/Create/index.jsx b/src/components/UpdatePackages/Create/index.jsx index a6442b5..b4079e7 100644 --- a/src/components/UpdatePackages/Create/index.jsx +++ b/src/components/UpdatePackages/Create/index.jsx @@ -1,4 +1,4 @@ -import React, { useRef } from "react"; +import React, { useRef, useState } from "react"; import { Button, TextField, Typography } from "@material-ui/core"; import { DropzoneArea } from "material-ui-dropzone"; import { useUserContext } from "../../Contexts/UserContext"; @@ -9,6 +9,7 @@ import { } from "../../Contexts/FileUploadContext"; import ModalProgressBar from "../../ModalProgressBar"; import useStyles from "../../useStyles"; +import { Redirect } from "react-router"; const FileUploadZone = ({ classes, token }) => { const { setFiles } = useFileUploadContext(); @@ -39,9 +40,10 @@ const FileUploadZone = ({ classes, token }) => { }; const MainForm = () => { - const { uploading, upload, files } = useFileUploadContext(); + const { uploading, upload, files, cancel } = useFileUploadContext(); const { token } = useUserContext(); const { setMessage } = useStatusContext(); + const [redirect, setRedirect] = useState(null); const classes = useStyles(); const packagenameEl = useRef(null); const versionEl = useRef(null); @@ -59,17 +61,26 @@ const MainForm = () => { description: descEl.current.value, releasenotes: releasenotesEl.current.value, }; + const result = await upload(formData, authToken, files); - await upload(formData, authToken, files); + if (!result || result.error) return; + + cancel(); + setMessage(`Package uploaded`); + setRedirect(`/carupdate-deploy/${result.id}`); } catch (e) { setMessage(e.message); } }; + if (redirect && redirect.length > 0) { + return ; + } + return (
- Upload Update Package + Create Update Package { inputProps={{ maxLength: "5120", }} + required fullWidth multiline rows={4} @@ -122,6 +134,7 @@ const MainForm = () => { inputProps={{ maxLength: "1024", }} + required fullWidth placeholder="Release Notes URL" inputRef={releasenotesEl} diff --git a/src/components/UpdatePackages/Edit/index.jsx b/src/components/UpdatePackages/Edit/index.jsx index d0954d1..6c5f5af 100644 --- a/src/components/UpdatePackages/Edit/index.jsx +++ b/src/components/UpdatePackages/Edit/index.jsx @@ -87,7 +87,7 @@ const MainForm = () => { return (
- Update Package {id} + Edit Update Package {id} { const classes = useStyles(); - const [pageSize, setPageSize] = useState(5); + const [pageSize, setPageSize] = useState(25); const [pageIndex, setPageIndex] = useState(0); const { getPackages, packages, totalPackages } = useUpdatesContext(); const { @@ -98,7 +98,7 @@ const UpdatePackagesList = () => { return (
- Updates + Update Packages diff --git a/src/services/__mocks__/updates.js b/src/services/__mocks__/updates.js index 74704dd..7da9b71 100644 --- a/src/services/__mocks__/updates.js +++ b/src/services/__mocks__/updates.js @@ -41,11 +41,15 @@ const updatesAPI = { }, getCarUpdates: async (filter, token) => { - return { data:[] }; + return { data: [] }; }, getVINUpdates: async (vin, token) => { - return { data:[] }; + return { data: [] }; + }, + + getCarUpdateProgress: async (carupdateids, token) => { + return { statuses: [] }; }, }; diff --git a/src/services/updates.js b/src/services/updates.js index c5788e2..8e286c4 100644 --- a/src/services/updates.js +++ b/src/services/updates.js @@ -45,6 +45,15 @@ const updatesAPI = { }) .then(fetchRespHandler); }, + + getCarUpdateProgress: async (carupdateids, token) => { + var u = `${API_ENDPOINT}/carupdatesstatuses?carupdateids=${carupdateids}`; + return fetch(u, { + method: "GET", + headers: Object.assign({ "Content-Type": "application/json" }, getAuthHeaderOptions(token)), + }) + .then(fetchRespHandler); + }, }; export default updatesAPI;