From 81aa3811b58f4d619c9d669ae65a0ddb6ed63fc8 Mon Sep 17 00:00:00 2001 From: harishshg Date: Thu, 11 May 2023 14:24:15 +0530 Subject: [PATCH 1/8] added name column to the runtime BOM --- .../features/vulnerabilities/pages/RuntimeBom.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx index 1186e12cfa..ed2cebedf7 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx @@ -180,9 +180,16 @@ const RuntimeBom = () => { ); }, header: () => 'Type', - minSize: 200, - size: 300, - maxSize: 500, + minSize: 50, + size: 100, + maxSize: 200, + }), + columnHelper.accessor('node_name', { + cell: (cell) => cell.getValue(), + header: () => 'Name', + minSize: 100, + size: 200, + maxSize: 300, }), columnHelper.accessor('node_id', { enableSorting: false, @@ -204,7 +211,7 @@ const RuntimeBom = () => { ); }, - header: () => 'Name', + header: () => 'Node', minSize: 200, size: 300, maxSize: 500, From c7b6039bfbd9dd6dbbd258306ad395baa818f1f9 Mon Sep 17 00:00:00 2001 From: harishshg Date: Thu, 11 May 2023 17:27:45 +0530 Subject: [PATCH 2/8] added sorting logic to severity --- .../vulnerabilities/api/sbomApiLoader.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/api/sbomApiLoader.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/api/sbomApiLoader.tsx index dd49c4d85a..33a2fa9a8f 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/api/sbomApiLoader.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/api/sbomApiLoader.tsx @@ -132,6 +132,22 @@ export const SbomModal = ({ /> ); }, + sortingFn: (rowA, rowB) => { + const severityA = rowA.original.severity?.toLowerCase(); + const severityB = rowB.original.severity?.toLowerCase(); + if (severityA === severityB) return 0; + if (severityA === 'critical') return -1; + if (severityB === 'critical') return 1; + if (severityA === 'high') return -1; + if (severityB === 'high') return 1; + if (severityA === 'medium') return -1; + if (severityB === 'medium') return 1; + if (severityA === 'low') return -1; + if (severityB === 'low') return 1; + if (severityA === 'unknown') return -1; + if (severityB === 'unknown') return 1; + return 0; + }, header: () => 'Severity', minSize: 50, size: 70, From f16e6900dd641ce00f0000a084496fcfebbcdc29 Mon Sep 17 00:00:00 2001 From: harishshg Date: Thu, 11 May 2023 17:32:43 +0530 Subject: [PATCH 3/8] removed node id and replace with node name --- .../src/features/vulnerabilities/pages/RuntimeBom.tsx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx index ed2cebedf7..94ca7a4098 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx @@ -185,13 +185,6 @@ const RuntimeBom = () => { maxSize: 200, }), columnHelper.accessor('node_name', { - cell: (cell) => cell.getValue(), - header: () => 'Name', - minSize: 100, - size: 200, - maxSize: 300, - }), - columnHelper.accessor('node_id', { enableSorting: false, cell: (info) => { return ( From 8eaccc0a885181c860438bee723dcf0cb84b9c42 Mon Sep 17 00:00:00 2001 From: harishshg Date: Thu, 11 May 2023 17:53:49 +0530 Subject: [PATCH 4/8] removed capitalize --- .../dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx index 94ca7a4098..4f80ae594a 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/RuntimeBom.tsx @@ -199,7 +199,7 @@ const RuntimeBom = () => { }} href="#" > - {info.getValue()} + {info.getValue()} ); From 7564b6336959922cb159a20c8e7bba14c346e9c9 Mon Sep 17 00:00:00 2001 From: harishshg Date: Thu, 11 May 2023 18:31:22 +0530 Subject: [PATCH 5/8] v2-ui-enhancements --- .../pages/VulnerabilityScans.tsx | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/VulnerabilityScans.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/VulnerabilityScans.tsx index d48b1ee66c..a294f39aa0 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/VulnerabilityScans.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/pages/VulnerabilityScans.tsx @@ -16,6 +16,7 @@ import { HiDotsVertical, HiDownload, HiOutlineExclamationCircle, + HiViewGrid, } from 'react-icons/hi'; import { ActionFunctionArgs, @@ -74,6 +75,7 @@ import { VulnerabilityIcon } from '@/components/sideNavigation/icons/Vulnerabili import { SEVERITY_COLORS } from '@/constants/charts'; import { IconMapForNodeType } from '@/features/onboard/components/IconMapForNodeType'; import { SuccessModalContent } from '@/features/settings/components/SuccessModalContent'; +import { SbomModal } from '@/features/vulnerabilities/api/sbomApiLoader'; import { ScanTypeEnum } from '@/types/common'; import { ApiError, @@ -658,6 +660,7 @@ const ActionDropdown = ({ setShowDeleteDialog, setScanIdToDelete, setNodeIdToDelete, + setSelectedNode, }: { icon: React.ReactNode; scanId: string; @@ -667,6 +670,12 @@ const ActionDropdown = ({ setShowDeleteDialog: React.Dispatch>; setScanIdToDelete: React.Dispatch>; setNodeIdToDelete: React.Dispatch>; + setSelectedNode: React.Dispatch< + React.SetStateAction<{ + nodeName: string; + scanId: string; + } | null> + >; }) => { const fetcher = useFetcher(); const [open, setOpen] = useState(false); @@ -731,6 +740,26 @@ const ActionDropdown = ({ Download SBOM + { + e.preventDefault(); + if (!isScanComplete(scanStatus)) return; + setSelectedNode({ + scanId, + nodeName: nodeId, + }); + }} + > + + + View SBOM + + { @@ -769,7 +798,10 @@ const VulnerabilityScans = () => { const [showDeleteDialog, setShowDeleteDialog] = useState(false); const [scanIdToDelete, setScanIdToDelete] = useState(''); const [nodeIdToDelete, setNodeIdToDelete] = useState(''); - + const [selectedNode, setSelectedNode] = useState<{ + nodeName: string; + scanId: string; + } | null>(null); const columnHelper = createColumnHelper(); const columns = useMemo(() => { @@ -985,6 +1017,7 @@ const VulnerabilityScans = () => { setScanIdToDelete={setScanIdToDelete} setNodeIdToDelete={setNodeIdToDelete} setShowDeleteDialog={setShowDeleteDialog} + setSelectedNode={setSelectedNode} /> ), header: () => '', @@ -1374,6 +1407,15 @@ const VulnerabilityScans = () => { + {selectedNode ? ( + { + setSelectedNode(null); + }} + /> + ) : null} ); }; From 1ef1ce24557018e2ebf099193003dff65f9379df Mon Sep 17 00:00:00 2001 From: harishshg Date: Fri, 12 May 2023 11:19:50 +0530 Subject: [PATCH 6/8] added delete and download in posture scans --- .../src/features/postures/pages/Accounts.tsx | 397 +++++++++++++++++- 1 file changed, 386 insertions(+), 11 deletions(-) diff --git a/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx b/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx index 9a6141cfe1..604143fbb9 100644 --- a/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx @@ -1,16 +1,26 @@ import cx from 'classnames'; -import { Suspense, useMemo, useRef, useState } from 'react'; +import { Suspense, useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { IconContext } from 'react-icons'; import { FaPlay, FaPlus } from 'react-icons/fa'; import { FiFilter } from 'react-icons/fi'; -import { HiChevronRight } from 'react-icons/hi'; import { + HiArchive, + HiChevronRight, + HiDotsVertical, + HiDownload, + HiOutlineExclamationCircle, +} from 'react-icons/hi'; +import { + ActionFunctionArgs, Form, generatePath, LoaderFunctionArgs, + useFetcher, useLoaderData, useParams, useSearchParams, } from 'react-router-dom'; +import { toast } from 'sonner'; import { Badge, Breadcrumb, @@ -18,30 +28,56 @@ import { Button, Checkbox, createColumnHelper, + Dropdown, + DropdownItem, getRowSelectionColumn, IconButton, + Modal, Popover, RowSelectionState, Table, TableSkeleton, } from 'ui-components'; -import { getCloudNodesApiClient } from '@/api/api'; -import { ApiDocsBadRequestResponse, ModelCloudNodeAccountInfo } from '@/api/generated'; +import { + getCloudNodesApiClient, + getReportsApiClient, + getScanResultsApiClient, +} from '@/api/api'; +import { + ApiDocsBadRequestResponse, + ModelCloudNodeAccountInfo, + ModelGenerateReportReqDurationEnum, + ModelGenerateReportReqReportTypeEnum, + UtilsReportFiltersNodeTypeEnum, + UtilsReportFiltersScanTypeEnum, +} from '@/api/generated'; import { ConfigureScanModal } from '@/components/ConfigureScanModal'; import { DFLink } from '@/components/DFLink'; import { FilterHeader } from '@/components/forms/FilterHeader'; import { ACCOUNT_CONNECTOR } from '@/components/hosts-connector/NoConnectors'; import { CLOUDS } from '@/components/scan-configure-forms/ComplianceScanConfigureForm'; import { providersToNameMapping } from '@/features/postures/pages/Posture'; +import { SuccessModalContent } from '@/features/settings/components/SuccessModalContent'; import { ComplianceScanNodeTypeEnum, ScanTypeEnum } from '@/types/common'; -import { ApiError, makeRequest } from '@/utils/api'; +import { + ApiError, + apiWrapper, + makeRequest, + retryUntilResponseHasValue, +} from '@/utils/api'; +import { download } from '@/utils/download'; import { typedDefer, TypedDeferredData } from '@/utils/router'; +import { isScanComplete } from '@/utils/scan'; import { DFAwait } from '@/utils/suspense'; import { getPageFromSearchParams } from '@/utils/table'; import { usePageNavigation } from '@/utils/usePageNavigation'; -// TODO: remove this once we have correct type from api +enum ActionEnumType { + DELETE = 'delete', + DOWNLOAD = 'download', +} + export const getNodeTypeByProviderName = ( providerName: string, ): ComplianceScanNodeTypeEnum | undefined => { @@ -82,6 +118,139 @@ const getActiveStatus = (searchParams: URLSearchParams) => { return searchParams.getAll('active'); }; +const action = async ({ + request, +}: ActionFunctionArgs): Promise<{ success?: boolean } | null> => { + const formData = await request.formData(); + const actionType = formData.get('actionType'); + const scanId = formData.get('scanId'); + const scanType = formData.get('scanType'); + if (!actionType) { + throw new Error('Invalid action'); + } + + if (actionType === ActionEnumType.DELETE) { + if (!scanId) { + throw new Error('Invalid action'); + } + const result = await makeRequest({ + apiFunction: getScanResultsApiClient().deleteScanResultsForScanID, + apiArgs: [ + { + scanId: scanId.toString(), + scanType: scanType as ScanTypeEnum, + }, + ], + errorHandler: async (r) => { + const error = new ApiError<{ + message?: string; + }>({}); + if (r.status === 400 || r.status === 409) { + const modelResponse: ApiDocsBadRequestResponse = await r.json(); + return error.set({ + message: modelResponse.message ?? '', + }); + } + }, + }); + if (ApiError.isApiError(result)) { + if (result.value()?.message !== undefined) { + const message = + result.value()?.message ?? 'Something went wrong, please try again'; + toast.error(message); + } + } + + return { + success: true, + }; + } else if (actionType === ActionEnumType.DOWNLOAD) { + const nodeType = formData.get('nodeType') as UtilsReportFiltersNodeTypeEnum; + if (!nodeType) { + throw new Error('Node Type is required'); + } + const getReportIdApi = apiWrapper({ + fn: getReportsApiClient().generateReport, + }); + + const getReportIdApiResponse = await getReportIdApi({ + modelGenerateReportReq: { + duration: ModelGenerateReportReqDurationEnum.NUMBER_0, + filters: { + node_type: nodeType, + scan_type: scanType as UtilsReportFiltersScanTypeEnum, + }, + report_type: ModelGenerateReportReqReportTypeEnum.Xlsx, + }, + }); + + if (!getReportIdApiResponse.ok) { + if (getReportIdApiResponse.error.response.status === 400) { + const modelResponse: ApiDocsBadRequestResponse = + await getReportIdApiResponse.error.response.json(); + const error = modelResponse.error_fields?.message; + if (error) { + toast.error(error); + return null; + } else { + toast.error('Something went wrong, please try again'); + return null; + } + } + throw getReportIdApiResponse.error; + } + + const reportId = getReportIdApiResponse.value.report_id; + if (!reportId) { + toast.error('Somethings went wrong, please try again'); + console.error('Report id is missing in api response'); + return null; + } + const getReportApi = apiWrapper({ + fn: getReportsApiClient().getReport, + }); + + const reportResponse = await retryUntilResponseHasValue( + getReportApi, + [{ reportId }], + async (response) => { + if (!response.ok) { + if (response.error.response.status === 400) { + const modelResponse: ApiDocsBadRequestResponse = + await response.error.response.json(); + const error = modelResponse.error_fields?.message; + if (error) { + toast.error(error); + return true; + } + } + toast.error('Something went wrong, please try again'); + return true; + } else { + const url = response.value.url; + if (!url) { + toast.success( + 'Download in progress, it may take some time however you can always find it on Integrations > Report Downloads', + ); + } + return !!url; + } + }, + ); + + if (reportResponse.ok) { + const url = reportResponse.value.url; + if (url) { + download(url); + } else { + toast.error('Something went wrong, please try again'); + } + } + + return null; + } + return null; +}; export async function getAccounts( nodeType: string, searchParams: URLSearchParams, @@ -155,6 +324,172 @@ const loader = async ({ }); }; +const DeleteConfirmationModal = ({ + showDialog, + scanId, + scanType, + setShowDialog, +}: { + showDialog: boolean; + scanId: string; + setShowDialog: React.Dispatch>; + scanType?: ScanTypeEnum; +}) => { + const fetcher = useFetcher(); + + const onDeleteAction = useCallback( + (actionType: string) => { + const formData = new FormData(); + formData.append('actionType', actionType); + formData.append('scanId', scanId); + formData.append('scanType', scanType ?? ''); + fetcher.submit(formData, { + method: 'post', + }); + }, + [scanId, scanType, fetcher], + ); + + return ( + setShowDialog(false)}> + {!fetcher.data?.success ? ( +
+ + + +

+ Selected scan will be deleted. +
+ Are you sure you want to delete? +

+
+ + +
+
+ ) : ( + + )} +
+ ); +}; + +const ActionDropdown = ({ + icon, + scanId, + scanStatus, + nodeType, + scanType, + setShowDeleteDialog, + setScanIdToDelete, + setScanTypeToDelete, +}: { + icon: React.ReactNode; + scanId?: string; + nodeType?: string; + scanType: ScanTypeEnum; + scanStatus: string; + setShowDeleteDialog: React.Dispatch>; + setScanIdToDelete: React.Dispatch>; + setScanTypeToDelete: React.Dispatch>; +}) => { + const fetcher = useFetcher(); + const [open, setOpen] = useState(false); + + const onDownloadAction = useCallback( + (actionType: ActionEnumType) => { + if (!scanId || !nodeType) return; + const formData = new FormData(); + formData.append('actionType', actionType); + formData.append('scanId', scanId); + formData.append('nodeType', nodeType); + formData.append('scanType', scanType ?? ''); + fetcher.submit(formData, { + method: 'post', + }); + }, + [scanId, fetcher], + ); + + useEffect(() => { + if (fetcher.state === 'idle') setOpen(false); + }, [fetcher]); + + return ( + + { + if (!isScanComplete(scanStatus)) return; + e.preventDefault(); + onDownloadAction(ActionEnumType.DOWNLOAD); + }} + > + + + Download Report + + + { + if (!scanId || !nodeType) return; + setScanIdToDelete(scanId); + setScanTypeToDelete(scanType); + setShowDeleteDialog(true); + }} + > + + + + + Delete + + + + } + > + + + ); +}; + const PostureTable = ({ data }: { data: LoaderDataType['data'] }) => { const [rowSelectionState, setRowSelectionState] = useState({}); const [searchParams, setSearchParams] = useSearchParams(); @@ -163,7 +498,15 @@ const PostureTable = ({ data }: { data: LoaderDataType['data'] }) => { typeof ScanTypeEnum.ComplianceScan | typeof ScanTypeEnum.CloudComplianceScan >(); const [scanNodeIds, setScanNodeIds] = useState(); + const accounts = data?.accounts ?? []; + const totalRows = data?.totalRows ?? 0; + const currentPage = data?.currentPage ?? 0; + const cloudProvider = accounts[0]?.cloud_provider ?? ''; + const [showDeleteDialog, setShowDeleteDialog] = useState(false); + const [scanIdToDelete, setScanIdToDelete] = useState(''); + const [scanTypeToDelete, setScanTypeToDelete] = useState(); + const nodeType = getNodeTypeByProviderName(cloudProvider); const columns = useMemo( () => [ getRowSelectionColumn(columnHelper, { @@ -301,15 +644,38 @@ const PostureTable = ({ data }: { data: LoaderDataType['data'] }) => { size: 100, maxSize: 120, }), + columnHelper.display({ + id: 'actions', + enableSorting: false, + cell: (cell) => { + const scanType = CLOUDS.includes( + cell.row.original.cloud_provider as ComplianceScanNodeTypeEnum, + ) + ? ScanTypeEnum.CloudComplianceScan + : ScanTypeEnum.ComplianceScan; + return ( + } + scanId={cell.row.original.last_scan_id} + nodeType={nodeType} + scanType={scanType} + scanStatus={cell.row.original.last_scan_status || ''} + setScanIdToDelete={setScanIdToDelete} + setScanTypeToDelete={setScanTypeToDelete} + setShowDeleteDialog={setShowDeleteDialog} + /> + ); + }, + header: () => '', + minSize: 50, + size: 50, + maxSize: 50, + enableResizing: false, + }), ], [rowSelectionState, searchParams, data], ); - const accounts = data?.accounts ?? []; - const totalRows = data?.totalRows ?? 0; - const currentPage = data?.currentPage ?? 0; - const cloudProvider = accounts[0]?.cloud_provider ?? ''; - const nodeType = getNodeTypeByProviderName(cloudProvider); return ( <>
@@ -363,6 +729,14 @@ const PostureTable = ({ data }: { data: LoaderDataType['data'] }) => { )} + {showDeleteDialog && ( + + )} { export const module = { loader, + action, element: , }; From a66a9f05207e4a85ab6c2bf1e87a0149598bf10c Mon Sep 17 00:00:00 2001 From: harishshg Date: Fri, 12 May 2023 16:27:58 +0530 Subject: [PATCH 7/8] added sorting shifted start scan action to action dropdown --- .../src/features/postures/pages/Accounts.tsx | 80 +++++++++++-------- 1 file changed, 46 insertions(+), 34 deletions(-) diff --git a/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx b/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx index 604143fbb9..c3be063cec 100644 --- a/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx @@ -395,18 +395,32 @@ const ActionDropdown = ({ scanStatus, nodeType, scanType, + nodeId, + cloudProvider, setShowDeleteDialog, setScanIdToDelete, setScanTypeToDelete, + setSelectedScanType, + setScanNodeIds, }: { icon: React.ReactNode; scanId?: string; nodeType?: string; scanType: ScanTypeEnum; scanStatus: string; + nodeId?: string; + cloudProvider?: string; setShowDeleteDialog: React.Dispatch>; setScanIdToDelete: React.Dispatch>; setScanTypeToDelete: React.Dispatch>; + setSelectedScanType: React.Dispatch< + React.SetStateAction< + | typeof ScanTypeEnum.ComplianceScan + | typeof ScanTypeEnum.CloudComplianceScan + | undefined + > + >; + setScanNodeIds: React.Dispatch>; }) => { const fetcher = useFetcher(); const [open, setOpen] = useState(false); @@ -438,6 +452,30 @@ const ActionDropdown = ({ onOpenChange={setOpen} content={ <> + { + if (!nodeId) { + throw new Error('Node id is required to start scan'); + } + const scanType = CLOUDS.includes( + cloudProvider as ComplianceScanNodeTypeEnum, + ) + ? ScanTypeEnum.CloudComplianceScan + : ScanTypeEnum.ComplianceScan; + setSelectedScanType(scanType); + setScanNodeIds([nodeId]); + }} + > + + + + + Start Scan + + - Download Report + Download Latest Report - Delete + Delete Latest Scan @@ -614,36 +652,6 @@ const PostureTable = ({ data }: { data: LoaderDataType['data'] }) => { size: 70, maxSize: 80, }), - columnHelper.display({ - id: 'startScan', - enableSorting: false, - cell: (info) => ( - - ), - header: () => 'Start action', - minSize: 80, - size: 100, - maxSize: 120, - }), columnHelper.display({ id: 'actions', enableSorting: false, @@ -657,12 +665,16 @@ const PostureTable = ({ data }: { data: LoaderDataType['data'] }) => { } scanId={cell.row.original.last_scan_id} + nodeId={cell.row.original.node_id} nodeType={nodeType} scanType={scanType} + cloudProvider={cell.row.original.cloud_provider} scanStatus={cell.row.original.last_scan_status || ''} setScanIdToDelete={setScanIdToDelete} setScanTypeToDelete={setScanTypeToDelete} setShowDeleteDialog={setShowDeleteDialog} + setSelectedScanType={setSelectedScanType} + setScanNodeIds={setScanNodeIds} /> ); }, @@ -742,12 +754,12 @@ const PostureTable = ({ data }: { data: LoaderDataType['data'] }) => { data={accounts ?? []} columns={columns} enableRowSelection - enablePagination totalRows={totalRows} - pageSize={30} rowSelectionState={rowSelectionState} onRowSelectionChange={setRowSelectionState} getRowId={(row) => row.node_id ?? ''} + enableColumnResizing + enableSorting /> From b9b48305d7c5f60fc7e2da6d569204e6f16aca91 Mon Sep 17 00:00:00 2001 From: harishshg Date: Fri, 12 May 2023 17:00:38 +0530 Subject: [PATCH 8/8] optimised sorting logic --- .../src/features/postures/pages/Accounts.tsx | 1 - .../vulnerabilities/api/sbomApiLoader.tsx | 25 ++++++++----------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx b/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx index c3be063cec..f127df4c62 100644 --- a/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/postures/pages/Accounts.tsx @@ -754,7 +754,6 @@ const PostureTable = ({ data }: { data: LoaderDataType['data'] }) => { data={accounts ?? []} columns={columns} enableRowSelection - totalRows={totalRows} rowSelectionState={rowSelectionState} onRowSelectionChange={setRowSelectionState} getRowId={(row) => row.node_id ?? ''} diff --git a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/api/sbomApiLoader.tsx b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/api/sbomApiLoader.tsx index 33a2fa9a8f..e813ff660c 100644 --- a/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/api/sbomApiLoader.tsx +++ b/deepfence_frontend/apps/dashboard/src/features/vulnerabilities/api/sbomApiLoader.tsx @@ -133,20 +133,17 @@ export const SbomModal = ({ ); }, sortingFn: (rowA, rowB) => { - const severityA = rowA.original.severity?.toLowerCase(); - const severityB = rowB.original.severity?.toLowerCase(); - if (severityA === severityB) return 0; - if (severityA === 'critical') return -1; - if (severityB === 'critical') return 1; - if (severityA === 'high') return -1; - if (severityB === 'high') return 1; - if (severityA === 'medium') return -1; - if (severityB === 'medium') return 1; - if (severityA === 'low') return -1; - if (severityB === 'low') return 1; - if (severityA === 'unknown') return -1; - if (severityB === 'unknown') return 1; - return 0; + const severityA = rowA.original.severity?.toLowerCase() || 'default'; + const severityB = rowB.original.severity?.toLowerCase() || 'default'; + const severityMap: { [key: string]: number } = { + critical: 4, + high: 3, + medium: 2, + low: 1, + unknown: 0, + default: 0, + }; + return severityMap[severityA] - severityMap[severityB]; }, header: () => 'Severity', minSize: 50,