CEC-2920 Aftersales certificates (#225)

* CEC-2920 aftersales certificates

* smells

* smells
This commit is contained in:
John Wu
2022-10-25 11:00:50 -07:00
committed by GitHub
parent 58890ea40e
commit aaf47f4cc7
7 changed files with 231 additions and 31 deletions

View File

@@ -4532,6 +4532,57 @@ exports[`App Route /tools/certificates/add authenticated 1`] = `
ICC ICC
</span> </span>
</label> </label>
<label
class="MuiFormControlLabel-root"
>
<span
aria-disabled="false"
class="MuiButtonBase-root MuiIconButton-root PrivateSwitchBase-root-0 MuiRadio-root MuiRadio-colorSecondary MuiIconButton-colorSecondary"
>
<span
class="MuiIconButton-label"
>
<input
class="PrivateSwitchBase-input-0"
name="cert-type"
type="radio"
value="Aftersales"
/>
<div
class="PrivateRadioButtonIcon-root-0"
>
<svg
aria-hidden="true"
class="MuiSvgIcon-root"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.42 0-8-3.58-8-8s3.58-8 8-8 8 3.58 8 8-3.58 8-8 8z"
/>
</svg>
<svg
aria-hidden="true"
class="MuiSvgIcon-root PrivateRadioButtonIcon-layer-0"
focusable="false"
viewBox="0 0 24 24"
>
<path
d="M8.465 8.465C9.37 7.56 10.62 7 12 7C14.76 7 17 9.24 17 12C17 13.38 16.44 14.63 15.535 15.535C14.63 16.44 13.38 17 12 17C9.24 17 7 14.76 7 12C7 10.62 7.56 9.37 8.465 8.465Z"
/>
</svg>
</div>
</span>
<span
class="MuiTouchRipple-root"
/>
</span>
<span
class="MuiTypography-root MuiFormControlLabel-label MuiTypography-body1"
>
Aftersales
</span>
</label>
<label <label
class="MuiFormControlLabel-root" class="MuiFormControlLabel-root"
> >

View File

@@ -1,4 +1,3 @@
import React, { useRef, useState } from "react";
import { import {
Button, Button,
FormControlLabel, FormControlLabel,
@@ -7,13 +6,15 @@ import {
RadioGroup, RadioGroup,
TextField, TextField,
} from "@material-ui/core"; } from "@material-ui/core";
import React, { useRef, useState } from "react";
import { CertTypeData, CertTypes } from "../../../utils/certificates";
import useStyles from "../../useStyles"; import useStyles from "../../useStyles";
import { CertTypes } from "../../Contexts/CertificateContext";
const getCertTypeLabel = (certtype) => { const getCertTypeLabel = (certtype) => {
if (certtype === CertTypes.Aftersales) return "Service Tool ID"; const item = CertTypeData.find((item) => certtype === item.value);
return "VIN"; if (item !== null) return item.inputlabel;
return "ID";
}; };
const CreateForm = ({ onCreate, busy }) => { const CreateForm = ({ onCreate, busy }) => {
@@ -60,21 +61,16 @@ const CreateForm = ({ onCreate, busy }) => {
onChange={onCertTypeChange} onChange={onCertTypeChange}
margin="normal" margin="normal"
> >
<FormControlLabel {CertTypeData.map((item, i) => {
value={CertTypes.TBOX} return (
control={<Radio />} <FormControlLabel
label="TBOX" key={i}
/> value={item.value}
<FormControlLabel label={item.label}
value={CertTypes.ICC} control={<Radio />}
control={<Radio />} />
label="ICC" );
/> })}
<FormControlLabel
value={CertTypes.Charging}
control={<Radio />}
label="Charging"
/>
</RadioGroup> </RadioGroup>
<Button <Button
type="submit" type="submit"

View File

@@ -1,12 +1,12 @@
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { logger } from "../../../services/monitoring";
import { import {
useCertificateContext,
CertificateProvider, CertificateProvider,
useCertificateContext,
} from "../../Contexts/CertificateContext"; } from "../../Contexts/CertificateContext";
import { useStatusContext } from "../../Contexts/StatusContext"; import { useStatusContext } from "../../Contexts/StatusContext";
import { useUserContext } from "../../Contexts/UserContext"; import { useUserContext } from "../../Contexts/UserContext";
import { logger } from "../../../services/monitoring";
import CreateForm from "./CreateForm"; import CreateForm from "./CreateForm";
import DownloadCerts from "./DownloadCerts"; import DownloadCerts from "./DownloadCerts";
@@ -43,11 +43,12 @@ const MainForm = () => {
const onCreate = async (data) => { const onCreate = async (data) => {
try { try {
const result = await createCert(data, token); const result = await createCert(data, token);
const name = data.common_name || data.tool_id;
setCommonName(name);
setPubCert(result.public_key); setPubCert(result.public_key);
setPrivCert(result.private_key); setPrivCert(result.private_key);
setCommonName(data.common_name); setMessage(`Created ${name} certificate`);
setMessage(`Created ${data.common_name} certificate`);
setView(VIEW_DOWNLOAD); setView(VIEW_DOWNLOAD);
} catch (e) { } catch (e) {
setMessage(e.message); setMessage(e.message);

View File

@@ -1,21 +1,25 @@
import React, { useContext, useState } from "react"; import React, { useContext, useState } from "react";
import api from "../../services/certificatesAPI"; import api from "../../services/certificatesAPI";
import { CertTypes } from "../../utils/certificates";
const CertificateContext = React.createContext(); const CertificateContext = React.createContext();
export const CertTypes = {
TBOX: "TBOX",
ICC: "ICC",
Charging: "Charging",
Aftersales: "Aftersales",
};
const validateCreate = (data) => { const validateCreate = (data) => {
if (!data.type) throw new Error("type is required"); if (!data.type) throw new Error("type is required");
if (!data.common_name) throw new Error("common name is required"); if (!data.common_name) throw new Error("common name is required");
}; };
const callCreateCert = (data, token) => {
if (data.type === CertTypes.Aftersales) {
data.tool_id = data.common_name;
delete data.common_name;
return api.createAftersales(data, token);
}
return api.create(data, token);
};
export const CertificateProvider = ({ children }) => { export const CertificateProvider = ({ children }) => {
const [busy, setBusy] = useState(false); const [busy, setBusy] = useState(false);
@@ -25,7 +29,8 @@ export const CertificateProvider = ({ children }) => {
validateCreate(data); validateCreate(data);
const result = await api.create(data, token); const result = await callCreateCert(data, token);
if (result.error) { if (result.error) {
throw new Error(`Create certificate error. ${result.message}`); throw new Error(`Create certificate error. ${result.message}`);
} }

View File

@@ -0,0 +1,105 @@
jest.mock("../../services/certificatesAPI");
import {
act,
cleanup,
fireEvent,
render,
screen,
waitFor,
} from "@testing-library/react";
import { useState } from "react";
import {
CertificateProvider,
useCertificateContext,
} from "./CertificateContext";
describe("CertificateContext", () => {
beforeEach(() => {
const TestComp = () => {
const { createCert } = useCertificateContext();
const [result, setResult] = useState(null);
const testCreate = async (data) => {
const x = await createCert(data);
setResult(JSON.stringify(x));
};
return (
<>
<div data-testid="output">{result}</div>
<button
data-testid="createCertTBOX"
onClick={async () => {
await testCreate({
common_name: "11111111111111111",
type: "TBOX",
});
}}
/>
<button
data-testid="createCertICC"
onClick={async () => {
await testCreate({
common_name: "11111111111111111",
type: "ICC",
});
}}
/>
<button
data-testid="createCertAftersales"
onClick={async () => {
await testCreate({
common_name: "11111111111111112",
type: "Aftersales",
});
}}
/>
</>
);
};
render(
<CertificateProvider>
<TestComp />
</CertificateProvider>
);
});
afterEach(() => {
cleanup();
});
it("TBOX certificate", async () => {
await act(async () => {
fireEvent.click(screen.getByTestId("createCertTBOX"));
await waitFor(() =>
expect(screen.getByTestId("output").innerHTML).toBe(
'{"private_key":"-----BEGIN RSA PRIVATE KEY-----DATA-----END RSA PRIVATE KEY-----","public_key":"-----BEGIN CERTIFICATE-----DATA-----END CERTIFICATE-----","serial_number":"00:76:d2:ca:86:35:8c:88:52:fd:94:8e:55:d3:b6:a8:2e:73:68:00","type":"TBOX"}'
)
);
});
});
it("ICC certificate", async () => {
await act(async () => {
fireEvent.click(screen.getByTestId("createCertICC"));
await waitFor(() =>
expect(screen.getByTestId("output").innerHTML).toBe(
'{"private_key":"-----BEGIN RSA PRIVATE KEY-----DATA-----END RSA PRIVATE KEY-----","public_key":"-----BEGIN CERTIFICATE-----DATA-----END CERTIFICATE-----","serial_number":"00:76:d2:ca:86:35:8c:88:52:fd:94:8e:55:d3:b6:a8:2e:73:68:00","type":"ICC"}'
)
);
});
});
it("Aftersales certificate", async () => {
await act(async () => {
fireEvent.click(screen.getByTestId("createCertAftersales"));
await waitFor(() =>
expect(screen.getByTestId("output").innerHTML).toBe(
'{"private_key":"-----BEGIN RSA PRIVATE KEY-----DATA-----END RSA PRIVATE KEY-----","public_key":"-----BEGIN CERTIFICATE-----DATA-----END CERTIFICATE-----","serial_number":"00:76:d2:ca:86:35:8c:88:52:fd:94:8e:55:d3:b6:a8:2e:73:68:00","type":"Aftersales"}'
)
);
});
});
});

View File

@@ -0,0 +1,13 @@
const success = {
private_key:
"-----BEGIN RSA PRIVATE KEY-----DATA-----END RSA PRIVATE KEY-----",
public_key: "-----BEGIN CERTIFICATE-----DATA-----END CERTIFICATE-----",
serial_number: "00:76:d2:ca:86:35:8c:88:52:fd:94:8e:55:d3:b6:a8:2e:73:68:00",
};
const certificatesAPI = {
create: (data) => Object.assign(success, { type: data.type }),
createAftersales: (data) => Object.assign(success, { type: data.type }),
};
export default certificatesAPI;

29
src/utils/certificates.js Normal file
View File

@@ -0,0 +1,29 @@
export const CertTypes = {
TBOX: "TBOX",
ICC: "ICC",
Charging: "Charging",
Aftersales: "Aftersales",
};
export const CertTypeData = [
{
value: CertTypes.TBOX,
label: "TBOX",
inputlabel: "VIN",
},
{
value: CertTypes.ICC,
label: "ICC",
inputlabel: "VIN",
},
{
value: CertTypes.Aftersales,
label: "Aftersales",
inputlabel: "Service Tool ID",
},
{
value: CertTypes.Charging,
label: "Charging",
inputlabel: "VIN",
},
];