CEC-3672 Update manifest version on deploy (#277)
* CEC-3672 Add versions to CarUpdatesContext Stub out getSoftwareVersions and updateManifestVersion * CEC-3672 update version on deploy * Validate version before updating
This commit is contained in:
@@ -3229,6 +3229,22 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
||||
<main
|
||||
class="MuiContainer-root MuiContainer-maxWidthLg"
|
||||
>
|
||||
<div
|
||||
class="MuiSnackbar-root MuiSnackbar-anchorOriginTopCenter"
|
||||
>
|
||||
<div
|
||||
class="MuiPaper-root MuiSnackbarContent-root MuiPaper-elevation6"
|
||||
direction="down"
|
||||
role="alert"
|
||||
style="opacity: 1; transform: scale(1, 1); transition: opacity 225ms cubic-bezier(0.4, 0, 0.2, 1) 0ms,transform 150ms cubic-bezier(0.4, 0, 0.2, 1) 0ms;"
|
||||
>
|
||||
<div
|
||||
class="MuiSnackbarContent-message"
|
||||
>
|
||||
getSoftwareVersions is not a function
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
data-testid="mocked-manifestsprovider"
|
||||
>
|
||||
@@ -3359,10 +3375,52 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiGrid-root makeStyles-textRightAlign-0 MuiGrid-item MuiGrid-grid-md-4"
|
||||
class="MuiGrid-root MuiGrid-container MuiGrid-item MuiGrid-justify-content-xs-flex-end MuiGrid-grid-md-4"
|
||||
>
|
||||
<div
|
||||
class="MuiFormControl-root MuiFormControl-marginNormal"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||
data-shrink="false"
|
||||
>
|
||||
Software Version
|
||||
</label>
|
||||
<div
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
||||
>
|
||||
<select
|
||||
aria-invalid="false"
|
||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||
/>
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root MuiSelect-icon MuiSelect-iconOutlined"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M7 10l5 5 5-5z"
|
||||
/>
|
||||
</svg>
|
||||
<fieldset
|
||||
aria-hidden="true"
|
||||
class="PrivateNotchedOutline-root-0 MuiOutlinedInput-notchedOutline"
|
||||
style="padding-left: 8px;"
|
||||
>
|
||||
<legend
|
||||
class="PrivateNotchedOutline-legend-0"
|
||||
style="width: 0.01px;"
|
||||
>
|
||||
<span>
|
||||
|
||||
</span>
|
||||
</legend>
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
class="MuiButtonBase-root MuiButton-root MuiButton-contained makeStyles-formControl-0 makeStyles-textField-0 MuiButton-containedPrimary Mui-disabled Mui-disabled"
|
||||
class="MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary Mui-disabled Mui-disabled"
|
||||
disabled=""
|
||||
tabindex="-1"
|
||||
type="submit"
|
||||
@@ -3370,7 +3428,16 @@ exports[`App Route /package-deploy authenticated 1`] = `
|
||||
<span
|
||||
class="MuiButton-label"
|
||||
>
|
||||
Deploy
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
class="MuiSvgIcon-root"
|
||||
focusable="false"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
d="M2.01 21L23 12 2.01 3 2 10l15 2-15 2z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import React, { useContext, useState } from "react";
|
||||
|
||||
import api from "../../services/updatesAPI";
|
||||
import { validateSoftwareVersion } from "../../utils/softwareVersions";
|
||||
import { validateStatusMessage } from "../../utils/statusMessage";
|
||||
|
||||
export const SELECT_VERSION = "Select Version";
|
||||
const FINAL_UPDATE_STATES = ["package_install_complete"];
|
||||
const CarUpdatesContext = React.createContext();
|
||||
const SELECT_VERSION_OBJ = { version: SELECT_VERSION }
|
||||
|
||||
const validateDeployClosure = (data, propertyName, errPfx) => {
|
||||
if (data === null) {
|
||||
@@ -32,6 +35,7 @@ const validateDeployFleetUpdates = (data) => {
|
||||
export const CarUpdatesProvider = ({ children }) => {
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [carUpdates, setCarUpdates] = useState([]);
|
||||
const [versions, setVersions] = useState([SELECT_VERSION_OBJ]);
|
||||
const [totalCarUpdates, setTotalCarUpdates] = useState(0);
|
||||
const [delayCount, setDelayCount] = useState(0);
|
||||
let progressTimer = 0;
|
||||
@@ -238,20 +242,60 @@ export const CarUpdatesProvider = ({ children }) => {
|
||||
return result;
|
||||
};
|
||||
|
||||
const getSoftwareVersions = async (token) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
setBusy(true);
|
||||
|
||||
result = await api.getSoftwareVersions(token);
|
||||
if (result.error)
|
||||
throw new Error(`Get software versions error. ${result.message}`);
|
||||
|
||||
result.data.unshift(SELECT_VERSION_OBJ)
|
||||
setVersions(result.data);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
const updateManifestVersion = async (id, version, token) => {
|
||||
let result;
|
||||
|
||||
try {
|
||||
setBusy(true);
|
||||
|
||||
if (!validateSoftwareVersion(version)) throw new Error(`invalid version ${version}`);
|
||||
|
||||
result = await api.updateManifestVersion(id, version, token);
|
||||
if (result.error)
|
||||
throw new Error(`Update manifest version error. ${result.message}`);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
return (
|
||||
<CarUpdatesContext.Provider
|
||||
value={{
|
||||
busy,
|
||||
carUpdates,
|
||||
totalCarUpdates,
|
||||
versions,
|
||||
cancelUpdate,
|
||||
deployCarUpdates,
|
||||
deployFleetUpdates,
|
||||
getCarUpdates,
|
||||
getLog,
|
||||
getSoftwareVersions,
|
||||
getVINUpdates,
|
||||
startMonitor,
|
||||
stopMonitor,
|
||||
updateManifestVersion,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
|
||||
25
src/components/Controls/DropDownList/index.jsx
Normal file
25
src/components/Controls/DropDownList/index.jsx
Normal file
@@ -0,0 +1,25 @@
|
||||
import { FormControl, InputLabel, Select } from "@material-ui/core";
|
||||
|
||||
export const DropDownList = ({data, label, value, labelField, valueField, onChange, classes, ...others}) => {
|
||||
return (
|
||||
<FormControl
|
||||
variant="outlined"
|
||||
margin="normal"
|
||||
>
|
||||
<InputLabel className={classes.whiteBackground}>
|
||||
{label}
|
||||
</InputLabel>
|
||||
<Select
|
||||
native
|
||||
value={value}
|
||||
variant="outlined"
|
||||
onChange={onChange}
|
||||
{...others}
|
||||
>
|
||||
{data && data.map((item, index) => (
|
||||
<option key={index} value={item[valueField || "value"]}>{item[labelField || "label"]}</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
}
|
||||
@@ -1,18 +1,19 @@
|
||||
import { Button, FormControlLabel, Grid, Switch, Typography } from "@material-ui/core";
|
||||
import clsx from "clsx";
|
||||
import SendIcon from "@material-ui/icons/Send";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { Redirect, useParams } from "react-router";
|
||||
|
||||
import { logger } from "../../../services/monitoring";
|
||||
import { LocalDateTimeString } from "../../../utils/dates";
|
||||
import { Permissions } from "../../../utils/roles";
|
||||
import { CarUpdatesProvider, useCarUpdatesContext } from "../../Contexts/CarUpdatesContext";
|
||||
import { CarUpdatesProvider, SELECT_VERSION, useCarUpdatesContext } from "../../Contexts/CarUpdatesContext";
|
||||
import { FleetProvider } from "../../Contexts/FleetContext";
|
||||
import { ManifestsProvider, useManifestsContext } from "../../Contexts/ManifestsContext";
|
||||
import { useStatusContext } from "../../Contexts/StatusContext";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
import { VehicleProvider } from "../../Contexts/VehicleContext";
|
||||
import CarSelectionTable from "../../Controls/CarSelectionTable";
|
||||
import { DropDownList } from "../../Controls/DropDownList";
|
||||
import FleetSelectionTable from "../../Controls/FleetSelectionTable";
|
||||
import { RoleWrap } from "../../Controls/RoleWrap";
|
||||
import SearchField from "../../Controls/SearchField";
|
||||
@@ -25,7 +26,7 @@ const MainForm = () => {
|
||||
const [updateType, setUpdateType] = useState(CAR_UPDATE);
|
||||
const {manifest_id} = useParams();
|
||||
const {getManifests, manifests, busy} = useManifestsContext();
|
||||
const {deployCarUpdates, deployFleetUpdates} = useCarUpdatesContext();
|
||||
const {deployCarUpdates, deployFleetUpdates, getSoftwareVersions, versions, updateManifestVersion} = useCarUpdatesContext();
|
||||
const {
|
||||
groups,
|
||||
providers,
|
||||
@@ -39,6 +40,7 @@ const MainForm = () => {
|
||||
const [createDate, setCreateDate] = useState("");
|
||||
const [selected, setSelected] = useState([]);
|
||||
const [search, setSearch] = useState("");
|
||||
const [softwareVersion, setSoftwareVersion] = useState(SELECT_VERSION);
|
||||
const [redirect, setRedirect] = useState("");
|
||||
const classes = useStyles();
|
||||
|
||||
@@ -77,6 +79,8 @@ const MainForm = () => {
|
||||
const data = {
|
||||
manifest_id: parseInt(manifest_id),
|
||||
}
|
||||
await updateManifestVersion(manifest_id, softwareVersion, token);
|
||||
|
||||
if (updateType === CAR_UPDATE) {
|
||||
data.vins = selected;
|
||||
await deployCarUpdates(data, token);
|
||||
@@ -96,13 +100,18 @@ const MainForm = () => {
|
||||
|
||||
const getData = async () => {
|
||||
try {
|
||||
getManifests({id: parseInt(manifest_id)}, token);
|
||||
await getManifests({id: parseInt(manifest_id)}, token);
|
||||
await getSoftwareVersions(token);
|
||||
} catch (e) {
|
||||
setMessage(e.message);
|
||||
logger.warn(e.stack);
|
||||
}
|
||||
};
|
||||
|
||||
const changeVersion = (e) => {
|
||||
setSoftwareVersion(e.target.value);
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
getData();
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
@@ -162,16 +171,22 @@ const MainForm = () => {
|
||||
<Grid item md={4} className={classes.textCenterAlign}>
|
||||
<SearchField classes={classes} onSearch={handleSearch}/>
|
||||
</Grid>
|
||||
<Grid item md={4} className={classes.textRightAlign}>
|
||||
<Grid item md={4} container justifyContent="flex-end">
|
||||
<DropDownList
|
||||
label="Software Version"
|
||||
labelField="version"
|
||||
valueField="version"
|
||||
value={softwareVersion}
|
||||
data={versions}
|
||||
classes={classes}
|
||||
onChange={changeVersion} />
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={busy || selected.length === 0}
|
||||
variant="contained"
|
||||
disabled={busy || selected.length === 0 || softwareVersion === SELECT_VERSION}
|
||||
color="primary"
|
||||
className={clsx(classes.formControl, classes.textField)}
|
||||
onClick={onSubmit}
|
||||
>
|
||||
{busy ? "Deploying..." : "Deploy"}
|
||||
<SendIcon />
|
||||
</Button>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -113,12 +113,11 @@ exports[`Manifest Details Component Render 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiFormControl-root makeStyles-form-0 MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||
class="MuiFormControl-root MuiFormControl-marginNormal"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-outlined"
|
||||
data-shrink="false"
|
||||
for="manifest-type"
|
||||
>
|
||||
Type
|
||||
</label>
|
||||
@@ -128,8 +127,6 @@ exports[`Manifest Details Component Render 1`] = `
|
||||
<select
|
||||
aria-invalid="false"
|
||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||
id="send-manifest-type"
|
||||
name="manifest-type"
|
||||
>
|
||||
<option
|
||||
value="standard"
|
||||
@@ -169,14 +166,13 @@ exports[`Manifest Details Component Render 1`] = `
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="MuiFormControl-root makeStyles-form-0 MuiFormControl-marginNormal MuiFormControl-fullWidth"
|
||||
class="MuiFormControl-root MuiFormControl-marginNormal"
|
||||
>
|
||||
<label
|
||||
class="MuiFormLabel-root MuiInputLabel-root makeStyles-whiteBackground-0 MuiInputLabel-formControl MuiInputLabel-animated MuiInputLabel-shrink MuiInputLabel-outlined MuiFormLabel-filled"
|
||||
data-shrink="true"
|
||||
for="manifest-active"
|
||||
>
|
||||
Type
|
||||
Active
|
||||
</label>
|
||||
<div
|
||||
class="MuiInputBase-root MuiOutlinedInput-root MuiInputBase-formControl"
|
||||
@@ -184,18 +180,16 @@ exports[`Manifest Details Component Render 1`] = `
|
||||
<select
|
||||
aria-invalid="false"
|
||||
class="MuiSelect-root MuiSelect-select MuiSelect-outlined MuiInputBase-input MuiOutlinedInput-input"
|
||||
id="send-manifest-active"
|
||||
name="manifest-active"
|
||||
>
|
||||
<option
|
||||
value="true"
|
||||
>
|
||||
active
|
||||
Active
|
||||
</option>
|
||||
<option
|
||||
value="false"
|
||||
>
|
||||
archived
|
||||
Archived
|
||||
</option>
|
||||
</select>
|
||||
<svg
|
||||
|
||||
@@ -1,17 +1,23 @@
|
||||
import { Button, FormControl, TextField } from "@material-ui/core";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { Redirect } from "react-router";
|
||||
import useStyles from "../../useStyles";
|
||||
import { useParams } from "react-router-dom";
|
||||
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";
|
||||
import { useUserContext } from "../../Contexts/UserContext";
|
||||
import { DropDownList } from "../../Controls/DropDownList";
|
||||
import useStyles from "../../useStyles";
|
||||
|
||||
const manifestTypes = [
|
||||
{ value: "standard", label: "Standard" },
|
||||
{ value: "forced", label: "Forced" },
|
||||
];
|
||||
|
||||
const activeStates = [
|
||||
{value: true, label: "Active" },
|
||||
{value: false, label: "Archived" },
|
||||
];
|
||||
|
||||
const emptyManifest = {
|
||||
name: "",
|
||||
version: "",
|
||||
@@ -138,53 +144,8 @@ const MainForm = () => {
|
||||
fullWidth
|
||||
onChange={changeName}
|
||||
/>
|
||||
<FormControl
|
||||
className={classes.form}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
margin="normal"
|
||||
>
|
||||
<InputLabel htmlFor="manifest-type" className={classes.whiteBackground}>
|
||||
Type
|
||||
</InputLabel>
|
||||
<Select
|
||||
native
|
||||
value={type}
|
||||
variant="outlined"
|
||||
inputProps={{
|
||||
name: "manifest-type",
|
||||
id: "send-manifest-type",
|
||||
}}
|
||||
onChange={changeType}
|
||||
>
|
||||
{manifestTypes.map((item, index) => (
|
||||
<option key={index} value={item.value}>{item.label}</option>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
<FormControl
|
||||
className={classes.form}
|
||||
variant="outlined"
|
||||
fullWidth
|
||||
margin="normal"
|
||||
>
|
||||
<InputLabel htmlFor="manifest-active" className={classes.whiteBackground}>
|
||||
Type
|
||||
</InputLabel>
|
||||
<Select
|
||||
native
|
||||
value={active}
|
||||
variant="outlined"
|
||||
inputProps={{
|
||||
name: "manifest-active",
|
||||
id: "send-manifest-active",
|
||||
}}
|
||||
onChange={changeActive}
|
||||
>
|
||||
<option key={0} value={true}>active</option>
|
||||
<option key={1} value={false}>archived</option>
|
||||
</Select>
|
||||
</FormControl>
|
||||
<DropDownList label="Type" data={manifestTypes} classes={classes} onChange={changeType} value={type} />
|
||||
<DropDownList label="Active" data={activeStates} classes={classes} onChange={changeActive} value={active}/>
|
||||
<Button
|
||||
type="submit"
|
||||
aria-label="send command"
|
||||
|
||||
Reference in New Issue
Block a user