diff --git a/backend/src/api/tests/index.ts b/backend/src/api/tests/index.ts index afc9c8ba..c8a0c092 100644 --- a/backend/src/api/tests/index.ts +++ b/backend/src/api/tests/index.ts @@ -41,8 +41,9 @@ export const listTests = async (req: Request, res: Response): Promise => { const { uuid } = req.params; let resp = await AppDataSource.getRepository(ApiEndpointTest) - .createQueryBuilder() + .createQueryBuilder("test") .select() + .leftJoinAndSelect("test.apiEndpoint", "apiEndpoint") .getMany(); await ApiResponseHandler.success(res, resp); diff --git a/backend/src/index.ts b/backend/src/index.ts index 8b5c785d..66f0aa4c 100644 --- a/backend/src/index.ts +++ b/backend/src/index.ts @@ -105,8 +105,8 @@ app.post("/api/v1/update_connection", update_connection); app.post("/api/v1/test/run", runTestHandler); app.post("/api/v1/test/save", saveTest); -app.get("/api/v1/tests/list", listTests); -app.get("/api/v1/tests/list/:uuid", getTest); +app.get("/api/v1/test/list", listTests); +app.get("/api/v1/test/list/:uuid", getTest); const main = async () => { try { diff --git a/common/src/testing/types.ts b/common/src/testing/types.ts index ce35c3e6..01a73b6d 100644 --- a/common/src/testing/types.ts +++ b/common/src/testing/types.ts @@ -1,3 +1,4 @@ +import { ApiEndpoint } from "../types"; import { RestMethod } from "../enums"; import { APIKeyAuthAddTo, AuthType, RequestBodyType } from "./enums"; @@ -66,3 +67,11 @@ export interface Test { tags: string[]; requests: Request[]; } + +export interface TestDetailed { + uuid: string; + name: string; + tags: string[]; + requests: Request[]; + apiEndpoint: ApiEndpoint; +} diff --git a/frontend/src/api/tests/index.ts b/frontend/src/api/tests/index.ts index 9c82d687..aa42e4b2 100644 --- a/frontend/src/api/tests/index.ts +++ b/frontend/src/api/tests/index.ts @@ -1,6 +1,6 @@ import axios from "axios"; import { getAPIURL } from "~/constants"; -import { Test } from "@common/testing/types"; +import { TestDetailed, Test } from "@common/testing/types"; export const runTest = async (test: Test) => { const resp = await axios.post(`${getAPIURL()}/test/run`, { @@ -8,6 +8,10 @@ export const runTest = async (test: Test) => { }); }; +export const listTests = async () => { + const resp = await axios.get>(`${getAPIURL()}/test/list`); + return resp; +}; export const saveTest = async (test: Test, endpoint_uuid: string) => { const resp = await axios.post(`${getAPIURL()}/test/save`, { diff --git a/frontend/src/components/TestList/List.tsx b/frontend/src/components/TestList/List.tsx new file mode 100644 index 00000000..af87cd77 --- /dev/null +++ b/frontend/src/components/TestList/List.tsx @@ -0,0 +1,88 @@ +import React from "react"; +import { useRouter } from "next/router"; +import { Badge, Text, useColorMode, VStack } from "@chakra-ui/react"; +import DataTable, { TableColumn } from "react-data-table-component"; +import { getCustomStyles, rowStyles } from "components/utils/TableUtils"; +import { TestDetailed } from "@common/testing/types"; +import { RISK_TO_COLOR } from "~/constants"; + +interface TestListProps { + tests: TestDetailed[]; +} + +const TestList: React.FC = React.memo(({ tests }) => { + const router = useRouter(); + const colorMode = useColorMode(); + const onRowClicked = ( + row: TestDetailed, + e: React.MouseEvent + ) => { + router.push(`/test/${row.uuid}`); + }; + const columns: TableColumn[] = [ + { + name: "Name", + sortable: true, + selector: (row: TestDetailed) => row.name || "", + id: "name", + }, + { + name: "Tags", + sortable: true, + selector: (row: TestDetailed) => row.tags?.join(", ") || "", + cell: (row: TestDetailed) => ( + + {row.tags?.map((tag, idx) => ( + {tag} + ))} + + ), + id: "hosts", + }, + { + name: "Requests", + sortable: true, + selector: (row: TestDetailed) => row.tags?.join(", ") || "", + cell: (row: TestDetailed) => ( + + {row.requests?.map((req, idx) => ( + {req.url} + ))} + + ), + id: "requests", + }, + { + name: "Endpoint Risk", + sortable: true, + selector: (row: TestDetailed) => row.apiEndpoint.riskScore || "", + cell: (row: TestDetailed) => ( + + {row.apiEndpoint.riskScore} + + ), + id: "endpointRisk", + }, + ]; + return ( + + ); +}); + +export default TestList; diff --git a/frontend/src/components/TestList/index.tsx b/frontend/src/components/TestList/index.tsx new file mode 100644 index 00000000..f70745e3 --- /dev/null +++ b/frontend/src/components/TestList/index.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { Box, VStack } from "@chakra-ui/react"; +import List from "./List"; +import { Test, TestDetailed } from "@common/testing/types"; + +const ListTests: React.FC<{ tests: Array }> = React.memo( + ({ tests }) => ( + + + + + + ) +); + +export default ListTests; diff --git a/frontend/src/pages/tests.tsx b/frontend/src/pages/tests.tsx index b5c868dc..8439d137 100644 --- a/frontend/src/pages/tests.tsx +++ b/frontend/src/pages/tests.tsx @@ -1,21 +1,31 @@ import { Heading, VStack } from "@chakra-ui/react"; import { SideNavLinkDestination } from "components/Sidebar/NavLinkUtils"; +import { GetServerSideProps } from "next"; +import { listTests } from "~/api/tests"; import { SidebarLayoutShell } from "~/components/SidebarLayoutShell"; +import ListTests from "~/components/TestList"; import { ContentContainer } from "~/components/utils/ContentContainer"; +import superjson from "superjson"; -const Tests = () => ( - +export const getServerSideProps: GetServerSideProps = async (context) => { + const tests = await listTests(); + return { + props: { + tests: superjson.stringify(tests.data), + }, + }; +}; + +const Tests = ({ tests }) => ( + Tests + ); - export default Tests;