diff --git a/package-lock.json b/package-lock.json index 8a493c2b8bac169b1651a32057be720d171053ac..a48b2dcc7472456d825f0460de3e7b561a91aa4c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,7 @@ "@ahooksjs/use-url-state": "^3.5.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", - "@ess-ics/ce-ui-common": "^0.3.3", + "@ess-ics/ce-ui-common": "^0.3.5", "@fontsource/roboto": "^4.1.0", "@mui/icons-material": "^5.14.1", "@mui/material": "^5.14.1", @@ -2655,9 +2655,9 @@ } }, "node_modules/@ess-ics/ce-ui-common": { - "version": "0.3.3", - "resolved": "https://artifactory.esss.lu.se/artifactory/api/npm/ics-npm/@ess-ics/ce-ui-common/-/ce-ui-common-0.3.3.tgz", - "integrity": "sha1-7ExgDtf0QXJ7uli5f+WM16kbNAE=", + "version": "0.3.5", + "resolved": "https://artifactory.esss.lu.se/artifactory/api/npm/ics-npm/@ess-ics/ce-ui-common/-/ce-ui-common-0.3.5.tgz", + "integrity": "sha1-0WjyKPTEvzNugP/jTcw3loGtlyQ=", "dependencies": { "@fontsource/titillium-web": "^4.5.9", "@mui/x-data-grid-pro": "^6.5.0", @@ -41722,9 +41722,9 @@ "dev": true }, "@ess-ics/ce-ui-common": { - "version": "0.3.3", - "resolved": "https://artifactory.esss.lu.se/artifactory/api/npm/ics-npm/@ess-ics/ce-ui-common/-/ce-ui-common-0.3.3.tgz", - "integrity": "sha1-7ExgDtf0QXJ7uli5f+WM16kbNAE=", + "version": "0.3.5", + "resolved": "https://artifactory.esss.lu.se/artifactory/api/npm/ics-npm/@ess-ics/ce-ui-common/-/ce-ui-common-0.3.5.tgz", + "integrity": "sha1-0WjyKPTEvzNugP/jTcw3loGtlyQ=", "requires": { "@fontsource/titillium-web": "^4.5.9", "@mui/x-data-grid-pro": "^6.5.0", diff --git a/package.json b/package.json index b5d604a18fbd81e01761a326fa5a0ae2cb7e48d9..4a254e85a5a6dde7bf8fa5f56da476e2aff14ef3 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "@ahooksjs/use-url-state": "^3.5.0", "@emotion/react": "^11.11.1", "@emotion/styled": "^11.11.0", - "@ess-ics/ce-ui-common": "^0.3.3", + "@ess-ics/ce-ui-common": "^0.3.5", "@fontsource/roboto": "^4.1.0", "@mui/icons-material": "^5.14.1", "@mui/material": "^5.14.1", diff --git a/src/App.js b/src/App.js index a67d3a21881c5c5e2267ac6409736027633e04de..e4d199025551fe57f7f034fcacc968a0de78c74e 100644 --- a/src/App.js +++ b/src/App.js @@ -9,7 +9,7 @@ import { IOCListView } from "./views/IOC/IOCListView"; import NavigationMenu from "./components/navigation/NavigationMenu"; import { IOCDetailsAccessControl } from "./views/IOC/IOCDetailsAccessControl"; import { JobDetailsAccessControl } from "./views/jobs/JobDetailsAccessControl"; -import { APIProvider } from "./api/SwaggerApi"; +import { LegacyApiProvider } from "./api/SwaggerApi"; import { UserProvider } from "./api/UserProvider"; import { HostListView } from "./views/host/HostListView"; import { HostDetailsAccessControl } from "./views/host/HostDetailsAccessControl"; @@ -29,6 +29,7 @@ import { GlobalAppBarContext } from "@ess-ics/ce-ui-common"; import { applicationTitle } from "./components/common/Helper"; import { CreateIOCAccessControl } from "./views/IOC/CreateIOCAccessControl"; import { UserDetailsAccessControl } from "./views/UserPage/UserDetailsAccessControl"; +import { DeployAPIProvider } from "./api/DeployApi"; // setting up the application (TAB)title function App() { @@ -45,95 +46,97 @@ function App() { <StyledEngineProvider injectFirst> <ThemeProvider theme={theme}> <CssBaseline /> - <APIProvider> - <UserProvider> - <TokenRenew /> - <NotificationProvider> - <NavigationMenu> - <Routes> - <Route - path="/" - element={<Navigate to="/iocs" />} - exact - /> - <Route - path="/records" - element={<RecordListView />} - exact - /> - <Route - path="/records/:name" - element={<RecordDetailsView />} - exact - /> - <Route - path="/iocs/create" - element={<CreateIOCAccessControl />} - exact - /> - <Route - path="/iocs/:id" - element={<IOCDetailsAccessControl exact />} - /> - <Route - path="/iocs" - element={<IOCListView />} - exact - /> - <Route - path="/jobs" - element={<JobLogAccessControl />} - exact - /> - <Route - path="/jobs/:id" - element={<JobDetailsAccessControl exact />} - /> - <Route - path="/hosts/:id" - element={<HostDetailsAccessControl />} - exact - /> - <Route - path="/hosts" - element={<HostListView />} - exact - /> - <Route - path="/statistics" - element={<StatisticsView />} - exact - /> - <Route - path="/help" - element={<HelpView />} - exact - /> - <Route - path="/login" - element={<LoginView />} - exact - /> - <Route - path="/error-test" - element={<TestErrorView />} - exact - /> - <Route - path="/user/:userName" - element={<UserDetailsAccessControl />} - exact - /> - <Route - path="*" - element={<NotFoundView />} - exact - /> - </Routes> - </NavigationMenu> - </NotificationProvider> - </UserProvider> - </APIProvider> + <DeployAPIProvider> + <LegacyApiProvider> + <UserProvider> + <TokenRenew /> + <NotificationProvider> + <NavigationMenu> + <Routes> + <Route + path="/" + element={<Navigate to="/iocs" />} + exact + /> + <Route + path="/records" + element={<RecordListView />} + exact + /> + <Route + path="/records/:name" + element={<RecordDetailsView />} + exact + /> + <Route + path="/iocs/create" + element={<CreateIOCAccessControl />} + exact + /> + <Route + path="/iocs/:id" + element={<IOCDetailsAccessControl exact />} + /> + <Route + path="/iocs" + element={<IOCListView />} + exact + /> + <Route + path="/jobs" + element={<JobLogAccessControl />} + exact + /> + <Route + path="/jobs/:id" + element={<JobDetailsAccessControl exact />} + /> + <Route + path="/hosts/:id" + element={<HostDetailsAccessControl />} + exact + /> + <Route + path="/hosts" + element={<HostListView />} + exact + /> + <Route + path="/statistics" + element={<StatisticsView />} + exact + /> + <Route + path="/help" + element={<HelpView />} + exact + /> + <Route + path="/login" + element={<LoginView />} + exact + /> + <Route + path="/error-test" + element={<TestErrorView />} + exact + /> + <Route + path="/user/:userName" + element={<UserDetailsAccessControl />} + exact + /> + <Route + path="*" + element={<NotFoundView />} + exact + /> + </Routes> + </NavigationMenu> + </NotificationProvider> + </UserProvider> + </LegacyApiProvider> + </DeployAPIProvider> </ThemeProvider> </StyledEngineProvider> </SnackbarProvider> diff --git a/src/api/APIProvider.spec.js b/src/api/APIProvider.spec.js index 01ce3fd6fd27497a4feec597849b961458832fb4..0e9bd2de12151a2b19c7343d846a4c31ebb2408a 100644 --- a/src/api/APIProvider.spec.js +++ b/src/api/APIProvider.spec.js @@ -1,5 +1,5 @@ import React, { useContext } from "react"; -import { apiContext, APIProvider, useAPI } from "./SwaggerApi"; +import { apiContext, LegacyApiProvider, useAPI } from "./SwaggerApi"; import { SnackbarProvider } from "notistack"; function AppHarness({ children }) { @@ -37,16 +37,16 @@ context("API", () => { }); }); - describe("APIProvider", () => { + describe("LegacyApiProvider", () => { it("makes the API available via context", () => { const Glue = () => { const api = useContext(apiContext); return <DisplayAPISpecification api={api} />; }; mountIntoHarness( - <APIProvider> + <LegacyApiProvider> <Glue /> - </APIProvider> + </LegacyApiProvider> ); checkAPIDisplayed(); }); diff --git a/src/api/DeployApi.js b/src/api/DeployApi.js new file mode 100644 index 0000000000000000000000000000000000000000..641fe78e403e4ba14ce786175f1ce168a02e97ad --- /dev/null +++ b/src/api/DeployApi.js @@ -0,0 +1,38 @@ +/** + * DeployAPIProvider + * Provide CE Deploy Backend API + */ +import React from "react"; +import { createContext } from "react"; +import { APIProvider } from "@ess-ics/ce-ui-common"; +import { node, arrayOf, oneOfType } from "prop-types"; + +const propTypes = { + /** Elements used as children */ + children: oneOfType([ + /** Array of elements. */ + arrayOf(node), + /** One or more elements without array. */ + node + ]) +}; + +export const apiContext = createContext(null); + +const apiOptions = { + url: `${window.API_BASE_ENDPOINT}`, + server: `${window.SERVER_ADDRESS}` +}; + +export function DeployAPIProvider({ children }) { + return ( + <APIProvider + context={apiContext} + options={apiOptions} + > + {children} + </APIProvider> + ); +} + +DeployAPIProvider.propTypes = propTypes; diff --git a/src/api/SwaggerApi.js b/src/api/SwaggerApi.js index 26683fa5f97cb79527b64842da8e09b725532708..756c084f987860cbafeb4a1350bc619ccc4b6978 100644 --- a/src/api/SwaggerApi.js +++ b/src/api/SwaggerApi.js @@ -40,7 +40,7 @@ export function useAPI() { export const apiContext = createContext(null); -export function APIProvider({ children }) { +export function LegacyApiProvider({ children }) { const [api] = useAPI(); return api ? ( @@ -905,20 +905,3 @@ export function useUpdateActiveDeploymentHost(id, onError) { ); return useAsync({ fcn: method, call: false, onError: onError }); } - -export function unpackStatistics(statistics) { - return { ...statistics }; -} - -export function useStatistics(statisticsType) { - const api = useContext(apiContext); - const method = useCallAndUnpack( - () => - api.apis.Statistics.calculateStatistics({ - statistics_type: statisticsType - }), - unpackStatistics - ); - - return useAsync({ fcn: method, call: true }); -} diff --git a/src/api/UserProvider.spec.js b/src/api/UserProvider.spec.js index 7eab7552b05dc345d85bf175eb9aef3c6fc710ef..f0477f49f311efbfa5b7f56869fe1f10d53d998e 100644 --- a/src/api/UserProvider.spec.js +++ b/src/api/UserProvider.spec.js @@ -1,5 +1,5 @@ import React, { useContext } from "react"; -import { APIProvider } from "./SwaggerApi"; +import { LegacyApiProvider } from "./SwaggerApi"; import { UserProvider } from "./UserProvider"; import { userContext } from "@ess-ics/ce-ui-common"; import { SnackbarProvider } from "notistack"; @@ -10,7 +10,7 @@ function AppHarness({ children }) { preventDuplicate maxSnack="5" > - <APIProvider>{children}</APIProvider> + <LegacyApiProvider>{children}</LegacyApiProvider> </SnackbarProvider> ); } diff --git a/src/components/auth/TokenRenew/TokenRenew.spec.js b/src/components/auth/TokenRenew/TokenRenew.spec.js index 30360a05b086e420432cb299ad5d53c6757c0929..fc6e44cf4e586f44cc5d4a30c45a517e4a130c69 100644 --- a/src/components/auth/TokenRenew/TokenRenew.spec.js +++ b/src/components/auth/TokenRenew/TokenRenew.spec.js @@ -1,6 +1,6 @@ import { SnackbarProvider } from "notistack"; import React from "react"; -import { APIProvider } from "../../../api/SwaggerApi"; +import { LegacyApiProvider } from "../../../api/SwaggerApi"; import { UserProvider } from "../../../api/UserProvider"; import TokenRenew from "."; @@ -10,9 +10,9 @@ function AppHarness({ children }) { preventDuplicate maxSnack="5" > - <APIProvider> + <LegacyApiProvider> <UserProvider>{children}</UserProvider> - </APIProvider> + </LegacyApiProvider> </SnackbarProvider> ); } diff --git a/src/components/common/notification/Notifications.spec.js b/src/components/common/notification/Notifications.spec.js index a00a3ae053159c43ad69bd1ecfafdecdfde9f258..82bd0c192dbbd9a8b7f5e618506fef31d55267b3 100644 --- a/src/components/common/notification/Notifications.spec.js +++ b/src/components/common/notification/Notifications.spec.js @@ -1,6 +1,6 @@ import { SnackbarProvider } from "notistack"; import React, { useContext } from "react"; -import { APIProvider } from "../../../api/SwaggerApi"; +import { LegacyApiProvider } from "../../../api/SwaggerApi"; import { UserProvider } from "../../../api/UserProvider"; import { userContext } from "@ess-ics/ce-ui-common"; import { useEffectOnMount } from "../../../hooks/MountEffects"; @@ -12,9 +12,9 @@ function AppHarness({ children }) { preventDuplicate maxSnack="5" > - <APIProvider> + <LegacyApiProvider> <UserProvider>{children}</UserProvider> - </APIProvider> + </LegacyApiProvider> </SnackbarProvider> ); } diff --git a/src/components/statistics/HostStatistics/HostStatistics.js b/src/components/statistics/HostStatistics/HostStatistics.js index 7e5f9af98e6e11f745f4002cd0c77f296058e38a..06adc1c7d5ec28792a2b77ae9634c409b8f10b1c 100644 --- a/src/components/statistics/HostStatistics/HostStatistics.js +++ b/src/components/statistics/HostStatistics/HostStatistics.js @@ -1,34 +1,53 @@ -import React from "react"; -import { Paper } from "@mui/material"; -import { KeyValueTable } from "@ess-ics/ce-ui-common/dist/components/common/KeyValueTable"; -import { useStatistics } from "../../../api/SwaggerApi"; +import React, { useContext, useEffect } from "react"; +import { Paper, Skeleton } from "@mui/material"; +import { KeyValueTable, useAPIMethod } from "@ess-ics/ce-ui-common"; +import { apiContext } from "../../../api/DeployApi"; + +const renderValue = (val) => { + return val ?? <Skeleton width="3ch" />; +}; export function HostStatistics() { - const [hostsRegistered] = useStatistics("HOSTS_REGISTERED"); - const [hostsWithIocs] = useStatistics("HOSTS_WITH_IOCS"); - const [hostsReachable] = useStatistics("HOSTS_REACHABLE"); - const [hostsWithoutIssues] = useStatistics("HOSTS_WITHOUT_ISSUE"); + const client = useContext(apiContext); - let hostStats = { - "Registered IOC-hosts": hostsRegistered?.value, - "IOC-hosts with IOCs": hostsWithIocs?.value, - "Reachable IOC-hosts with IOCs": hostsReachable?.value, - "Issue free IOC-hosts with IOCs": hostsWithoutIssues?.value - }; + // Do not call on render; when one of these finishes then it and the others + // will be called again on the next render; this causes an infinite loop. + const { value: hostsRegistered, wrapper: getHostsRegistered } = useAPIMethod({ + fcn: client.apis.Statistics.calculateStatistics, + call: false + }); + const { value: hostsWithIocs, wrapper: getHostsWithIocs } = useAPIMethod({ + fcn: client.apis.Statistics.calculateStatistics, + call: false + }); + const { value: hostsReachable, wrapper: getHostsReachable } = useAPIMethod({ + fcn: client.apis.Statistics.calculateStatistics, + call: false + }); + const { value: hostsWithoutIssues, wrapper: getHostsWithoutIssues } = + useAPIMethod({ + fcn: client.apis.Statistics.calculateStatistics, + call: false + }); - if ( - hostsRegistered || - hostsWithIocs || - hostsReachable || - hostsWithoutIssues - ) { - hostStats = { - "Registered IOC-hosts": hostsRegistered?.value, - "IOC-hosts with IOCs": hostsWithIocs?.value, - "Reachable IOC-hosts with IOCs": hostsReachable?.value, - "Issue free IOC-hosts with IOCs": hostsWithoutIssues?.value - }; - } + useEffect(() => { + getHostsRegistered({ statistics_type: "HOSTS_REGISTERED" }); + getHostsWithIocs({ statistics_type: "HOSTS_WITH_IOCS" }); + getHostsReachable({ statistics_type: "HOSTS_REACHABLE" }); + getHostsWithoutIssues({ statistics_type: "HOSTS_WITHOUT_ISSUE" }); + }, [ + getHostsRegistered, + getHostsWithIocs, + getHostsReachable, + getHostsWithoutIssues + ]); + + const hostStats = { + "Registered IOC-hosts": renderValue(hostsRegistered?.value), + "IOC-hosts with IOCs": renderValue(hostsWithIocs?.value), + "Reachable IOC-hosts with IOCs": renderValue(hostsReachable?.value), + "Issue free IOC-hosts with IOCs": renderValue(hostsWithoutIssues?.value) + }; return ( <Paper> diff --git a/src/components/statistics/IOCStatistics/IOCStatistics.js b/src/components/statistics/IOCStatistics/IOCStatistics.js index 96f3884ca919b7124df115f2c27fbc7069287c53..074661170c1efad892d226697a71a993b3d3f2c6 100644 --- a/src/components/statistics/IOCStatistics/IOCStatistics.js +++ b/src/components/statistics/IOCStatistics/IOCStatistics.js @@ -1,29 +1,54 @@ -import React from "react"; -import { Paper } from "@mui/material"; +import React, { useContext, useEffect } from "react"; +import { Paper, Skeleton } from "@mui/material"; import { KeyValueTable } from "@ess-ics/ce-ui-common/dist/components/common/KeyValueTable"; -import { useStatistics } from "../../../api/SwaggerApi"; +import { apiContext } from "../../../api/DeployApi"; +import { useAPIMethod } from "@ess-ics/ce-ui-common"; + +const renderValue = (val) => { + return val ?? <Skeleton width="3ch" />; +}; export function IOCStatistics() { - const [iocsRegistered] = useStatistics("IOCS_REGISTERED"); - const [iocsDeployed] = useStatistics("IOCS_DEPLOYED"); - const [iocsReachable] = useStatistics("IOCS_RUNNING"); - const [iocsWithoutIssues] = useStatistics("IOCS_RUNNING_WITHOUT_ISSUE"); + const client = useContext(apiContext); - let iocStats = { - "Registered IOCs": iocsRegistered?.value, - "Deployed IOCs": iocsDeployed?.value, - "Running IOCs": iocsReachable?.value, - "Issue-free and running IOCs": iocsWithoutIssues?.value - }; + // Do not call on render; when one of these finishes then it and the others + // will be called again on the next render; this causes an infinite loop. + const { value: iocsRegistered, wrapper: getIocsRegistered } = useAPIMethod({ + fcn: client.apis.Statistics.calculateStatistics, + call: false + }); + const { value: iocsDeployed, wrapper: getIocsDeployed } = useAPIMethod({ + fcn: client.apis.Statistics.calculateStatistics, + call: false + }); + const { value: iocsReachable, wrapper: getIocsReachable } = useAPIMethod({ + fcn: client.apis.Statistics.calculateStatistics, + call: false + }); + const { value: iocsWithoutIssues, wrapper: getIocsWithoutIssues } = + useAPIMethod({ + fcn: client.apis.Statistics.calculateStatistics, + call: false + }); - if (iocsRegistered || iocsDeployed || iocsReachable || iocsWithoutIssues) { - iocStats = { - "Registered IOCs": iocsRegistered?.value, - "Deployed IOCs": iocsDeployed?.value, - "Running IOCs": iocsReachable?.value, - "Issue-free and running IOCs": iocsWithoutIssues?.value - }; - } + useEffect(() => { + getIocsRegistered({ statistics_type: "IOCS_REGISTERED" }); + getIocsDeployed({ statistics_type: "IOCS_DEPLOYED" }); + getIocsReachable({ statistics_type: "IOCS_RUNNING" }); + getIocsWithoutIssues({ statistics_type: "IOCS_RUNNING_WITHOUT_ISSUE" }); + }, [ + getIocsDeployed, + getIocsReachable, + getIocsRegistered, + getIocsWithoutIssues + ]); + + const iocStats = { + "Registered IOCs": renderValue(iocsRegistered?.value), + "Deployed IOCs": renderValue(iocsDeployed?.value), + "Running IOCs": renderValue(iocsReachable?.value), + "Issue-free and running IOCs": renderValue(iocsWithoutIssues?.value) + }; return ( <Paper> diff --git a/src/mocks/AppHarness.js b/src/mocks/AppHarness.js index cb0ff47983f0658888712e7903a217b82babd416..89d1e6619dc40a9efab9f01a04464782e5459c07 100644 --- a/src/mocks/AppHarness.js +++ b/src/mocks/AppHarness.js @@ -3,37 +3,12 @@ import { SnackbarProvider } from "notistack"; import { Container, CssBaseline, StyledEngineProvider } from "@mui/material"; import { ThemeProvider } from "@mui/material/styles"; import { theme } from "../style/Theme"; -import { APIProvider } from "../api/SwaggerApi"; +import { LegacyApiProvider } from "../api/SwaggerApi"; import { UserProvider } from "../api/UserProvider"; import { NotificationProvider } from "../components/common/notification/Notifications"; import NavigationMenu from "../components/navigation/NavigationMenu"; import { MemoryRouter } from "react-router-dom"; - -export function AppHarness({ children, initialHistory = ["/"] }) { - return ( - <SnackbarProvider - preventDuplicate - maxSnack="5" - > - <StyledEngineProvider injectFirst> - <ThemeProvider theme={theme}> - <CssBaseline /> - <MemoryRouter initialEntries={initialHistory}> - <APIProvider> - <UserProvider> - <NotificationProvider> - <NavigationMenu> - <Container>{children}</Container> - </NavigationMenu> - </NotificationProvider> - </UserProvider> - </APIProvider> - </MemoryRouter> - </ThemeProvider> - </StyledEngineProvider> - </SnackbarProvider> - ); -} +import { DeployAPIProvider } from "../api/DeployApi"; export function RouterHarness({ children, initialHistory = ["/"] }) { return ( @@ -41,9 +16,30 @@ export function RouterHarness({ children, initialHistory = ["/"] }) { <ThemeProvider theme={theme}> <CssBaseline /> <MemoryRouter initialEntries={initialHistory}> - <APIProvider>{children}</APIProvider> + <DeployAPIProvider> + <LegacyApiProvider>{children}</LegacyApiProvider> + </DeployAPIProvider> </MemoryRouter> </ThemeProvider> </StyledEngineProvider> ); } + +export function AppHarness({ children, initialHistory = ["/"] }) { + return ( + <SnackbarProvider + preventDuplicate + maxSnack="5" + > + <RouterHarness initialHistory={initialHistory}> + <UserProvider> + <NotificationProvider> + <NavigationMenu> + <Container>{children}</Container> + </NavigationMenu> + </NotificationProvider> + </UserProvider> + </RouterHarness> + </SnackbarProvider> + ); +} diff --git a/src/mocks/mockAPI.js b/src/mocks/mockAPI.js index 7eca16ec9749b641cd087e055bb021dabb7efd86..7fa3a2cd56788c32645b029afce962f9c43a6242 100644 --- a/src/mocks/mockAPI.js +++ b/src/mocks/mockAPI.js @@ -1,5 +1,12 @@ import { matchPath } from "react-router-dom"; +// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random +function randBetween(min, max) { + min = Math.ceil(min); + max = Math.floor(max); + return Math.floor(Math.random() * (max - min + 1) + min); +} + function getParameters(req, pattern) { const match = matchPath( { path: "/api/v1" + pattern }, @@ -206,6 +213,31 @@ function activeIocHistory(req) { return { body }; } +function calculateStatistics(req) { + const params = getParameters(req, "/statistics/general/:val"); + const validValues = [ + "HOSTS_REGISTERED", + "HOSTS_WITH_IOCS", + "HOSTS_REACHABLE", + "HOSTS_WITHOUT_ISSUE", + "IOCS_REGISTERED", + "IOCS_DEPLOYED", + "IOCS_RUNNING", + "IOCS_RUNNING_WITHOUT_ISSUE" + ]; + if (validValues.includes(params?.val)) { + return { + status: 200, + body: { + statisticType: params.val, + value: randBetween(100, 1500) + } + }; + } else { + return { status: 400 }; + } +} + function fetchIOCByName(req) { const body = require("./fixtures/NamingNames.json"); return { body }; @@ -247,7 +279,8 @@ const mockAPI = { getStatistics, currentlyActiveIocs, iocDeploymentHistory, - activeIocHistory + activeIocHistory, + calculateStatistics }, naming: { fetchIOCByName @@ -377,7 +410,11 @@ export const apiHandlers = [ qRegExp(".*/statistics/general/active_ioc_history"), mockAPI.statistics.activeIocHistory ), - + makeHandler( + "GET", + qRegExp(".*/statistics/general/.*"), + mockAPI.statistics.calculateStatistics + ), // naming makeHandler( "GET",