From 9a9766df129fae10024c3b77d80d8513e7b84d8f Mon Sep 17 00:00:00 2001 From: arpanetus Date: Wed, 26 Oct 2022 03:54:20 +0600 Subject: [PATCH] CEC-2579 Add ability to edit manifest (#226) --- .../App/__snapshots__/App.test.js.snap | 46 +++++ src/components/Contexts/ManifestsContext.jsx | 28 +++ .../Contexts/ManifestsContext.test.jsx | 109 ++++++++++ .../Contexts/__mocks__/ManifestsContext.jsx | 1 + src/components/Manifest/List/index.jsx | 21 ++ .../Update/__snapshots__/index.test.jsx.snap | 192 ++++++++++++++++++ src/components/Manifest/Update/index.jsx | 185 +++++++++++++++++ src/components/Manifest/Update/index.test.jsx | 45 ++++ src/components/Routes/SiteRoutes.jsx | 9 + src/services/__mocks__/manifestsAPI.js | 7 + src/services/manifestsAPI.js | 13 ++ 11 files changed, 656 insertions(+) create mode 100644 src/components/Contexts/ManifestsContext.test.jsx create mode 100644 src/components/Manifest/Update/__snapshots__/index.test.jsx.snap create mode 100644 src/components/Manifest/Update/index.jsx create mode 100644 src/components/Manifest/Update/index.test.jsx diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index 9363bc1..666d7a0 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -3187,6 +3187,29 @@ exports[`App Route /packages authenticated 1`] = ` + + + Type + + + 1.0 + + Standard + @@ -3293,6 +3321,24 @@ exports[`App Route /packages authenticated 1`] = ` /> + + + { return result; }; + const updateManifest = async (id, data, token) => { + validateManifestUpdate(data); + + let result; + + try { + setBusy(true); + result = await api.updateManifest(id, data, token); + if (result.error) + throw new Error(`Update manifest error. ${result.message}`); + } finally { + setBusy(false); + } + + return result; + } + const deleteManifest = async (package_id, token) => { let result; @@ -69,6 +86,7 @@ export const ManifestsProvider = ({ children }) => { busy, manifests, totalManifests, + updateManifest, getManifest, getManifests, deleteManifest, @@ -79,4 +97,14 @@ export const ManifestsProvider = ({ children }) => { ); }; +const validateManifestUpdate = (data) => { + if (data == null) throw new Error("No manifest data"); + + if (data.name == null || data.name.length>255) throw new Error("Invalid manifest name"); + + if (data.type == null || !["forced", "standard"].includes(data.type)) { + throw new Error("Invalid manifest type"); + } +} + export const useManifestsContext = () => useContext(ManifestsContext); diff --git a/src/components/Contexts/ManifestsContext.test.jsx b/src/components/Contexts/ManifestsContext.test.jsx new file mode 100644 index 0000000..531c858 --- /dev/null +++ b/src/components/Contexts/ManifestsContext.test.jsx @@ -0,0 +1,109 @@ +jest.mock("../../services/manifestsAPI"); + +import { + render, + cleanup, + screen, + fireEvent, + waitFor, +} from "@testing-library/react"; +import { ManifestsProvider, useManifestsContext } from "./ManifestsContext"; +import { StatusProvider, useStatusContext } from "./StatusContext"; + +const checkBaseResults = (error, busy) => { + expect(screen.getByTestId("error").innerHTML).toEqual(error); + expect(screen.getByTestId("busy").innerHTML).toEqual(busy); +}; + +describe("ManifestContext", () => { + describe("updateManifest", () => { + beforeEach(async () => { + const TestComp = () => { + const { busy, updateManifest } = useManifestsContext(); + const { message, setMessage } = useStatusContext(); + const update = async (data) => { + try { + await updateManifest(1, data); + } catch (e) { + setMessage(e.message); + } + }; + + return ( + <> +
{message}
+
{busy.toString()}
+ + + + + + + +`; diff --git a/src/components/Manifest/Update/index.jsx b/src/components/Manifest/Update/index.jsx new file mode 100644 index 0000000..e4897f5 --- /dev/null +++ b/src/components/Manifest/Update/index.jsx @@ -0,0 +1,185 @@ +import React, {useEffect, useState} from "react"; +import {useParams} from "react-router-dom"; +import { Redirect } from "react-router"; +import useStyles from "../../useStyles"; +import {ManifestsProvider, useManifestsContext} from "../../Contexts/ManifestsContext"; +import {useUserContext} from "../../Contexts/UserContext"; +import {useStatusContext} from "../../Contexts/StatusContext"; +import {Button, FormControl, InputLabel, Select, TextField} from "@material-ui/core"; + +const manifestTypes = [ + {value: "standard", label: "Standard"}, + {value: "forced", label: "Forced"}, +]; + +const emptyManifest = { + name: "", + version: "", +}; + +const MainForm = () => { + + const {manifest_id} = useParams(); + const classes = useStyles(); + + const [manifest, setManifest] = useState(null); + const [redirect, setRedirect] = useState(null); + + const {getManifest, busy, updateManifest} = useManifestsContext(); + const { + token: { + idToken: {jwtToken: token}, + }, + } = useUserContext(); + + const {setMessage, setTitle, setSitePath} = useStatusContext(); + + const [name, setName] = useState(""); + const [type, setType] = useState(""); + + + const changeName = (e) => { + setName(e.target.value); + } + + const changeType = (e) => { + setType(e.target.value); + }; + + const onSubmit = async (e) => { + e.preventDefault(); + try { + const data = {name, type}; + console.log(data); + const result = await updateManifest(manifest_id, data, token); + if (!result || result.error) return; + + setMessage(`Updated manifest ${manifest_id}`); + setRedirect(`/package-status/${manifest_id}`); + + } catch (e) { + setMessage(`Failed to update manifest ${manifest_id}`); + } + } + + useEffect(() => { + (async () => { + try { + const result = await getManifest(manifest_id, token); + if (result.error) { + throw new Error(`Get manifest error. ${result.message}`); + } else { + setManifest(result); + setName(result.name); + setType(result.type); + } + } catch (e) { + setMessage(e.message); + } + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [token]); + + useEffect(() => { + const curManifest = manifest ? manifest : emptyManifest; + + setTitle("Update Package"); + setSitePath([ + { + label: "Deployments", + link: "/packages", + }, + { + label: `Update Package ${curManifest.name} ${curManifest.version}`, + }, + ]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [manifest]); + + if (redirect && redirect.length > 0) { + return ; + } + + if (manifest === null) return
Loading Manifest...
; + + return ( +
+ + + + + + Type + + + + + + +
+ ); +} + +const ManifestUpdate = () => ( + + + +); + +export default ManifestUpdate; \ No newline at end of file diff --git a/src/components/Manifest/Update/index.test.jsx b/src/components/Manifest/Update/index.test.jsx new file mode 100644 index 0000000..d851193 --- /dev/null +++ b/src/components/Manifest/Update/index.test.jsx @@ -0,0 +1,45 @@ +jest.mock("../../Contexts/ManifestsContext"); +jest.mock("../../Contexts/UserContext"); + +import React from "react"; +import { render, waitFor } from "@testing-library/react"; +import { BrowserRouter } from "react-router-dom"; + +import { ManifestsProvider } from "../../Contexts/ManifestsContext"; +import { UserProvider, setToken } from "../../Contexts/UserContext"; +import { StatusProvider } from "../../Contexts/StatusContext"; +import ManifestUpdate from "."; +import { TEST_AUTH_OBJECT } from "../../../utils/testing"; +import addSnapshotSerializer from "../../../utils/snapshot"; + +const renderManifestUpdate = async () => { + const { container } = render( + + + + + + + + + ); + await waitFor(() => { + /* render */ + }); + + return container; +} + + +describe("Manifest Details Component", () => { + beforeAll(() => { + setToken(TEST_AUTH_OBJECT); + addSnapshotSerializer(expect); + }); + + it("Render", async () => { + setToken(TEST_AUTH_OBJECT); + const view = await renderManifestUpdate(); + expect(view).toMatchSnapshot(); + }); +}); diff --git a/src/components/Routes/SiteRoutes.jsx b/src/components/Routes/SiteRoutes.jsx index 626b014..1bb32fb 100644 --- a/src/components/Routes/SiteRoutes.jsx +++ b/src/components/Routes/SiteRoutes.jsx @@ -28,6 +28,7 @@ const Home = React.lazy(() => import("../Home")); const Manifests = React.lazy(() => import("../Manifest/List")); const ManifestDeploy = React.lazy(() => import("../Manifest/Deploy")); const ManifestStatus = React.lazy(() => import("../Manifest/Status")); +const ManifestUpdate = React.lazy(() => import("../Manifest/Update")); const PageNotFound = React.lazy(() => import("../404")); const SSOForm = React.lazy(() => import("../SSOForm")); const VehicleAddForm = React.lazy(() => import("../Cars/Add")); @@ -152,6 +153,14 @@ const SiteRoutes = () => { groups={groups} roles={[Roles.READ, Roles.CREATE]} /> + } + type={TYPES.PROTECTED} + token={token} + groups={groups} + roles={[Roles.READ, Roles.CREATE]} + /> } diff --git a/src/services/__mocks__/manifestsAPI.js b/src/services/__mocks__/manifestsAPI.js index 16adcdb..6404bc5 100644 --- a/src/services/__mocks__/manifestsAPI.js +++ b/src/services/__mocks__/manifestsAPI.js @@ -57,6 +57,13 @@ const manifestsAPI = { return { statuses: [] }; }, + updateManifest: async (id, data, token) => { + return { + id, + ...data, + } + }, + getManifest: async (id, token) => { return { id, diff --git a/src/services/manifestsAPI.js b/src/services/manifestsAPI.js index 6ad7205..33a6436 100644 --- a/src/services/manifestsAPI.js +++ b/src/services/manifestsAPI.js @@ -32,6 +32,19 @@ const manifestsAPI = { .catch(errorHandler); }, + updateManifest: async (manifest_id, data, token) => { + return fetch(`${API_ENDPOINT}/manifest?id=${manifest_id}`, { + method: "PUT", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ), + body: JSON.stringify(data), + }) + .then(fetchRespHandler) + .catch(errorHandler); + }, + getManifests: async (search, token) => { const u = addQueryParams(`${API_ENDPOINT}/manifests`, search); return fetch(u, {