Skip to content

Commit

Permalink
Findings polish iteration (elastic#69)
Browse files Browse the repository at this point in the history
  • Loading branch information
orouz committed Jan 13, 2022
1 parent 0f6adf0 commit 35790b1
Show file tree
Hide file tree
Showing 6 changed files with 26 additions and 34 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ const FindingsComponentWithTestProvider = () => {
);
};

describe('Test findings page conditional rendering', () => {
describe('<Findings />', () => {
it("renders the error state component when 'kubebeat' DataView doesn't exists", async () => {
spy.mockImplementation(() => ({ status: 'success', data: undefined } as any));
spy.mockImplementation(() => ({ status: 'success' } as UseQueryResult<DataView>));

render(<FindingsComponentWithTestProvider />);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ const getDefaultQuery = (): Required<URLState> => ({
},
});

// with https://github.com/microsoft/TypeScript/pull/46266
// destructuring would make this more concise and won't leak props
// TODO(TS 4.6): destructure {status, error, data} to make this more concise without losing types
// see with https://github.com/microsoft/TypeScript/pull/46266
export const getFetchState = <T extends FindingsEsSearchMutation>(v: T): FindingsFetchState => {
switch (v.status) {
case 'error':
Expand Down Expand Up @@ -77,7 +77,7 @@ export const FindingsTableContainer = ({ dataView }: { dataView: DataView }) =>
// This sends a new search request to ES
// it's called whenever we have a new searchState from the URL
useEffect(() => {
mutation.mutate(void 0, {
mutation.mutate(undefined, {
onError: (e) => {
notifications?.toasts.addError(e instanceof Error ? e : new Error(), {
title: 'Search failed',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ interface FindingFlyoutProps {
findings: CSPFinding;
}

// TODO: fix scrollbar jumps
export const FindingsRuleFlyout = ({ onClose, findings }: FindingFlyoutProps) => {
const [tab, setTab] = useState<FindingsTab>('result');
return (
Expand Down Expand Up @@ -98,8 +97,9 @@ const FindingsTab = ({ tab, findings }: { findings: CSPFinding; tab: FindingsTab
return <Cards data={getRuleCards(findings)} />;
case 'resource':
return <Cards data={getResourceCards(findings)} />;
default:
assertNever(tab);
}
assertNever(tab);
};

const getResourceCards = ({ resource }: CSPFinding): Card[] => [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ interface BaseFindingsSearchBarProps {

type FindingsSearchBarProps = FindingsFetchState & BaseFindingsSearchBarProps;

/**
* Temporary Search Bar using x-pack/plugins/data
*
*/
export const FindingsSearchBar = ({
dataView,
dateRange,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import React, { useMemo, useState } from 'react';
import React, { useState, useMemo } from 'react';
import {
Criteria,
EuiLink,
Expand All @@ -28,13 +28,9 @@ interface BaseFindingsTableProps {

type FindingsTableProps = FindingsFetchState & BaseFindingsTableProps;

/**
* Temporary findings table
*/
export const FindingsTable = ({ data, status, error, selectItem }: FindingsTableProps) => {
const [pageIndex, setPageIndex] = useState(0);
const [pageSize, setPageSize] = useState(25);
const columns = useMemo(getColumns, []);

const getCellProps = (item: CSPFinding, column: EuiTableFieldDataColumnType<CSPFinding>) => ({
onClick: column.field === 'rule.name' ? () => selectItem(item) : undefined,
Expand All @@ -48,6 +44,15 @@ export const FindingsTable = ({ data, status, error, selectItem }: FindingsTable
setPageSize(size);
};

const page = useMemo(
() =>
orderBy(data, ['@timestamp'], ['desc']).slice(
pageIndex * pageSize,
pageSize * pageIndex + pageSize
),
[data, pageSize, pageIndex]
);

// TODO: add empty/error/loading views
if (!data) return null;

Expand All @@ -60,9 +65,6 @@ export const FindingsTable = ({ data, status, error, selectItem }: FindingsTable
hidePerPageOptions: false,
};

const sortedData = orderBy(data, ['@timestamp'], ['desc']);
const page = sortedData.slice(pageIndex * pageSize, pageSize * pageIndex + pageSize);

return (
<EuiBasicTable
data-test-subj={TEST_SUBJECTS.FINDINGS_TABLE}
Expand All @@ -78,8 +80,8 @@ export const FindingsTable = ({ data, status, error, selectItem }: FindingsTable
);
};

const RuleName = (name: string) => <EuiLink>{name}</EuiLink>;
const RuleTags = (tags: string[]) => (
const ruleNameRenderer = (name: string) => <EuiLink>{name}</EuiLink>;
const ruleTagsRenderer = (tags: string[]) => (
<EuiFlexGroup>
<EuiFlexItem>
<EuiBadgeGroup>
Expand All @@ -92,11 +94,11 @@ const RuleTags = (tags: string[]) => (
</EuiFlexItem>
</EuiFlexGroup>
);
const ResultEvaluation = (type: PropsOf<typeof CSPEvaluationBadge>['type']) => (
const resultEvaluationRenderer = (type: PropsOf<typeof CSPEvaluationBadge>['type']) => (
<CSPEvaluationBadge type={type} />
);

const getColumns = (): Array<EuiTableFieldDataColumnType<CSPFinding>> => [
const columns: Array<EuiTableFieldDataColumnType<CSPFinding>> = [
{
field: 'resource.filename',
name: 'Resource',
Expand All @@ -107,18 +109,18 @@ const getColumns = (): Array<EuiTableFieldDataColumnType<CSPFinding>> => [
name: 'Rule Name',
width: '50%',
truncateText: true,
render: RuleName,
render: ruleNameRenderer,
},
{
field: 'result.evaluation',
name: 'Evaluation',
width: '80px',
render: ResultEvaluation,
render: resultEvaluationRenderer,
},
{
field: 'rule.tags',
name: 'Tags',
render: RuleTags,
render: ruleTagsRenderer,
},
{
field: '@timestamp',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,14 @@ import type { CspPluginSetup } from '../../types';
import { CSP_KUBEBEAT_INDEX_PATTERN } from '../../../common/constants';
import { useKibana } from '../../../../../../src/plugins/kibana_react/public';

// TODO: find similar/existing function
export const extractErrorMessage = (e: unknown) =>
export const extractErrorMessage = (e: unknown): string =>
typeof e === 'string' ? e : (e as Error)?.message || 'Unknown Error';

// TODO: find kibanas' equivalent fn
export const isNonNullable = <T extends unknown>(v: T): v is NonNullable<T> =>
v !== null && v !== undefined;

/**
* Temp DataView Utility
* registers a kibana data view for kubebeat* index
* registers a kibana data view for kubebeat* index and fetches it
* TODO: use perfected kibana data views
*/

Expand All @@ -55,9 +52,6 @@ export const useKubebeatDataView = () => {
return useQuery(['kubebeat_dataview'], getKubebeatDataView);
};

/**
* Temp URL state utility
*/
export const useSourceQueryParam = <T extends object>(getDefaultQuery: () => T) => {
const history = useHistory();
const [state, set] = useState<T>(getDefaultQuery());
Expand Down

0 comments on commit 35790b1

Please sign in to comment.