Skip to content

Commit

Permalink
[Cloud Security] CloudSecurityDataTable component (#167587)
Browse files Browse the repository at this point in the history
## Summary

This PR introduces the Cloud Security DataTable component, meant to
replace and consolidate the tables used in the Cloud Security plugin

The CloudSecurityDataTable component is a wrapper over the
`<UnifiedDataTable/>` component. We made that decision based on the
number of features it provides for the users plus support from the most
common features such as Flyout, Sort, Filtering, and Pagination to the
most advanced features like Virtualization and DataView integration.

- **Virtualization**: Thanks to Virtualization, users can fetch more
data on the table than the limit of `500`, and also use larger items per
page if desired.
- **DataView integration**: This option allows users to rename Columns
as needed by setting custom labels in the DataView.
- **Column control**: Users can move columns and resize it as needed,
and that settings is saved on the local storage.
- **Row Height control**: Users can modify in the Advanced Settings ->
`discover:rowHeight` the height of the row.

Below is a Matrix comparing the features between the current Findings
table, the Vulnerabilities Data Grid and the new CloudSecurityDataTable.

Feature | CloudSecurity DataTable (new >= 8.11) | Findings Table
(current <= 8.10) | Vulnerabilities Data Grid (current <= 8.10)
-- | -- | -- | --
Tooltip on Cell Hover | ❌ | ✅ | ❌
Column Sorting | ✅ | ✅ | ✅
Multi Column Sorting | ✅ | ❌ | ✅
Column filtering | ✅ | ✅ | ✅
Pagination | ✅ | ✅ | ✅
Rows per page | ✅ | ✅ | ✅
Virtualization (Support to load more data) | ✅ | ❌ | ❌
Custom component on Column Header | ❌ (only custom text) | ✅ | ❌
Additional controls on the left | ✅ | ❌ | ✅
Text truncation | ✅ | ✅ | ✅
Override Style | ✅ (#166994) | ✅ |
✅
Load more button | ✅ | ❌ | ❌
Resize column | ✅ | ❌ | ✅
Reorder column | ✅ | ❌ | ✅
FullScreen button | ✅ | ❌ | ✅
User-defined row height | ✅ | ❌ | ❌
external pagination control (enables Flyout pagination) | ❌ | ✅ | ✅
user-defined column renaming | ✅ (using DataViews) | ❌ | ❌


### Regressions

There are some regressions such as losing the ability to paginate on the
Flyout and the table pagination is no longer controlled by URL params,
that's because pagination is controlled by an internal state in the
`<UnifiedDataTable/>` component, and we plan to re-enable those features
again in the future by contributing to the `<UnifiedDataTable/>`
component.

### Screenshots


![image](https://github.com/elastic/kibana/assets/19270322/a0d1f95a-adcc-4e58-9d3e-0adec3df8b3b)

### Videos

Basic Features + Virtualization


https://github.com/elastic/kibana/assets/19270322/b1a61592-e1ae-4baf-9610-3e24c473c17d



https://github.com/elastic/kibana/assets/19270322/d8e6106c-0ca3-4277-b78b-5ca482095ae1


DataView integration



https://github.com/elastic/kibana/assets/19270322/0d583243-bb86-45e4-baa5-dc63253da8f6

Row Height Control



https://github.com/elastic/kibana/assets/19270322/b1d43609-7c8a-4855-ab2f-624c18663579

---------

Co-authored-by: kibanamachine <[email protected]>
Co-authored-by: Jordan <[email protected]>
  • Loading branch information
3 people authored Oct 5, 2023
1 parent e1d3cb9 commit 8d5dfaf
Show file tree
Hide file tree
Showing 27 changed files with 911 additions and 745 deletions.
4 changes: 3 additions & 1 deletion x-pack/plugins/cloud_security_posture/kibana.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"requiredPlugins": [
"navigation",
"data",
"dataViews",
"fleet",
"unifiedSearch",
"taskManager",
Expand All @@ -19,7 +20,8 @@
"discover",
"cloud",
"licensing",
"share"
"share",
"kibanaUtils"
],
"optionalPlugins": ["usageCollection"],
"requiredBundles": ["kibanaReact", "usageCollection"]
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,45 @@
import { useQuery } from '@tanstack/react-query';
import { useKibana } from '@kbn/kibana-react-plugin/public';
import type { DataView } from '@kbn/data-plugin/common';
import { i18n } from '@kbn/i18n';
import { LATEST_FINDINGS_INDEX_PATTERN } from '../../../common/constants';
import { CspClientPluginStartDeps } from '../../types';

const cloudSecurityFieldLabels: Record<string, string> = {
'result.evaluation': i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.resultColumnLabel',
{ defaultMessage: 'Result' }
),
'resource.id': i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.resourceIdColumnLabel',
{ defaultMessage: 'Resource ID' }
),
'resource.name': i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.resourceNameColumnLabel',
{ defaultMessage: 'Resource Name' }
),
'resource.sub_type': i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.resourceTypeColumnLabel',
{ defaultMessage: 'Resource Type' }
),
'rule.benchmark.rule_number': i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.ruleNumberColumnLabel',
{ defaultMessage: 'Rule Number' }
),
'rule.name': i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.ruleNameColumnLabel',
{ defaultMessage: 'Rule Name' }
),
'rule.section': i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.ruleSectionColumnLabel',
{ defaultMessage: 'CIS Section' }
),
'@timestamp': i18n.translate(
'xpack.csp.findings.findingsTable.findingsTableColumn.lastCheckedColumnLabel',
{ defaultMessage: 'Last Checked' }
),
} as const;

