From 7da71cf8b9b0604340225e9790900c87b2b83d4f Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Tue, 3 May 2022 18:06:45 +0300 Subject: [PATCH 1/8] add pagination --- .../resource_findings_container.tsx | 27 +++++++++++++---- .../resource_findings_table.tsx | 30 ++++++++++++++++--- .../use_resource_findings.ts | 24 ++++++++++++--- .../public/pages/findings/utils.ts | 17 +++++++++++ 4 files changed, 84 insertions(+), 14 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx index a48926b3653aa..ef233d828c1f8 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx @@ -15,16 +15,18 @@ import * as TEST_SUBJECTS from '../../test_subjects'; import { PageWrapper, PageTitle, PageTitleText } from '../../layout/findings_layout'; import { useCspBreadcrumbs } from '../../../../common/navigation/use_csp_breadcrumbs'; import { findingsNavigation } from '../../../../common/navigation/constants'; -import { useResourceFindings } from './use_resource_findings'; +import { ResourceFindingsQuery, useResourceFindings } from './use_resource_findings'; import { useUrlQuery } from '../../../../common/hooks/use_url_query'; import type { FindingsBaseURLQuery } from '../../types'; -import { getBaseQuery } from '../../utils'; +import { getBaseQuery, getEuiPaginationFromEsSearchSource } from '../../utils'; import { ResourceFindingsTable } from './resource_findings_table'; import { FindingsSearchBar } from '../../layout/findings_search_bar'; -const getDefaultQuery = (): FindingsBaseURLQuery => ({ +export const getDefaultQuery = (): FindingsBaseURLQuery & ResourceFindingsQuery => ({ query: { language: 'kuery', query: '' }, filters: [], + from: 0, + size: 10, }); const BackToResourcesButton = () => { @@ -47,8 +49,14 @@ export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { const { urlQuery, setUrlQuery } = useUrlQuery(getDefaultQuery); const resourceFindings = useResourceFindings({ - ...getBaseQuery({ dataView, filters: urlQuery.filters, query: urlQuery.query }), + ...getBaseQuery({ + dataView, + filters: urlQuery.filters, + query: urlQuery.query, + }), resourceId: params.resourceId, + size: urlQuery.size, + from: urlQuery.from, }); return ( @@ -58,7 +66,7 @@ export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { setQuery={setUrlQuery} query={urlQuery.query} filters={urlQuery.filters} - loading={resourceFindings.isLoading} + loading={resourceFindings.isFetching} /> @@ -77,9 +85,16 @@ export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { + setUrlQuery({ from: pagination?.index, size: pagination?.size }) + } /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx index ec04d05109cdd..89e1cc3cae5b3 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx @@ -4,18 +4,38 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React from 'react'; -import { EuiEmptyPrompt, EuiBasicTable } from '@elastic/eui'; +import React, { useCallback } from 'react'; +import { + EuiEmptyPrompt, + EuiBasicTable, + type Criteria, + type EuiBasicTableProps, +} from '@elastic/eui'; import { extractErrorMessage } from '../../../../../common/utils/helpers'; import * as TEXT from '../../translations'; import type { ResourceFindingsResult } from './use_resource_findings'; import { getFindingsColumns } from '../../layout/findings_layout'; +import type { CspFinding } from '../../types'; -type FindingsGroupByResourceProps = ResourceFindingsResult; +interface Props extends ResourceFindingsResult { + pagination: EuiBasicTableProps['pagination']; + setPagination(pagination: Criteria['page']): void; +} const columns = getFindingsColumns(); -const ResourceFindingsTableComponent = ({ error, data, loading }: FindingsGroupByResourceProps) => { +const ResourceFindingsTableComponent = ({ + error, + data, + loading, + pagination, + setPagination, +}: Props) => { + const onTableChange = useCallback( + (params: Criteria) => setPagination(params.page), + [setPagination] + ); + if (!loading && !data?.page.length) return {TEXT.NO_FINDINGS}} />; @@ -25,6 +45,8 @@ const ResourceFindingsTableComponent = ({ error, data, loading }: FindingsGroupB error={error ? extractErrorMessage(error) : undefined} items={data?.page || []} columns={columns} + onChange={onTableChange} + pagination={pagination} /> ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts index 7123b80ef0228..e54088a4dafe1 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts @@ -12,10 +12,15 @@ import { useKibana } from '../../../../common/hooks/use_kibana'; import { showErrorToast } from '../../latest_findings/use_latest_findings'; import type { CspFinding, FindingsBaseEsQuery, FindingsQueryResult } from '../../types'; -interface UseResourceFindingsOptions extends FindingsBaseEsQuery { +interface UseResourceFindingsOptions extends FindingsBaseEsQuery, ResourceFindingsQuery { resourceId: string; } +export interface ResourceFindingsQuery { + from: number; + size: number; +} + export type ResourceFindingsResult = FindingsQueryResult< ReturnType['data'] | undefined, unknown @@ -25,8 +30,12 @@ export const getResourceFindingsQuery = ({ index, query, resourceId, + size, + from, }: UseResourceFindingsOptions): estypes.SearchRequest => ({ index, + size, + from, body: { query: { ...query, @@ -38,21 +47,28 @@ export const getResourceFindingsQuery = ({ }, }); -export const useResourceFindings = ({ index, query, resourceId }: UseResourceFindingsOptions) => { +export const useResourceFindings = ({ + index, + query, + resourceId, + size, + from, +}: UseResourceFindingsOptions) => { const { data, notifications: { toasts }, } = useKibana().services; return useQuery( - ['csp_resource_findings', { index, query, resourceId }], + ['csp_resource_findings', { index, query, resourceId, size, from }], () => lastValueFrom>( data.search.search({ - params: getResourceFindingsQuery({ index, query, resourceId }), + params: getResourceFindingsQuery({ index, query, resourceId, size, from }), }) ), { + keepPreviousData: true, select: ({ rawResponse: { hits } }) => ({ page: hits.hits.map((hit) => hit._source!), total: hits.total as number, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts index d3281a1a0dbc8..0be76644c17c8 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts @@ -7,6 +7,7 @@ import { buildEsQuery } from '@kbn/es-query'; import type { DataView } from '@kbn/data-plugin/common'; +import type { EuiBasicTableProps } from '@elastic/eui'; import type { FindingsBaseEsQuery, FindingsBaseURLQuery } from './types'; export const getBaseQuery = ({ @@ -20,3 +21,19 @@ export const getBaseQuery = ({ // will be accounted for before releasing the feature query: buildEsQuery(dataView, query, filters), }); + +export const getEuiPaginationFromEsSearchSource = ({ + from: pageIndex, + size: pageSize, + total, +}: { + total?: number | undefined; + size: number; + from: number; +}): EuiBasicTableProps['pagination'] => ({ + pageSize, + pageIndex: Math.ceil(pageIndex / pageSize), + totalItemCount: total || 0, + pageSizeOptions: [10, 25, 100], + showPerPageOptions: true, +}); From ca39595b829f434ac605f86bf0515e806ed4a68b Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Tue, 3 May 2022 18:45:40 +0300 Subject: [PATCH 2/8] clean --- .../latest_findings/latest_findings_table.tsx | 17 ++-------- .../resource_findings_container.tsx | 32 ++++++++----------- .../resource_findings_table.tsx | 18 ++++------- .../use_resource_findings.ts | 2 +- .../public/pages/findings/utils.ts | 2 +- 5 files changed, 25 insertions(+), 46 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx index 6a2bd1c129b50..b346d90e9257b 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx @@ -21,6 +21,7 @@ import type { CspFinding } from '../types'; import type { FindingsGroupByNoneQuery, CspFindingsResult } from './use_latest_findings'; import { FindingsRuleFlyout } from '../findings_flyout/findings_flyout'; import { getExpandColumn, getFindingsColumns } from '../layout/findings_layout'; +import { getEuiPaginationFromEs } from '../utils'; interface BaseFindingsTableProps extends FindingsGroupByNoneQuery { setQuery(query: Partial): void; @@ -49,7 +50,7 @@ const FindingsTableComponent = ({ const pagination = useMemo( () => - getEuiPaginationFromEsSearchSource({ + getEuiPaginationFromEs({ from, size, total: data?.total, @@ -100,20 +101,6 @@ const FindingsTableComponent = ({ ); }; -const getEuiPaginationFromEsSearchSource = ({ - from: pageIndex, - size: pageSize, - total, -}: Pick & { - total: number | undefined; -}): EuiBasicTableProps['pagination'] => ({ - pageSize, - pageIndex: Math.ceil(pageIndex / pageSize), - totalItemCount: total || 0, - pageSizeOptions: [10, 25, 100], - showPerPageOptions: true, -}); - const getEuiSortFromEsSearchSource = ( sort: FindingsGroupByNoneQuery['sort'] ): EuiBasicTableProps['sorting'] => { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx index ef233d828c1f8..5711511fa00ee 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx @@ -18,29 +18,27 @@ import { findingsNavigation } from '../../../../common/navigation/constants'; import { ResourceFindingsQuery, useResourceFindings } from './use_resource_findings'; import { useUrlQuery } from '../../../../common/hooks/use_url_query'; import type { FindingsBaseURLQuery } from '../../types'; -import { getBaseQuery, getEuiPaginationFromEsSearchSource } from '../../utils'; +import { getBaseQuery } from '../../utils'; import { ResourceFindingsTable } from './resource_findings_table'; import { FindingsSearchBar } from '../../layout/findings_search_bar'; -export const getDefaultQuery = (): FindingsBaseURLQuery & ResourceFindingsQuery => ({ +const getDefaultQuery = (): FindingsBaseURLQuery & ResourceFindingsQuery => ({ query: { language: 'kuery', query: '' }, filters: [], from: 0, size: 10, }); -const BackToResourcesButton = () => { - return ( - - - - - - ); -}; +const BackToResourcesButton = () => ( + + + + + +); export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { useCspBreadcrumbs([findingsNavigation.findings_default]); @@ -85,10 +83,8 @@ export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { ['pagination']; +interface Props extends ResourceFindingsResult, ResourceFindingsQuery { setPagination(pagination: Criteria['page']): void; } @@ -28,7 +23,8 @@ const ResourceFindingsTableComponent = ({ error, data, loading, - pagination, + size, + from, setPagination, }: Props) => { const onTableChange = useCallback( @@ -46,7 +42,7 @@ const ResourceFindingsTableComponent = ({ items={data?.page || []} columns={columns} onChange={onTableChange} - pagination={pagination} + pagination={getEuiPaginationFromEs({ size, from, total: data?.total })} /> ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts index e54088a4dafe1..635fa790d6207 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts @@ -26,7 +26,7 @@ export type ResourceFindingsResult = FindingsQueryResult< unknown >; -export const getResourceFindingsQuery = ({ +const getResourceFindingsQuery = ({ index, query, resourceId, diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts index 0be76644c17c8..690e84b77e37a 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts @@ -22,7 +22,7 @@ export const getBaseQuery = ({ query: buildEsQuery(dataView, query, filters), }); -export const getEuiPaginationFromEsSearchSource = ({ +export const getEuiPaginationFromEs = ({ from: pageIndex, size: pageSize, total, From 262d326538d1c209c0396a58401e34d22310bf22 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Tue, 3 May 2022 19:30:39 +0300 Subject: [PATCH 3/8] fix i18n --- .../public/pages/findings/layout/findings_layout.tsx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx index ee1a00abc4901..1374f98b45933 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/layout/findings_layout.tsx @@ -16,6 +16,7 @@ import { } from '@elastic/eui'; import { css } from '@emotion/react'; import moment from 'moment'; +import { i18n } from '@kbn/i18n'; import { CspEvaluationBadge } from '../../../components/csp_evaluation_badge'; import * as TEXT from '../translations'; import { CspFinding } from '../types'; @@ -52,8 +53,10 @@ export const getExpandColumn = ({ width: '40px', actions: [ { - name: 'Expand', - description: 'Expand', + name: i18n.translate('xpack.csp.expandColumnNameLabel', { defaultMessage: 'Expand' }), + description: i18n.translate('xpack.csp.expandColumnDescriptionLabel', { + defaultMessage: 'Expand', + }), type: 'icon', icon: 'expand', onClick, From c4749dc85a07170ccfc85c24bc618164d0c67577 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Wed, 4 May 2022 13:19:33 +0300 Subject: [PATCH 4/8] fix pagination --- .../latest_findings_container.tsx | 20 ++++-- .../latest_findings_table.test.tsx | 16 ++--- .../latest_findings/latest_findings_table.tsx | 63 ++++--------------- .../resource_findings_container.tsx | 13 ++-- .../resource_findings_table.tsx | 16 ++--- .../public/pages/findings/types.ts | 11 ++++ .../public/pages/findings/utils.ts | 26 +++++++- 7 files changed, 83 insertions(+), 82 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx index 78a1fd758b6ee..4c247d95599fe 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx @@ -15,10 +15,16 @@ import * as TEST_SUBJECTS from '../test_subjects'; import { useUrlQuery } from '../../../common/hooks/use_url_query'; import { useLatestFindings } from './use_latest_findings'; import type { FindingsGroupByNoneQuery } from './use_latest_findings'; -import type { FindingsBaseURLQuery } from '../types'; +import type { CspFinding, FindingsBaseURLQuery } from '../types'; import { useFindingsCounter } from '../use_findings_count'; import { FindingsDistributionBar } from '../layout/findings_distribution_bar'; -import { getBaseQuery } from '../utils'; +import { + getBaseQuery, + getEsPaginationFromEui, + getEsSortFromEui, + getEuiPaginationFromEs, + getEuiSortFromEs, +} from '../utils'; import { PageWrapper, PageTitle, PageTitleText } from '../layout/findings_layout'; import { FindingsGroupBySelector } from '../layout/findings_group_by_selector'; import { useCspBreadcrumbs } from '../../../common/navigation/use_csp_breadcrumbs'; @@ -75,11 +81,17 @@ export const LatestFindingsContainer = ({ dataView }: { dataView: DataView }) => /> setUrlQuery(getEsPaginationFromEui(page))} + sorting={getEuiSortFromEs(urlQuery.sort)} + setSorting={(sort) => setUrlQuery({ sort: getEsSortFromEui(sort) })} /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx index d01af2fa96e94..115facac7ca53 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx @@ -70,10 +70,10 @@ describe('', () => { loading: false, data: { page: [], total: 0 }, error: null, - sort: [], - from: 1, - size: 10, - setQuery: jest.fn, + sorting: {}, + pagination: { pageSize: 10, pageIndex: 1, totalItemCount: 0 }, + setSorting: jest.fn(), + setPagination: jest.fn(), }; render( @@ -93,10 +93,10 @@ describe('', () => { loading: false, data: { page: data, total: 10 }, error: null, - sort: [], - from: 0, - size: 10, - setQuery: jest.fn, + sorting: {}, + pagination: { pageSize: 10, pageIndex: 1, totalItemCount: 0 }, + setSorting: jest.fn(), + setPagination: jest.fn(), }; render( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx index b346d90e9257b..b58ad522bb839 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx @@ -5,38 +5,28 @@ * 2.0. */ import React, { useCallback, useMemo, useState } from 'react'; -import { - type Criteria, - EuiEmptyPrompt, - EuiBasicTable, - EuiBasicTableProps, - EuiBasicTableColumn, -} from '@elastic/eui'; -import { SortDirection } from '@kbn/data-plugin/common'; +import { type Criteria, EuiEmptyPrompt, EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; import { EuiTableActionsColumnType } from '@elastic/eui/src/components/basic_table/table_types'; import { extractErrorMessage } from '../../../../common/utils/helpers'; import * as TEST_SUBJECTS from '../test_subjects'; import * as TEXT from '../translations'; -import type { CspFinding } from '../types'; -import type { FindingsGroupByNoneQuery, CspFindingsResult } from './use_latest_findings'; +import type { CspFinding, Pagination, Sorting } from '../types'; +import type { CspFindingsResult } from './use_latest_findings'; import { FindingsRuleFlyout } from '../findings_flyout/findings_flyout'; import { getExpandColumn, getFindingsColumns } from '../layout/findings_layout'; -import { getEuiPaginationFromEs } from '../utils'; -interface BaseFindingsTableProps extends FindingsGroupByNoneQuery { - setQuery(query: Partial): void; -} +interface BaseFindingsTableProps extends Pagination, Sorting {} type FindingsTableProps = CspFindingsResult & BaseFindingsTableProps; const FindingsTableComponent = ({ - setQuery, - from, - size, - sort = [], error, data, loading, + pagination, + setPagination, + sorting, + setSorting, }: FindingsTableProps) => { const [selectedFinding, setSelectedFinding] = useState(); @@ -48,23 +38,12 @@ const FindingsTableComponent = ({ [] ); - const pagination = useMemo( - () => - getEuiPaginationFromEs({ - from, - size, - total: data?.total, - }), - [from, size, data] - ); - - const sorting = useMemo(() => getEuiSortFromEsSearchSource(sort), [sort]); - const onTableChange = useCallback( (params: Criteria) => { - setQuery(getEsSearchQueryFromEuiTableParams(params)); + setPagination(params.page!); + setSorting(params.sort!); }, - [setQuery] + [setPagination, setSorting] ); // Show "zero state" @@ -101,24 +80,4 @@ const FindingsTableComponent = ({ ); }; -const getEuiSortFromEsSearchSource = ( - sort: FindingsGroupByNoneQuery['sort'] -): EuiBasicTableProps['sorting'] => { - if (!sort.length) return; - - const entry = Object.entries(sort[0])?.[0]; - if (!entry) return; - - const [field, direction] = entry; - return { sort: { field: field as keyof CspFinding, direction: direction as SortDirection } }; -}; - -const getEsSearchQueryFromEuiTableParams = ({ - page, - sort, -}: Criteria): Partial => ({ - ...(!!page && { from: page.index * page.size, size: page.size }), - sort: sort ? [{ [sort.field]: SortDirection[sort.direction] }] : undefined, -}); - export const FindingsTable = React.memo(FindingsTableComponent); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx index 5711511fa00ee..4355e5013b90d 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx @@ -18,7 +18,7 @@ import { findingsNavigation } from '../../../../common/navigation/constants'; import { ResourceFindingsQuery, useResourceFindings } from './use_resource_findings'; import { useUrlQuery } from '../../../../common/hooks/use_url_query'; import type { FindingsBaseURLQuery } from '../../types'; -import { getBaseQuery } from '../../utils'; +import { getBaseQuery, getEsPaginationFromEui, getEuiPaginationFromEs } from '../../utils'; import { ResourceFindingsTable } from './resource_findings_table'; import { FindingsSearchBar } from '../../layout/findings_search_bar'; @@ -83,14 +83,15 @@ export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { - setUrlQuery({ from: pagination?.index, size: pagination?.size }) - } + pagination={getEuiPaginationFromEs({ + size: urlQuery.size, + from: urlQuery.from, + total: resourceFindings.data?.total, + })} + setPagination={(page) => setUrlQuery(getEsPaginationFromEui(page))} /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx index 107c1a5ef425a..1bb2fce4abdbb 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx @@ -8,14 +8,11 @@ import React, { useCallback } from 'react'; import { EuiEmptyPrompt, EuiBasicTable, type Criteria } from '@elastic/eui'; import { extractErrorMessage } from '../../../../../common/utils/helpers'; import * as TEXT from '../../translations'; -import type { ResourceFindingsResult, ResourceFindingsQuery } from './use_resource_findings'; +import type { ResourceFindingsResult } from './use_resource_findings'; import { getFindingsColumns } from '../../layout/findings_layout'; -import type { CspFinding } from '../../types'; -import { getEuiPaginationFromEs } from '../../utils'; +import type { CspFinding, Pagination } from '../../types'; -interface Props extends ResourceFindingsResult, ResourceFindingsQuery { - setPagination(pagination: Criteria['page']): void; -} +interface Props extends ResourceFindingsResult, Pagination {} const columns = getFindingsColumns(); @@ -23,12 +20,11 @@ const ResourceFindingsTableComponent = ({ error, data, loading, - size, - from, + pagination, setPagination, }: Props) => { const onTableChange = useCallback( - (params: Criteria) => setPagination(params.page), + (params: Criteria) => setPagination(params.page!), [setPagination] ); @@ -42,7 +38,7 @@ const ResourceFindingsTableComponent = ({ items={data?.page || []} columns={columns} onChange={onTableChange} - pagination={getEuiPaginationFromEs({ size, from, total: data?.total })} + pagination={pagination} /> ); }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts index 9fed484a88128..31ef2e4be0e46 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts @@ -4,11 +4,22 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ +import { Criteria, EuiBasicTableProps } from '@elastic/eui'; import type { BoolQuery, Filter, Query } from '@kbn/es-query'; import { UseQueryResult } from 'react-query'; export type FindingsGroupByKind = 'default' | 'resource'; +export interface Pagination { + pagination: EuiBasicTableProps['pagination']; + setPagination(pagination: NonNullable['page']>): void; +} + +export interface Sorting { + sorting: EuiBasicTableProps['sorting']; + setSorting(sorting: NonNullable['sort']>): void; +} + export interface FindingsBaseURLQuery { query: Query; filters: Filter[]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts index 690e84b77e37a..df320ae77de7a 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts @@ -6,8 +6,9 @@ */ import { buildEsQuery } from '@kbn/es-query'; -import type { DataView } from '@kbn/data-plugin/common'; -import type { EuiBasicTableProps } from '@elastic/eui'; +import type { DataView, EsQuerySortValue } from '@kbn/data-plugin/common'; +import { Criteria, EuiBasicTableProps } from '@elastic/eui'; +import { SortDirection } from '@kbn/data-plugin/common'; import type { FindingsBaseEsQuery, FindingsBaseURLQuery } from './types'; export const getBaseQuery = ({ @@ -22,6 +23,23 @@ export const getBaseQuery = ({ query: buildEsQuery(dataView, query, filters), }); +export const getEuiSortFromEs = ( + sort: EsQuerySortValue[] +): EuiBasicTableProps['sorting'] => { + if (!sort.length) return; + + const entry = Object.entries(sort[0])?.[0]; + if (!entry) return; + + const [field, direction] = entry; + return { sort: { field: field as keyof T, direction: direction as SortDirection } }; +}; + +export const getEsSortFromEui = ( + sort: Criteria['sort'] +): EsQuerySortValue[] | undefined => + sort ? [{ [sort.field]: sort.direction as SortDirection }] : undefined; + export const getEuiPaginationFromEs = ({ from: pageIndex, size: pageSize, @@ -37,3 +55,7 @@ export const getEuiPaginationFromEs = ({ pageSizeOptions: [10, 25, 100], showPerPageOptions: true, }); + +export const getEsPaginationFromEui = (page: Criteria['page']) => ({ + ...(!!page && { from: page.index * page.size, size: page.size }), +}); From 3e5ccd74aff137dc41a968ec3f7dfa4c274f3d00 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Wed, 4 May 2022 13:23:18 +0300 Subject: [PATCH 5/8] rename --- .../cloud_security_posture/public/pages/findings/utils.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts index df320ae77de7a..666a4b9544978 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts @@ -41,8 +41,8 @@ export const getEsSortFromEui = ( sort ? [{ [sort.field]: sort.direction as SortDirection }] : undefined; export const getEuiPaginationFromEs = ({ - from: pageIndex, size: pageSize, + from, total, }: { total?: number | undefined; @@ -50,7 +50,7 @@ export const getEuiPaginationFromEs = ({ from: number; }): EuiBasicTableProps['pagination'] => ({ pageSize, - pageIndex: Math.ceil(pageIndex / pageSize), + pageIndex: Math.ceil(from / pageSize), totalItemCount: total || 0, pageSizeOptions: [10, 25, 100], showPerPageOptions: true, From 1615bf0ceb512719e824e244d09e03cda332ca37 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Thu, 12 May 2022 15:10:11 +0300 Subject: [PATCH 6/8] fixes --- .../public/common/hooks/use_url_query.ts | 1 - .../latest_findings_container.tsx | 31 ++++++++++++------- .../latest_findings_table.test.tsx | 6 ++-- .../latest_findings/latest_findings_table.tsx | 31 ++++++++++--------- .../resource_findings_container.tsx | 6 +++- .../resource_findings_table.tsx | 20 ++++++------ .../public/pages/findings/types.ts | 11 ------- .../public/pages/findings/utils.ts | 12 +++---- 8 files changed, 57 insertions(+), 61 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_url_query.ts b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_url_query.ts index 2eea943f757cf..0573d77e6f9c8 100644 --- a/x-pack/plugins/cloud_security_posture/public/common/hooks/use_url_query.ts +++ b/x-pack/plugins/cloud_security_posture/public/common/hooks/use_url_query.ts @@ -33,7 +33,6 @@ export const useUrlQuery = (getDefaultQuery: () => T) => { // Set initial query useEffect(() => { - // TODO: condition should be if decoding failed if (search) return; replace({ search: encodeQuery(getDefaultQuery() as RisonObject) }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx index 4c247d95599fe..2e8d66cdb121c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx @@ -15,7 +15,7 @@ import * as TEST_SUBJECTS from '../test_subjects'; import { useUrlQuery } from '../../../common/hooks/use_url_query'; import { useLatestFindings } from './use_latest_findings'; import type { FindingsGroupByNoneQuery } from './use_latest_findings'; -import type { CspFinding, FindingsBaseURLQuery } from '../types'; +import type { FindingsBaseURLQuery } from '../types'; import { useFindingsCounter } from '../use_findings_count'; import { FindingsDistributionBar } from '../layout/findings_distribution_bar'; import { @@ -41,6 +41,7 @@ export const getDefaultQuery = (): FindingsBaseURLQuery & FindingsGroupByNoneQue export const LatestFindingsContainer = ({ dataView }: { dataView: DataView }) => { useCspBreadcrumbs([findingsNavigation.findings_default]); const { urlQuery, setUrlQuery } = useUrlQuery(getDefaultQuery); + const baseEsQuery = useMemo( () => getBaseQuery({ dataView, filters: urlQuery.filters, query: urlQuery.query }), [dataView, urlQuery.filters, urlQuery.query] @@ -61,16 +62,10 @@ export const LatestFindingsContainer = ({ dataView }: { dataView: DataView }) => setQuery={setUrlQuery} query={urlQuery.query} filters={urlQuery.filters} - loading={findingsGroupByNone.isLoading} + loading={findingsGroupByNone.isFetching} /> - - - } - /> - + setUrlQuery(getEsPaginationFromEui(page))} sorting={getEuiSortFromEs(urlQuery.sort)} - setSorting={(sort) => setUrlQuery({ sort: getEsSortFromEui(sort) })} + setTableOptions={({ page, sort }) => + setUrlQuery({ + ...(page && getEsPaginationFromEui(page)), + ...(sort && getEsSortFromEui(sort)), + }) + } /> ); }; + +const LatestFindingsPageTitle = () => ( + + } + /> + +); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx index 115facac7ca53..dac098b326f17 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx @@ -72,8 +72,7 @@ describe('', () => { error: null, sorting: {}, pagination: { pageSize: 10, pageIndex: 1, totalItemCount: 0 }, - setSorting: jest.fn(), - setPagination: jest.fn(), + setTableOptions: jest.fn(), }; render( @@ -95,8 +94,7 @@ describe('', () => { error: null, sorting: {}, pagination: { pageSize: 10, pageIndex: 1, totalItemCount: 0 }, - setSorting: jest.fn(), - setPagination: jest.fn(), + setTableOptions: jest.fn(), }; render( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx index b58ad522bb839..65fa91793aaab 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx @@ -4,18 +4,28 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback, useMemo, useState } from 'react'; -import { type Criteria, EuiEmptyPrompt, EuiBasicTable, EuiBasicTableColumn } from '@elastic/eui'; +import React, { useMemo, useState } from 'react'; +import { + type Criteria, + EuiEmptyPrompt, + EuiBasicTable, + EuiBasicTableColumn, + EuiBasicTableProps, +} from '@elastic/eui'; import { EuiTableActionsColumnType } from '@elastic/eui/src/components/basic_table/table_types'; import { extractErrorMessage } from '../../../../common/utils/helpers'; import * as TEST_SUBJECTS from '../test_subjects'; import * as TEXT from '../translations'; -import type { CspFinding, Pagination, Sorting } from '../types'; +import type { CspFinding } from '../types'; import type { CspFindingsResult } from './use_latest_findings'; import { FindingsRuleFlyout } from '../findings_flyout/findings_flyout'; import { getExpandColumn, getFindingsColumns } from '../layout/findings_layout'; -interface BaseFindingsTableProps extends Pagination, Sorting {} +interface BaseFindingsTableProps { + pagination: EuiBasicTableProps['pagination']; + sorting: EuiBasicTableProps['sorting']; + setTableOptions(options: Criteria): void; +} type FindingsTableProps = CspFindingsResult & BaseFindingsTableProps; @@ -24,9 +34,8 @@ const FindingsTableComponent = ({ data, loading, pagination, - setPagination, sorting, - setSorting, + setTableOptions, }: FindingsTableProps) => { const [selectedFinding, setSelectedFinding] = useState(); @@ -38,14 +47,6 @@ const FindingsTableComponent = ({ [] ); - const onTableChange = useCallback( - (params: Criteria) => { - setPagination(params.page!); - setSorting(params.sort!); - }, - [setPagination, setSorting] - ); - // Show "zero state" if (!loading && !data?.page.length) // TODO: use our own logo @@ -67,7 +68,7 @@ const FindingsTableComponent = ({ columns={columns} pagination={pagination} sorting={sorting} - onChange={onTableChange} + onChange={setTableOptions} hasActions /> {selectedFinding && ( diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx index 4355e5013b90d..5ad0afef02218 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx @@ -91,7 +91,11 @@ export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { from: urlQuery.from, total: resourceFindings.data?.total, })} - setPagination={(page) => setUrlQuery(getEsPaginationFromEui(page))} + setTableOptions={({ page }) => + setUrlQuery({ + ...(page && getEsPaginationFromEui(page)), + }) + } /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx index 1bb2fce4abdbb..b167f1e43d61c 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx @@ -4,15 +4,18 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import React, { useCallback } from 'react'; -import { EuiEmptyPrompt, EuiBasicTable, type Criteria } from '@elastic/eui'; +import React from 'react'; +import { EuiEmptyPrompt, EuiBasicTable, type Criteria, EuiBasicTableProps } from '@elastic/eui'; import { extractErrorMessage } from '../../../../../common/utils/helpers'; import * as TEXT from '../../translations'; import type { ResourceFindingsResult } from './use_resource_findings'; import { getFindingsColumns } from '../../layout/findings_layout'; -import type { CspFinding, Pagination } from '../../types'; +import type { CspFinding } from '../../types'; -interface Props extends ResourceFindingsResult, Pagination {} +interface Props extends ResourceFindingsResult { + pagination: EuiBasicTableProps['pagination']; + setTableOptions(options: Pick, 'page'>): void; +} const columns = getFindingsColumns(); @@ -21,13 +24,8 @@ const ResourceFindingsTableComponent = ({ data, loading, pagination, - setPagination, + setTableOptions, }: Props) => { - const onTableChange = useCallback( - (params: Criteria) => setPagination(params.page!), - [setPagination] - ); - if (!loading && !data?.page.length) return {TEXT.NO_FINDINGS}} />; @@ -37,7 +35,7 @@ const ResourceFindingsTableComponent = ({ error={error ? extractErrorMessage(error) : undefined} items={data?.page || []} columns={columns} - onChange={onTableChange} + onChange={setTableOptions} pagination={pagination} /> ); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts index 31ef2e4be0e46..9fed484a88128 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/types.ts @@ -4,22 +4,11 @@ * 2.0; you may not use this file except in compliance with the Elastic License * 2.0. */ -import { Criteria, EuiBasicTableProps } from '@elastic/eui'; import type { BoolQuery, Filter, Query } from '@kbn/es-query'; import { UseQueryResult } from 'react-query'; export type FindingsGroupByKind = 'default' | 'resource'; -export interface Pagination { - pagination: EuiBasicTableProps['pagination']; - setPagination(pagination: NonNullable['page']>): void; -} - -export interface Sorting { - sorting: EuiBasicTableProps['sorting']; - setSorting(sorting: NonNullable['sort']>): void; -} - export interface FindingsBaseURLQuery { query: Query; filters: Filter[]; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts index 666a4b9544978..ede9fa9bbb924 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts @@ -35,10 +35,9 @@ export const getEuiSortFromEs = ( return { sort: { field: field as keyof T, direction: direction as SortDirection } }; }; -export const getEsSortFromEui = ( - sort: Criteria['sort'] -): EsQuerySortValue[] | undefined => - sort ? [{ [sort.field]: sort.direction as SortDirection }] : undefined; +export const getEsSortFromEui = (sort: NonNullable['sort']>) => ({ + sort: [{ [sort.field]: sort.direction as SortDirection }], +}); export const getEuiPaginationFromEs = ({ size: pageSize, @@ -56,6 +55,7 @@ export const getEuiPaginationFromEs = ({ showPerPageOptions: true, }); -export const getEsPaginationFromEui = (page: Criteria['page']) => ({ - ...(!!page && { from: page.index * page.size, size: page.size }), +export const getEsPaginationFromEui = (page: NonNullable['page']>) => ({ + from: page.index * page.size, + size: page.size, }); From 7c737b1288f3905466e2a88d4eff7b670b642cf6 Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Sun, 15 May 2022 14:07:15 +0300 Subject: [PATCH 7/8] clean --- .../latest_findings_container.test.tsx | 4 +- .../latest_findings_container.tsx | 39 ++++++--------- .../latest_findings_table.test.tsx | 4 +- .../latest_findings/latest_findings_table.tsx | 15 +++--- .../latest_findings/use_latest_findings.ts | 36 +++++-------- .../resource_findings_container.tsx | 26 +++++----- .../resource_findings_table.tsx | 6 +-- .../use_resource_findings.ts | 19 ++++--- .../public/pages/findings/utils.ts | 50 ++++++------------- 9 files changed, 84 insertions(+), 115 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx index ae980c1e492bb..29c9df5f4a932 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.test.tsx @@ -18,6 +18,7 @@ import { useLocation } from 'react-router-dom'; import { RisonObject } from 'rison-node'; import { buildEsQuery } from '@kbn/es-query'; import { getFindingsCountAggQuery } from '../use_findings_count'; +import { getPaginationQuery } from '../utils'; jest.mock('../../../common/api/use_latest_findings_data_view'); jest.mock('../../../common/api/use_cis_kubernetes_integration'); @@ -69,9 +70,8 @@ describe('', () => { expect(dataMock.search.search).toHaveBeenNthCalledWith(2, { params: getFindingsQuery({ ...baseQuery, + ...getPaginationQuery(query), sort: query.sort, - size: query.size, - from: query.from, }), }); }); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx index 2e8d66cdb121c..5737fdf292c77 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx @@ -7,7 +7,6 @@ import React, { useMemo } from 'react'; import { EuiSpacer } from '@elastic/eui'; import type { DataView } from '@kbn/data-plugin/common'; -import { SortDirection } from '@kbn/data-plugin/common'; import { FormattedMessage } from '@kbn/i18n-react'; import { FindingsTable } from './latest_findings_table'; import { FindingsSearchBar } from '../layout/findings_search_bar'; @@ -18,13 +17,7 @@ import type { FindingsGroupByNoneQuery } from './use_latest_findings'; import type { FindingsBaseURLQuery } from '../types'; import { useFindingsCounter } from '../use_findings_count'; import { FindingsDistributionBar } from '../layout/findings_distribution_bar'; -import { - getBaseQuery, - getEsPaginationFromEui, - getEsSortFromEui, - getEuiPaginationFromEs, - getEuiSortFromEs, -} from '../utils'; +import { getBaseQuery, getPaginationQuery, getPaginationTableParams } from '../utils'; import { PageWrapper, PageTitle, PageTitleText } from '../layout/findings_layout'; import { FindingsGroupBySelector } from '../layout/findings_group_by_selector'; import { useCspBreadcrumbs } from '../../../common/navigation/use_csp_breadcrumbs'; @@ -33,9 +26,9 @@ import { findingsNavigation } from '../../../common/navigation/constants'; export const getDefaultQuery = (): FindingsBaseURLQuery & FindingsGroupByNoneQuery => ({ query: { language: 'kuery', query: '' }, filters: [], - sort: [{ ['@timestamp']: SortDirection.desc }], - from: 0, - size: 10, + sort: { field: '@timestamp', direction: 'desc' }, + pageIndex: 0, + pageSize: 10, }); export const LatestFindingsContainer = ({ dataView }: { dataView: DataView }) => { @@ -50,8 +43,7 @@ export const LatestFindingsContainer = ({ dataView }: { dataView: DataView }) => const findingsCount = useFindingsCounter(baseEsQuery); const findingsGroupByNone = useLatestFindings({ ...baseEsQuery, - size: urlQuery.size, - from: urlQuery.from, + ...getPaginationQuery({ pageIndex: urlQuery.pageIndex, pageSize: urlQuery.pageSize }), sort: urlQuery.sort, }); @@ -71,25 +63,24 @@ export const LatestFindingsContainer = ({ dataView }: { dataView: DataView }) => total={findingsGroupByNone.data?.total || 0} passed={findingsCount.data?.passed || 0} failed={findingsCount.data?.failed || 0} - pageStart={urlQuery.from + 1} // API index is 0, but UI is 1 - pageEnd={urlQuery.from + urlQuery.size} + pageStart={urlQuery.pageIndex + 1} // API index is 0, but UI is 1 + pageEnd={urlQuery.pageIndex + urlQuery.pageSize} /> - setUrlQuery({ - ...(page && getEsPaginationFromEui(page)), - ...(sort && getEsSortFromEui(sort)), - }) + setUrlQuery({ pageIndex: page.index, pageSize: page.size, sort }) } /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx index dac098b326f17..37e4c03d2fa7f 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.test.tsx @@ -70,7 +70,7 @@ describe('', () => { loading: false, data: { page: [], total: 0 }, error: null, - sorting: {}, + sorting: { sort: { field: '@timestamp', direction: 'desc' } }, pagination: { pageSize: 10, pageIndex: 1, totalItemCount: 0 }, setTableOptions: jest.fn(), }; @@ -92,7 +92,7 @@ describe('', () => { loading: false, data: { page: data, total: 10 }, error: null, - sorting: {}, + sorting: { sort: { field: '@timestamp', direction: 'desc' } }, pagination: { pageSize: 10, pageIndex: 1, totalItemCount: 0 }, setTableOptions: jest.fn(), }; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx index 65fa91793aaab..784bd3fa50767 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_table.tsx @@ -6,13 +6,14 @@ */ import React, { useMemo, useState } from 'react'; import { - type Criteria, EuiEmptyPrompt, EuiBasicTable, EuiBasicTableColumn, - EuiBasicTableProps, + type Pagination, + type EuiBasicTableProps, + type CriteriaWithPagination, + type EuiTableActionsColumnType, } from '@elastic/eui'; -import { EuiTableActionsColumnType } from '@elastic/eui/src/components/basic_table/table_types'; import { extractErrorMessage } from '../../../../common/utils/helpers'; import * as TEST_SUBJECTS from '../test_subjects'; import * as TEXT from '../translations'; @@ -21,10 +22,12 @@ import type { CspFindingsResult } from './use_latest_findings'; import { FindingsRuleFlyout } from '../findings_flyout/findings_flyout'; import { getExpandColumn, getFindingsColumns } from '../layout/findings_layout'; +type TableProps = Required>; + interface BaseFindingsTableProps { - pagination: EuiBasicTableProps['pagination']; - sorting: EuiBasicTableProps['sorting']; - setTableOptions(options: Criteria): void; + pagination: Pagination; + sorting: TableProps['sorting']; + setTableOptions(options: CriteriaWithPagination): void; } type FindingsTableProps = CspFindingsResult & BaseFindingsTableProps; diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts index 608f400953c86..3a86ef3a3c037 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/use_latest_findings.ts @@ -7,8 +7,9 @@ import { useQuery } from 'react-query'; import { number } from 'io-ts'; import { lastValueFrom } from 'rxjs'; -import type { EsQuerySortValue, IEsSearchResponse } from '@kbn/data-plugin/common'; +import type { IEsSearchResponse } from '@kbn/data-plugin/common'; import type { CoreStart } from '@kbn/core/public'; +import type { Criteria, Pagination } from '@elastic/eui'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import { extractErrorMessage } from '../../../../common/utils/helpers'; import * as TEXT from '../translations'; @@ -16,12 +17,18 @@ import type { CspFinding, FindingsQueryResult } from '../types'; import { useKibana } from '../../../common/hooks/use_kibana'; import type { FindingsBaseEsQuery } from '../types'; -interface UseFindingsOptions extends FindingsBaseEsQuery, FindingsGroupByNoneQuery {} - -export interface FindingsGroupByNoneQuery { +interface UseFindingsOptions extends FindingsBaseEsQuery { from: NonNullable; size: NonNullable; - sort: EsQuerySortValue[]; + sort: Sort; +} + +type Sort = NonNullable['sort']>; + +export interface FindingsGroupByNoneQuery { + pageIndex: Pagination['pageIndex']; + pageSize: Pagination['pageSize']; + sort: Sort; } interface CspFindingsData { @@ -37,23 +44,6 @@ const FIELDS_WITHOUT_KEYWORD_MAPPING = new Set(['@timestamp']); const getSortKey = (key: string): string => FIELDS_WITHOUT_KEYWORD_MAPPING.has(key) ? key : `${key}.keyword`; -/** - * @description utility to transform a column header key to its field mapping for sorting - * @example Adds '.keyword' to every property we sort on except values of `FIELDS_WITHOUT_KEYWORD_MAPPING` - * @todo find alternative - * @note we choose the keyword 'keyword' in the field mapping - */ -const mapEsQuerySortKey = (sort: readonly EsQuerySortValue[]): EsQuerySortValue[] => - sort.slice().reduce((acc, cur) => { - const entry = Object.entries(cur)[0]; - if (!entry) return acc; - - const [k, v] = entry; - acc.push({ [getSortKey(k)]: v }); - - return acc; - }, []); - export const showErrorToast = ( toasts: CoreStart['notifications']['toasts'], error: unknown @@ -67,7 +57,7 @@ export const getFindingsQuery = ({ index, query, size, from, sort }: UseFindings query, size, from, - sort: mapEsQuerySortKey(sort), + sort: [{ [getSortKey(sort.field)]: sort.direction }], }); export const useLatestFindings = ({ index, query, sort, from, size }: UseFindingsOptions) => { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx index 5ad0afef02218..e1794a92d7b92 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_container.tsx @@ -18,15 +18,15 @@ import { findingsNavigation } from '../../../../common/navigation/constants'; import { ResourceFindingsQuery, useResourceFindings } from './use_resource_findings'; import { useUrlQuery } from '../../../../common/hooks/use_url_query'; import type { FindingsBaseURLQuery } from '../../types'; -import { getBaseQuery, getEsPaginationFromEui, getEuiPaginationFromEs } from '../../utils'; +import { getBaseQuery, getPaginationQuery, getPaginationTableParams } from '../../utils'; import { ResourceFindingsTable } from './resource_findings_table'; import { FindingsSearchBar } from '../../layout/findings_search_bar'; const getDefaultQuery = (): FindingsBaseURLQuery & ResourceFindingsQuery => ({ query: { language: 'kuery', query: '' }, filters: [], - from: 0, - size: 10, + pageIndex: 0, + pageSize: 10, }); const BackToResourcesButton = () => ( @@ -47,14 +47,16 @@ export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { const { urlQuery, setUrlQuery } = useUrlQuery(getDefaultQuery); const resourceFindings = useResourceFindings({ + resourceId: params.resourceId, ...getBaseQuery({ dataView, filters: urlQuery.filters, query: urlQuery.query, }), - resourceId: params.resourceId, - size: urlQuery.size, - from: urlQuery.from, + ...getPaginationQuery({ + pageSize: urlQuery.pageSize, + pageIndex: urlQuery.pageIndex, + }), }); return ( @@ -86,15 +88,13 @@ export const ResourceFindings = ({ dataView }: { dataView: DataView }) => { loading={resourceFindings.isFetching} data={resourceFindings.data} error={resourceFindings.error} - pagination={getEuiPaginationFromEs({ - size: urlQuery.size, - from: urlQuery.from, - total: resourceFindings.data?.total, + pagination={getPaginationTableParams({ + pageSize: urlQuery.pageSize, + pageIndex: urlQuery.pageIndex, + totalItemCount: resourceFindings.data?.total || 0, })} setTableOptions={({ page }) => - setUrlQuery({ - ...(page && getEsPaginationFromEui(page)), - }) + setUrlQuery({ pageIndex: page.index, pageSize: page.size }) } /> diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx index b167f1e43d61c..8300cdd503fee 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/resource_findings_table.tsx @@ -5,7 +5,7 @@ * 2.0. */ import React from 'react'; -import { EuiEmptyPrompt, EuiBasicTable, type Criteria, EuiBasicTableProps } from '@elastic/eui'; +import { EuiEmptyPrompt, EuiBasicTable, CriteriaWithPagination, Pagination } from '@elastic/eui'; import { extractErrorMessage } from '../../../../../common/utils/helpers'; import * as TEXT from '../../translations'; import type { ResourceFindingsResult } from './use_resource_findings'; @@ -13,8 +13,8 @@ import { getFindingsColumns } from '../../layout/findings_layout'; import type { CspFinding } from '../../types'; interface Props extends ResourceFindingsResult { - pagination: EuiBasicTableProps['pagination']; - setTableOptions(options: Pick, 'page'>): void; + pagination: Pagination; + setTableOptions(options: CriteriaWithPagination): void; } const columns = getFindingsColumns(); diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts index 635fa790d6207..9e015d84e2043 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings_by_resource/resource_findings/use_resource_findings.ts @@ -8,17 +8,20 @@ import { useQuery } from 'react-query'; import { lastValueFrom } from 'rxjs'; import { IEsSearchResponse } from '@kbn/data-plugin/common'; import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; +import { Pagination } from '@elastic/eui'; import { useKibana } from '../../../../common/hooks/use_kibana'; import { showErrorToast } from '../../latest_findings/use_latest_findings'; import type { CspFinding, FindingsBaseEsQuery, FindingsQueryResult } from '../../types'; -interface UseResourceFindingsOptions extends FindingsBaseEsQuery, ResourceFindingsQuery { +interface UseResourceFindingsOptions extends FindingsBaseEsQuery { resourceId: string; + from: NonNullable; + size: NonNullable; } export interface ResourceFindingsQuery { - from: number; - size: number; + pageIndex: Pagination['pageIndex']; + pageSize: Pagination['pageSize']; } export type ResourceFindingsResult = FindingsQueryResult< @@ -30,12 +33,12 @@ const getResourceFindingsQuery = ({ index, query, resourceId, - size, from, + size, }: UseResourceFindingsOptions): estypes.SearchRequest => ({ index, - size, from, + size, body: { query: { ...query, @@ -51,8 +54,8 @@ export const useResourceFindings = ({ index, query, resourceId, - size, from, + size, }: UseResourceFindingsOptions) => { const { data, @@ -60,11 +63,11 @@ export const useResourceFindings = ({ } = useKibana().services; return useQuery( - ['csp_resource_findings', { index, query, resourceId, size, from }], + ['csp_resource_findings', { index, query, resourceId, from, size }], () => lastValueFrom>( data.search.search({ - params: getResourceFindingsQuery({ index, query, resourceId, size, from }), + params: getResourceFindingsQuery({ index, query, resourceId, from, size }), }) ), { diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts index ede9fa9bbb924..5f4d574930370 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/utils.ts @@ -6,9 +6,8 @@ */ import { buildEsQuery } from '@kbn/es-query'; -import type { DataView, EsQuerySortValue } from '@kbn/data-plugin/common'; -import { Criteria, EuiBasicTableProps } from '@elastic/eui'; -import { SortDirection } from '@kbn/data-plugin/common'; +import type { DataView } from '@kbn/data-plugin/common'; +import { EuiBasicTableProps, Pagination } from '@elastic/eui'; import type { FindingsBaseEsQuery, FindingsBaseURLQuery } from './types'; export const getBaseQuery = ({ @@ -23,39 +22,22 @@ export const getBaseQuery = ({ query: buildEsQuery(dataView, query, filters), }); -export const getEuiSortFromEs = ( - sort: EsQuerySortValue[] -): EuiBasicTableProps['sorting'] => { - if (!sort.length) return; +type TablePagination = NonNullable['pagination']>; - const entry = Object.entries(sort[0])?.[0]; - if (!entry) return; - - const [field, direction] = entry; - return { sort: { field: field as keyof T, direction: direction as SortDirection } }; -}; - -export const getEsSortFromEui = (sort: NonNullable['sort']>) => ({ - sort: [{ [sort.field]: sort.direction as SortDirection }], +export const getPaginationTableParams = ( + params: TablePagination & Pick, 'pageIndex' | 'pageSize'>, + pageSizeOptions = [10, 25, 100], + showPerPageOptions = true +): Required => ({ + ...params, + pageSizeOptions, + showPerPageOptions, }); -export const getEuiPaginationFromEs = ({ - size: pageSize, - from, - total, -}: { - total?: number | undefined; - size: number; - from: number; -}): EuiBasicTableProps['pagination'] => ({ +export const getPaginationQuery = ({ + pageIndex, pageSize, - pageIndex: Math.ceil(from / pageSize), - totalItemCount: total || 0, - pageSizeOptions: [10, 25, 100], - showPerPageOptions: true, -}); - -export const getEsPaginationFromEui = (page: NonNullable['page']>) => ({ - from: page.index * page.size, - size: page.size, +}: Pick) => ({ + from: pageIndex * pageSize, + size: pageSize, }); From 7360740eae4544085e58c5f48ff3f0a3940ea0ca Mon Sep 17 00:00:00 2001 From: Or Ouziel Date: Sun, 15 May 2022 14:27:03 +0300 Subject: [PATCH 8/8] use old pagination counter --- .../findings/latest_findings/latest_findings_container.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx index 5737fdf292c77..66bbe01ae9b02 100644 --- a/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx +++ b/x-pack/plugins/cloud_security_posture/public/pages/findings/latest_findings/latest_findings_container.tsx @@ -63,8 +63,8 @@ export const LatestFindingsContainer = ({ dataView }: { dataView: DataView }) => total={findingsGroupByNone.data?.total || 0} passed={findingsCount.data?.passed || 0} failed={findingsCount.data?.failed || 0} - pageStart={urlQuery.pageIndex + 1} // API index is 0, but UI is 1 - pageEnd={urlQuery.pageIndex + urlQuery.pageSize} + pageStart={urlQuery.pageIndex * urlQuery.pageSize + 1} // API index is 0, but UI is 1 + pageEnd={urlQuery.pageIndex * urlQuery.pageSize + urlQuery.pageSize} />