diff --git a/package-lock.json b/package-lock.json index 176b24a..5dd6166 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,8 @@ "date-fns": "^2.29.2", "email-validator": "^2.0.4", "env-cmd": "^10.1.0", + "file-saver": "^2.0.5", + "filesaver": "^0.0.13", "jwt-decode": "^3.1.2", "leaflet": "^1.8.0", "material-ui-dropzone": "^3.5.0", @@ -9424,6 +9426,11 @@ "webpack": "^4.0.0 || ^5.0.0" } }, + "node_modules/file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "node_modules/file-selector": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz", @@ -9443,6 +9450,15 @@ "minimatch": "^3.0.4" } }, + "node_modules/filesaver": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/filesaver/-/filesaver-0.0.13.tgz", + "integrity": "sha512-ay2iShYJKmzKRPk89cgb14foqtCXcJIe5i+qdlSPAouKfBv7F2VZ0lxk9GjpcODe9p2YrXfi3Q+4CRn7ZDmleQ==", + "dependencies": { + "mkdirp": "^0.5.0", + "safename": "0.0.4" + } + }, "node_modules/filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -15612,6 +15628,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safename": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/safename/-/safename-0.0.4.tgz", + "integrity": "sha512-+n4TsvESZKTXbHxOTSyQ0Q1JCXRb6MohgrqC2fbdALzTNQP/IhPOnCNRA4JPtagQq+6DD5ZsQ3lKMy57BYvwJA==" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -24710,6 +24731,11 @@ "schema-utils": "^3.0.0" } }, + "file-saver": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/file-saver/-/file-saver-2.0.5.tgz", + "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==" + }, "file-selector": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/file-selector/-/file-selector-0.1.19.tgz", @@ -24726,6 +24752,15 @@ "minimatch": "^3.0.4" } }, + "filesaver": { + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/filesaver/-/filesaver-0.0.13.tgz", + "integrity": "sha512-ay2iShYJKmzKRPk89cgb14foqtCXcJIe5i+qdlSPAouKfBv7F2VZ0lxk9GjpcODe9p2YrXfi3Q+4CRn7ZDmleQ==", + "requires": { + "mkdirp": "^0.5.0", + "safename": "0.0.4" + } + }, "filesize": { "version": "8.0.7", "resolved": "https://registry.npmjs.org/filesize/-/filesize-8.0.7.tgz", @@ -29080,6 +29115,11 @@ "is-regex": "^1.1.4" } }, + "safename": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/safename/-/safename-0.0.4.tgz", + "integrity": "sha512-+n4TsvESZKTXbHxOTSyQ0Q1JCXRb6MohgrqC2fbdALzTNQP/IhPOnCNRA4JPtagQq+6DD5ZsQ3lKMy57BYvwJA==" + }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", diff --git a/package.json b/package.json index 68a020d..f90fa4c 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,8 @@ "date-fns": "^2.29.2", "email-validator": "^2.0.4", "env-cmd": "^10.1.0", + "file-saver": "^2.0.5", + "filesaver": "^0.0.13", "jwt-decode": "^3.1.2", "leaflet": "^1.8.0", "material-ui-dropzone": "^3.5.0", diff --git a/src/components/App/__snapshots__/App.test.js.snap b/src/components/App/__snapshots__/App.test.js.snap index d1a399a..7ec5bb8 100644 --- a/src/components/App/__snapshots__/App.test.js.snap +++ b/src/components/App/__snapshots__/App.test.js.snap @@ -9911,6 +9911,24 @@ exports[`App Route /vehicle-status authenticated 1`] = ` class="MuiTouchRipple-root" /> + + diff --git a/src/components/CANSelfServe/SelfServe/__snapshots__/index.test.jsx.snap b/src/components/CANSelfServe/SelfServe/__snapshots__/index.test.jsx.snap new file mode 100644 index 0000000..862780e --- /dev/null +++ b/src/components/CANSelfServe/SelfServe/__snapshots__/index.test.jsx.snap @@ -0,0 +1,374 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Render Render 1`] = ` +
+
+
+
+
+
+
+
+
+ +
+ +
+ +
+
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+
+
+ +
+ +
+ +
+
+ +
+
+
+
+
+
+ +
+
+ + ​ + +
+ + +
+
+
+
+ +
+
+
+
+
+
+`; diff --git a/src/components/CANSelfServe/SelfServe/index.jsx b/src/components/CANSelfServe/SelfServe/index.jsx new file mode 100644 index 0000000..4dbf59d --- /dev/null +++ b/src/components/CANSelfServe/SelfServe/index.jsx @@ -0,0 +1,197 @@ +import DateFnsUtils from '@date-io/date-fns'; +import { Button, Checkbox, Chip, CircularProgress, FormControl, Grid, InputLabel, ListItemText, MenuItem, Select } from "@material-ui/core"; +import { KeyboardDatePicker, KeyboardTimePicker, MuiPickersUtilsProvider } from '@material-ui/pickers'; +import React, { useEffect, useState } from "react"; +import { logger } from "../../../services/monitoring"; +import { CANSignalsExportProvider, useCANSignalsExportContext } from "../../Contexts/CANSignalsExportContext"; +import { useStatusContext } from "../../Contexts/StatusContext"; +import { useUserContext } from "../../Contexts/UserContext"; +import useStyles from "../../useStyles"; + +const MainForm = ({ id }) => { + const classes = useStyles(); + const { setMessage } = useStatusContext(); + const { busy, canSignals, getCANSignalList, getDynamicColumnCANSignals } = useCANSignalsExportContext(); + + const [selectedStartDate, setSelectedStartDate] = useState(new Date(Date.now() - 24 * 60 * 60 * 1000)); + const [selectedEndDate, setSelectedEndDate] = useState(new Date()); + const [selectedCanSignals, setSelectedCanSignals] = useState([]); + + + const { + token: { + idToken: { jwtToken: token }, + }, + } = useUserContext(); + + const handleSubmit = async (event) => { + event.preventDefault(); + let timestamp_start = Date.parse(selectedStartDate.toUTCString()) / 1000 + let timestamp_end = Date.parse(selectedEndDate.toUTCString()) / 1000 + try { + await getDynamicColumnCANSignals(id, timestamp_start, timestamp_end, selectedCanSignals, token) + } catch(e){ + setMessage(e.message); + logger.error(e.stack) + } + }; + + const isSubmitDisabled = !selectedStartDate || !selectedEndDate || selectedCanSignals.length === 0; + + useEffect(() => { + (async () => { + try { + if (!token) return; + await getCANSignalList(token); + } catch (e) { + setMessage(e.message); + logger.warn(e.stack); + } + })(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [token]); + + const handleDateChange = (value, dateType) => { + const newDate = new Date(value); + const oldDate = dateType === "start" ? selectedStartDate || new Date() : selectedEndDate || new Date(); + newDate.setHours(oldDate.getHours()); + newDate.setMinutes(oldDate.getMinutes()); + newDate.setSeconds(oldDate.getSeconds()); + if (dateType === "start") { + setSelectedStartDate(newDate); + } else { + setSelectedEndDate(newDate); + } + }; + + const handleTimeFromChange = (value) => { + setSelectedStartDate(value); + }; + const handleTimeToChange = (value) => { + setSelectedEndDate(value); + }; + + const handleSelectedItemsChange = (event) => { + setSelectedCanSignals(event.target.value); + }; + + return ( +
+ + + + + + handleDateChange(value, "start")} + KeyboardButtonProps={{ + 'aria-label': 'change date', + }} + /> + + + + + + handleDateChange(value, "end")} + KeyboardButtonProps={{ + 'aria-label': 'change date', + }} + /> + + + + + + + + + + Select CAN signals + + + + + + + +
+ ); +}; + +const CANSignalExport = (props) => ( + + + +); + +export default CANSignalExport; diff --git a/src/components/CANSelfServe/SelfServe/index.test.jsx b/src/components/CANSelfServe/SelfServe/index.test.jsx new file mode 100644 index 0000000..5106c38 --- /dev/null +++ b/src/components/CANSelfServe/SelfServe/index.test.jsx @@ -0,0 +1,42 @@ +jest.mock("../../Contexts/StatusContext"); +jest.mock("../../Contexts/UserContext"); +jest.mock("../../../services/CANSignalAPI"); +jest.useFakeTimers(); +jest.setSystemTime(new Date(2023, 3, 1, 6, 30, 45, 100)); + +import { render, waitFor } from "@testing-library/react"; +import { BrowserRouter } from "react-router-dom"; +import addSnapshotSerializer from "../../../utils/snapshot"; +import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing"; + +import { StatusProvider } from "../../Contexts/StatusContext"; +import { setToken, UserProvider } from "../../Contexts/UserContext"; +import CANSignalExport from "./index"; + +const renderCANSignalExport = async () => { + const { container } = render( + + + + + + + + ); + await waitFor(() => { + /* render */ + }); + return container; +}; + +describe("Render", () => { + beforeAll(() => { + addSnapshotSerializer(expect); + }); + + it("Render", async () => { + setToken(TEST_AUTH_OBJECT_FISKER); + const container = await renderCANSignalExport(); + expect(container).toMatchSnapshot(); + }); +}); diff --git a/src/components/CANSelfServe/SelfServeTab.jsx b/src/components/CANSelfServe/SelfServeTab.jsx new file mode 100644 index 0000000..ce250d3 --- /dev/null +++ b/src/components/CANSelfServe/SelfServeTab.jsx @@ -0,0 +1,21 @@ +import { Typography } from "@material-ui/core"; +import clsx from "clsx"; +import React from "react"; +import { useParams } from "react-router"; + +import useStyles from "../useStyles"; +import SelfServe from "./SelfServe"; + +const SelfServeTab = () => { + const { vin } = useParams(); + const classes = useStyles(); + + return ( +
+ Can Signals Self Serve + +
+ ); +}; + +export default SelfServeTab; diff --git a/src/components/Cars/Status/__snapshots__/index.test.jsx.snap b/src/components/Cars/Status/__snapshots__/index.test.jsx.snap index b08cc58..2036b7d 100644 --- a/src/components/Cars/Status/__snapshots__/index.test.jsx.snap +++ b/src/components/Cars/Status/__snapshots__/index.test.jsx.snap @@ -195,6 +195,24 @@ exports[`CarStatus Render 1`] = ` class="MuiTouchRipple-root" /> + + diff --git a/src/components/Cars/Status/index.jsx b/src/components/Cars/Status/index.jsx index 94a49c6..ffe3802 100644 --- a/src/components/Cars/Status/index.jsx +++ b/src/components/Cars/Status/index.jsx @@ -5,6 +5,7 @@ import { useParams } from "react-router"; import { useLocation } from "react-router-dom"; import { hasRole, Permissions } from "../../../utils/roles"; +import SelfServeTab from "../../CANSelfServe/SelfServeTab"; import { useStatusContext } from "../../Contexts/StatusContext"; import { useUserContext } from "../../Contexts/UserContext"; import TabPanel from "../../Controls/TabPanel"; @@ -17,7 +18,7 @@ import DigitalTwinTab from "./DigitalTwinTab"; import ECUsTab from "./ECUsTab"; import FleetsTab from "./FleetsTab"; import RemoteCommandsTab from "./RemoteCommandsTab"; -import TRexLogsTab from "./TRexLogsTab" +import TRexLogsTab from "./TRexLogsTab"; const tabHashes = ["details", "updates", "filters"]; @@ -60,6 +61,10 @@ const TabViews = [ component: FleetsTab, rolesPerProvider: Permissions.FiskerRead, }, + { + label: "CAN Signal Export", + component: SelfServeTab + } ]; const filterTabs = (data, groups, providers) => { diff --git a/src/components/Cars/Status/index.test.jsx b/src/components/Cars/Status/index.test.jsx index 21a32d6..739a284 100644 --- a/src/components/Cars/Status/index.test.jsx +++ b/src/components/Cars/Status/index.test.jsx @@ -6,15 +6,15 @@ jest.mock("@material-ui/core/utils/unstable_useId", () => ); import { render, waitFor } from "@testing-library/react"; -import { BrowserRouter } from "react-router-dom"; import routeData from "react-router"; +import { BrowserRouter } from "react-router-dom"; +import addSnapshotSerializer from "../../../utils/snapshot"; +import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing"; import { CANFiltersProvider } from "../../Contexts/CANFiltersContext"; import { StatusProvider } from "../../Contexts/StatusContext"; -import { UserProvider, setToken } from "../../Contexts/UserContext"; -import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing"; +import { setToken, UserProvider } from "../../Contexts/UserContext"; import CarStatus from "./index"; -import addSnapshotSerializer from "../../../utils/snapshot"; const renderCarStatus = async () => { const { container } = render( diff --git a/src/components/Contexts/CANFiltersContext.jsx b/src/components/Contexts/CANFiltersContext.jsx index b751827..4b2870d 100644 --- a/src/components/Contexts/CANFiltersContext.jsx +++ b/src/components/Contexts/CANFiltersContext.jsx @@ -1,6 +1,6 @@ import React, { useContext, useState } from "react"; import api from "../../services/CANFiltersAPI"; -import { validateCANID, validateFilter } from "../../utils/validationSupplier"; +import { validateCANID, validateFilter, validateVIN } from "../../utils/validationSupplier"; const CANFiltersContext = React.createContext(); @@ -100,10 +100,4 @@ export const CANFiltersProvider = ({ children }) => { ); }; -const validateVIN = (vin) => { - if (vin == null || vin.length !== 17) { - throw new Error("Invalid VIN"); - } -}; - export const useCANFiltersContext = () => useContext(CANFiltersContext); diff --git a/src/components/Contexts/CANSignalsExportContext.jsx b/src/components/Contexts/CANSignalsExportContext.jsx new file mode 100644 index 0000000..175c851 --- /dev/null +++ b/src/components/Contexts/CANSignalsExportContext.jsx @@ -0,0 +1,73 @@ +import React, { useContext, useState } from "react"; +import api from "../../services/CANSignalAPI"; +import { validateVIN } from "../../utils/validationSupplier"; + +const CANSignalsExportContext = React.createContext(); + +export const CANSignalsExportProvider = ({ children }) => { + const [busy, setBusy] = useState(false); + + const [canSignals, setCanSignals] = useState([]); + + const getCANSignalList = async (token) => { + try { + setBusy(true); + const result = await api.getCanSignalList(token); + if (result.error) { + throw new Error(`Get can signal list error. ${result.message}`); + } + setCanSignals(result.data ?? []); + return result; + } finally { + setBusy(false); + } + }; + + const getDynamicColumnCANSignals = async (vin, timestart, timeend, cansingals, token) => { + try { + setBusy(true) + if (!vin) return; + + validateVIN(vin); + if (timestart > timeend) throw new Error("Start time cannot be after end time"); + const result = await api.getCanSignalsVin(vin, timestart, timeend, cansingals, token); + if (result.error || !result.ok) + throw new Error(`Get CAN signals error. ${result.message}`); + + const blob = await result.blob(); + const reader = new FileReader(); + reader.onload = () => { + const csvData = reader.result; + const blob = new Blob([csvData], { type: 'text/csv;charset=utf-8' }); + const link = document.createElement('a'); + link.href = window.URL.createObjectURL(blob); + link.download = 'CAN_signals.csv'; + link.click(); + }; + reader.readAsText(blob); + } catch (e) { + throw new Error(e) + } + finally { + setBusy(false) + } + } + + return ( + + {children} + + ); +}; + +export const useCANSignalsExportContext = () => useContext(CANSignalsExportContext); + + + diff --git a/src/components/Contexts/VehicleContext.jsx b/src/components/Contexts/VehicleContext.jsx index 412c64e..31d0eb6 100644 --- a/src/components/Contexts/VehicleContext.jsx +++ b/src/components/Contexts/VehicleContext.jsx @@ -1,6 +1,8 @@ import React, { useContext, useState } from "react"; + import { logger } from "../../services/monitoring"; import api from "../../services/vehiclesAPI"; +import { validateVIN } from "../../utils/validationSupplier"; const VehicleContext = React.createContext(); @@ -285,10 +287,4 @@ const validateVehicle = (v) => { validateVIN(v.vin); }; -const validateVIN = (vin) => { - if (vin == null || vin.length !== 17) { - throw new Error("Invalid VIN"); - } -}; - export const useVehicleContext = () => useContext(VehicleContext); diff --git a/src/components/Contexts/__mocks__/CANSignalsExportContext.jsx b/src/components/Contexts/__mocks__/CANSignalsExportContext.jsx new file mode 100644 index 0000000..a69a596 --- /dev/null +++ b/src/components/Contexts/__mocks__/CANSignalsExportContext.jsx @@ -0,0 +1,23 @@ +let busy = false; +let canSignals = [ + { + signal_name: "123", + }, + { + signal_name: "456", + }, + { + signal_name: "789", + }, +]; + +export const CANSignalsExportProvider = ({ children }) => { + return
{children}
; +}; + +export const useCANSignalsExportContext= () => ({ + busy, + canSignals, + getCANSignalList: jest.fn(), + getDynamicColumnCANSignals: jest.fn(), +}); diff --git a/src/components/Controls/TRexLogs/index.jsx b/src/components/Controls/TRexLogs/index.jsx index 29e78d3..165c4ed 100644 --- a/src/components/Controls/TRexLogs/index.jsx +++ b/src/components/Controls/TRexLogs/index.jsx @@ -8,12 +8,12 @@ import { TablePagination, TableRow } from "@material-ui/core"; -import LinearProgress from '@mui/material/LinearProgress'; import Checkbox from '@mui/material/Checkbox'; -import FormGroup from '@mui/material/FormGroup'; -import FormControlLabel from '@mui/material/FormControlLabel'; import FormControl from '@mui/material/FormControl'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import FormGroup from '@mui/material/FormGroup'; import FormLabel from '@mui/material/FormLabel'; +import LinearProgress from '@mui/material/LinearProgress'; import { KeyboardDatePicker, MuiPickersUtilsProvider @@ -134,7 +134,6 @@ const TRexLogsTable = ({ vin, token, classes }) => { let controller = new AbortController() const readBlob = async (offset, count) => { - console.log(`reading from offset: ${offset}`) return await api.getTRexLogs(vin, fromatDateForRequest(selectedDate), offset, count, "UP", token, controller) } const getDesiredSize = () => { @@ -156,10 +155,8 @@ const TRexLogsTable = ({ vin, token, classes }) => { fetched.error = result.error break } - console.log(`ret.RealOffset ${result.RealOffset}\nret.bytesRead ${result.bytesRead}\ndesired offset ${offset}\ndesired read size ${readSize}\nblobsize: ${result.blobSize}`) setBlobSize(result.blobSize) readSize *= 2 - console.log(`new read size: ${readSize}`) offset = result.RealOffset + result.bytesRead setCurrentOffset(offset) fetched = transformLogs(result.data).concat(fetched) @@ -179,7 +176,6 @@ const TRexLogsTable = ({ vin, token, classes }) => { setTotal(0) const msg = `Cannot fetch logs for ${fromatDateForRequest(selectedDate)}` setMessage(msg) - console.log(`${msg}, Cloud error:\n${fetched.error}`); return } setCurrentOffset(offset) @@ -191,9 +187,7 @@ const TRexLogsTable = ({ vin, token, classes }) => { try { if (!vin || !token) return; const desiredSize = getDesiredSize() - console.log(`desired size: ${desiredSize}, actual size: ${logs.length}`) if (desiredSize < logs.length || allLogsFetched) { - console.log(`enough logs in cache`) setTotal(getFilteredLogs(logs).length) return } @@ -228,7 +222,6 @@ const TRexLogsTable = ({ vin, token, classes }) => { const handleNewFilter = (event) => { setPageIndex(0) - console.log(event) setCurrentLogLevels({ ...currectLogLevels, [event.target.defaultValue]: event.target.checked, diff --git a/src/components/SMS/Send/__snapshots__/index.test.jsx.snap b/src/components/SMS/Send/__snapshots__/index.test.jsx.snap index d03c0ae..dbbf31c 100644 --- a/src/components/SMS/Send/__snapshots__/index.test.jsx.snap +++ b/src/components/SMS/Send/__snapshots__/index.test.jsx.snap @@ -1,3 +1,168 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`SMS Send Component Render 1`] = `undefined`; +exports[`SMS Send Component Render 1`] = ` +
+
+
+
+
+
+ +
+ + +
+
+
+ +
+ + +
+
+ + +
+
+
+
+
+`; diff --git a/src/components/SMS/Send/index.jsx b/src/components/SMS/Send/index.jsx index 0d4005b..0abe10a 100644 --- a/src/components/SMS/Send/index.jsx +++ b/src/components/SMS/Send/index.jsx @@ -1,9 +1,9 @@ import React, { useEffect, useState } from "react"; -import { useSMSContext, SMSProvider } from "../../Contexts/SMSContext"; +import { logger } from "../../../services/monitoring"; +import { SMSProvider, useSMSContext } from "../../Contexts/SMSContext"; import { useStatusContext } from "../../Contexts/StatusContext"; import { useUserContext } from "../../Contexts/UserContext"; -import { logger } from "../../../services/monitoring"; import SendForm from "./SendForm"; import ViewResult from "./ViewResult"; diff --git a/src/components/SMS/Send/index.test.jsx b/src/components/SMS/Send/index.test.jsx index 29ce3dd..3e9b5b8 100644 --- a/src/components/SMS/Send/index.test.jsx +++ b/src/components/SMS/Send/index.test.jsx @@ -1,15 +1,15 @@ -import {TEST_AUTH_OBJECT_FISKER, TEST_TOKEN_FISKER} from "../../../utils/testing"; +import { TEST_AUTH_OBJECT_FISKER } from "../../../utils/testing"; jest.mock("../../Contexts/SMSContext"); jest.mock("../../Contexts/UserContext"); -import SMSSend from "./index"; -import {BrowserRouter} from "react-router-dom"; -import {UserProvider, setToken} from "../../Contexts/UserContext"; -import {StatusProvider} from "../../Contexts/StatusContext"; +import { render, waitFor } from "@testing-library/react"; +import { BrowserRouter } from "react-router-dom"; import addSnapshotSerializer from "../../../utils/snapshot"; -import {render, waitFor} from "@testing-library/react"; +import { StatusProvider } from "../../Contexts/StatusContext"; +import { setToken, UserProvider } from "../../Contexts/UserContext"; +import SMSSend from "./index"; const renderSendSMS = async () => { @@ -38,7 +38,7 @@ describe("SMS Send Component", () => { it("Render", async () => { // setToken(TEST_AUTH_OBJECT_FISKER); - const {container} = await renderSendSMS(); + const container = await renderSendSMS(); expect(container).toMatchSnapshot(); }); }); \ No newline at end of file diff --git a/src/services/CANSignalAPI.js b/src/services/CANSignalAPI.js new file mode 100644 index 0000000..6035dab --- /dev/null +++ b/src/services/CANSignalAPI.js @@ -0,0 +1,32 @@ +import { + addQueryParams, errorHandler, fetchRespHandler, getAuthHeaderOptions +} from "../utils/http"; + +const API_ENDPOINT = process.env.REACT_APP_OTA_SERVICE_URL; + +const canSignalAPI = { + + getCanSignalsVin: async (vin, timestamp_start, timestamp_end, can_signals, token) => + fetch(addQueryParams(`${API_ENDPOINT}/can_signals_export`, { vin, timestamp_start, timestamp_end, can_signals }), { + method: "GET", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ), + responseType: "blob" + }) + .catch(errorHandler), + + + getCanSignalList: async (token) => + fetch(addQueryParams(`${API_ENDPOINT}/can_signals_list`), { + method: "GET", + headers: Object.assign( + { "Content-Type": "application/json" }, + getAuthHeaderOptions(token) + ), + }) + .then(fetchRespHandler) + .catch(errorHandler), +}; +export default canSignalAPI; diff --git a/src/services/__mocks__/CANSignalAPI.js b/src/services/__mocks__/CANSignalAPI.js new file mode 100644 index 0000000..e088c01 --- /dev/null +++ b/src/services/__mocks__/CANSignalAPI.js @@ -0,0 +1,16 @@ +const canSignalList = [ + { signal_name: "123"}, + { signal_name: "456"}, + { signal_name: "789"} +]; + +const canSignalAPI = { + getCanSignalsVin: async (vin, timestamp_start, timestamp_end, can_signals, token) => { + return { data: "fake data" }; + }, + getCanSignalList: async (token) => { + return canSignalList; + } +}; + +export default canSignalAPI; \ No newline at end of file diff --git a/src/utils/http.js b/src/utils/http.js index 2d8b69a..5709cb4 100644 --- a/src/utils/http.js +++ b/src/utils/http.js @@ -6,7 +6,6 @@ export const addQueryParams = (url, params) => { const u = new URL(url); Object.keys(params).forEach((key) => u.searchParams.append(key, params[key])); - return u.toString(); };