/**
* TODO: use perfected kibana data views
*/
Expand All @@ -19,11 +56,23 @@ export const useLatestFindingsDataView = (dataView: string) => {
} = useKibana<CspClientPluginStartDeps>().services;

const findDataView = async (): Promise<DataView> => {
const dataViewObj = (await dataViews.find(dataView))?.[0];
const [dataViewObj] = await dataViews.find(dataView);
if (!dataViewObj) {
throw new Error(`Data view not found [Name: {${dataView}}]`);
}

if (dataView === LATEST_FINDINGS_INDEX_PATTERN) {
Object.entries(cloudSecurityFieldLabels).forEach(([field, label]) => {
if (
!dataViewObj.getFieldAttrs()[field]?.customLabel ||
dataViewObj.getFieldAttrs()[field]?.customLabel === field
) {
dataViewObj.setFieldCustomLabel(field, label);
}
});
await dataViews.updateSavedObject(dataViewObj);
}

return dataViewObj;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export const CSP_MOMENT_FORMAT = 'MMMM D, YYYY @ HH:mm:ss.SSS';
export const MAX_FINDINGS_TO_LOAD = 500;
export const DEFAULT_VISIBLE_ROWS_PER_PAGE = 25;

export const LOCAL_STORAGE_DATA_TABLE_PAGE_SIZE_KEY = 'cloudPosture:dataTable:pageSize';
export const LOCAL_STORAGE_DATA_TABLE_COLUMNS_KEY = 'cloudPosture:dataTable:columns';
export const LOCAL_STORAGE_PAGE_SIZE_FINDINGS_KEY = 'cloudPosture:findings:pageSize';
export const LOCAL_STORAGE_PAGE_SIZE_BENCHMARK_KEY = 'cloudPosture:benchmark:pageSize';
export const LOCAL_STORAGE_PAGE_SIZE_RULES_KEY = 'cloudPosture:rules:pageSize';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,36 @@ import { Dispatch, SetStateAction, useCallback } from 'react';
import { type DataView } from '@kbn/data-views-plugin/common';
import { BoolQuery } from '@kbn/es-query';
import { CriteriaWithPagination } from '@elastic/eui';
import { DataTableRecord } from '@kbn/discover-utils/types';
import { useUrlQuery } from '../use_url_query';
import { usePageSize } from '../use_page_size';
import { getDefaultQuery, useBaseEsQuery, usePersistedQuery } from './utils';

interface QuerySort {
direction: string;
id: string;
}
import { LOCAL_STORAGE_DATA_TABLE_COLUMNS_KEY } from '../../constants';

export interface CloudPostureTableResult {
// TODO: Remove any when all finding tables are converted to CloudSecurityDataTable
setUrlQuery: (query: any) => void;
// TODO: remove any, this sorting is used for both EuiGrid and EuiTable which uses different types of sorts
// TODO: Remove any when all finding tables are converted to CloudSecurityDataTable
sort: any;
// TODO: Remove any when all finding tables are converted to CloudSecurityDataTable
filters: any[];
query?: { bool: BoolQuery };
queryError?: Error;
pageIndex: number;
// TODO: remove any, urlQuery is an object with query fields but we also add custom fields to it, need to assert usages
urlQuery: any;
setTableOptions: (options: CriteriaWithPagination<object>) => void;
// TODO: Remove any when all finding tables are converted to CloudSecurityDataTable
handleUpdateQuery: (query: any) => void;
pageSize: number;
setPageSize: Dispatch<SetStateAction<number | undefined>>;
onChangeItemsPerPage: (newPageSize: number) => void;
onChangePage: (newPageIndex: number) => void;
onSort: (sort: QuerySort[]) => void;
// TODO: Remove any when all finding tables are converted to CloudSecurityDataTable
onSort: (sort: any) => void;
onResetFilters: () => void;
columnsLocalStorageKey: string;
getRowsFromPages: (data: Array<{ page: DataTableRecord[] }> | undefined) => DataTableRecord[];
}

/*
Expand All @@ -44,10 +47,13 @@ export const useCloudPostureTable = ({
defaultQuery = getDefaultQuery,
dataView,
paginationLocalStorageKey,
columnsLocalStorageKey,
}: {
// TODO: Remove any when all finding tables are converted to CloudSecurityDataTable
defaultQuery?: (params: any) => any;
dataView: DataView;
paginationLocalStorageKey: string;
columnsLocalStorageKey?: string;
}): CloudPostureTableResult => {
const getPersistedDefaultQuery = usePersistedQuery(defaultQuery);
const { urlQuery, setUrlQuery } = useUrlQuery(getPersistedDefaultQuery);
Expand Down Expand Up @@ -120,6 +126,13 @@ export const useCloudPostureTable = ({
[setUrlQuery]
);

const getRowsFromPages = (data: Array<{ page: DataTableRecord[] }> | undefined) =>
data
?.map(({ page }: { page: DataTableRecord[] }) => {
return page;
})
.flat() || [];

return {
setUrlQuery,
sort: urlQuery.sort,
Expand All @@ -136,5 +149,7 @@ export const useCloudPostureTable = ({
onChangePage,
onSort,
onResetFilters,
columnsLocalStorageKey: columnsLocalStorageKey || LOCAL_STORAGE_DATA_TABLE_COLUMNS_KEY,
getRowsFromPages,
};
};
Loading

0 comments on commit 8d5dfaf

Please sign in to comment.