Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SIEM] Fix Inspect query 'request timestamp' value changes when curso… #54223

Merged
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -59,179 +59,178 @@ interface Props {
kqlMode: KqlMode;
onChangeItemsPerPage: OnChangeItemsPerPage;
query: Query;
showInspect: boolean;
start: number;
sort: Sort;
timelineTypeContext: TimelineTypeContextProps;
toggleColumn: (column: ColumnHeader) => void;
utilityBar?: (totalCount: number) => React.ReactNode;
}

export const EventsViewer = React.memo<Props>(
({
browserFields,
columns,
const EventsViewerComponent: React.FC<Props> = ({
browserFields,
columns,
dataProviders,
deletedEventIds,
end,
filters,
headerFilterGroup,
height = DEFAULT_EVENTS_VIEWER_HEIGHT,
id,
indexPattern,
isLive,
itemsPerPage,
itemsPerPageOptions,
kqlMode,
onChangeItemsPerPage,
query,
start,
sort,
timelineTypeContext,
toggleColumn,
utilityBar,
}) => {
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
const kibana = useKibana();
const combinedQueries = combineQueries({
config: esQuery.getEsQueryConfig(kibana.services.uiSettings),
dataProviders,
deletedEventIds,
end,
filters,
headerFilterGroup,
height = DEFAULT_EVENTS_VIEWER_HEIGHT,
id,
indexPattern,
isLive,
itemsPerPage,
itemsPerPageOptions,
browserFields,
filters,
kqlQuery: query,
kqlMode,
onChangeItemsPerPage,
query,
showInspect,
start,
sort,
timelineTypeContext,
toggleColumn,
utilityBar,
}) => {
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
const kibana = useKibana();
const combinedQueries = combineQueries({
config: esQuery.getEsQueryConfig(kibana.services.uiSettings),
dataProviders,
indexPattern,
browserFields,
filters,
kqlQuery: query,
kqlMode,
start,
end,
isEventViewer: true,
});
const queryFields = useMemo(
() =>
union(
columnsHeader.map(c => c.id),
timelineTypeContext.queryFields ?? []
),
[columnsHeader, timelineTypeContext.queryFields]
);
end,
isEventViewer: true,
});
const queryFields = useMemo(
() =>
union(
columnsHeader.map(c => c.id),
timelineTypeContext.queryFields ?? []
),
[columnsHeader, timelineTypeContext.queryFields]
);

return (
<EuiPanel data-test-subj="events-viewer-panel" grow={false}>
<AutoSizer detectAnyWindowResize={true} content>
{({ measureRef, content: { width = 0 } }) => (
<>
<WrappedByAutoSizer ref={measureRef}>
<div
data-test-subj="events-viewer-measured"
style={{ height: '0px', width: '100%' }}
/>
</WrappedByAutoSizer>
return (
<EuiPanel data-test-subj="events-viewer-panel" grow={false}>
<AutoSizer detectAnyWindowResize={true} content>
{({ measureRef, content: { width = 0 } }) => (
<>
<WrappedByAutoSizer ref={measureRef}>
<div
data-test-subj="events-viewer-measured"
style={{ height: '0px', width: '100%' }}
/>
</WrappedByAutoSizer>

{combinedQueries != null ? (
<TimelineQuery
fields={queryFields}
filterQuery={combinedQueries.filterQuery}
id={id}
indexPattern={indexPattern}
limit={itemsPerPage}
sortField={{
sortFieldId: sort.columnId,
direction: sort.sortDirection as Direction,
}}
sourceId="default"
>
{({
events,
getUpdatedAt,
inspect,
loading,
loadMore,
pageInfo,
refetch,
totalCount = 0,
}) => {
const totalCountMinusDeleted =
totalCount > 0 ? totalCount - deletedEventIds.length : 0;
{combinedQueries != null ? (
<TimelineQuery
fields={queryFields}
filterQuery={combinedQueries.filterQuery}
id={id}
indexPattern={indexPattern}
limit={itemsPerPage}
sortField={{
sortFieldId: sort.columnId,
direction: sort.sortDirection as Direction,
}}
sourceId="default"
>
{({
events,
getUpdatedAt,
inspect,
loading,
loadMore,
pageInfo,
refetch,
totalCount = 0,
}) => {
const totalCountMinusDeleted =
totalCount > 0 ? totalCount - deletedEventIds.length : 0;

// TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt)
return (
<>
<HeaderSection
id={id}
showInspect={showInspect}
subtitle={
utilityBar
? undefined
: `${
i18n.SHOWING
}: ${totalCountMinusDeleted.toLocaleString()} ${i18n.UNIT(
totalCountMinusDeleted
)}`
}
title={timelineTypeContext?.title ?? i18n.EVENTS}
>
{headerFilterGroup}
</HeaderSection>
// TODO: Reset eventDeletedIds/eventLoadingIds on refresh/loadmore (getUpdatedAt)
return (
<>
<HeaderSection
id={id}
subtitle={
utilityBar
? undefined
: `${
i18n.SHOWING
}: ${totalCountMinusDeleted.toLocaleString()} ${i18n.UNIT(
totalCountMinusDeleted
)}`
}
title={timelineTypeContext?.title ?? i18n.EVENTS}
>
{headerFilterGroup}
</HeaderSection>

{utilityBar?.(totalCountMinusDeleted)}
{utilityBar?.(totalCountMinusDeleted)}

<div
data-test-subj={`events-container-loading-${loading}`}
style={{ width: `${width}px` }}
<div
data-test-subj={`events-container-loading-${loading}`}
style={{ width: `${width}px` }}
>
<ManageTimelineContext
loading={loading}
width={width}
type={timelineTypeContext}
>
<ManageTimelineContext
<TimelineRefetch
id={id}
inputId="global"
inspect={inspect}
loading={loading}
width={width}
type={timelineTypeContext}
>
<TimelineRefetch
id={id}
inputId="global"
inspect={inspect}
loading={loading}
refetch={refetch}
/>
refetch={refetch}
/>

<StatefulBody
browserFields={browserFields}
data={events.filter(e => !deletedEventIds.includes(e._id))}
id={id}
isEventViewer={true}
height={height}
sort={sort}
toggleColumn={toggleColumn}
/>

<StatefulBody
browserFields={browserFields}
data={events.filter(e => !deletedEventIds.includes(e._id))}
id={id}
isEventViewer={true}
height={height}
sort={sort}
toggleColumn={toggleColumn}
/>
<Footer
compact={isCompactFooter(width)}
getUpdatedAt={getUpdatedAt}
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
height={footerHeight}
isEventViewer={true}
isLive={isLive}
isLoading={loading}
itemsCount={events.length}
itemsPerPage={itemsPerPage}
itemsPerPageOptions={itemsPerPageOptions}
onChangeItemsPerPage={onChangeItemsPerPage}
onLoadMore={loadMore}
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
serverSideEventCount={totalCountMinusDeleted}
tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
/>
</ManageTimelineContext>
</div>
</>
);
}}
</TimelineQuery>
) : null}
</>
)}
</AutoSizer>
</EuiPanel>
);
};

<Footer
compact={isCompactFooter(width)}
getUpdatedAt={getUpdatedAt}
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
height={footerHeight}
isEventViewer={true}
isLive={isLive}
isLoading={loading}
itemsCount={events.length}
itemsPerPage={itemsPerPage}
itemsPerPageOptions={itemsPerPageOptions}
onChangeItemsPerPage={onChangeItemsPerPage}
onLoadMore={loadMore}
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
serverSideEventCount={totalCountMinusDeleted}
tieBreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)}
/>
</ManageTimelineContext>
</div>
</>
);
}}
</TimelineQuery>
) : null}
</>
)}
</AutoSizer>
</EuiPanel>
);
},
export const EventsViewer = React.memo(
EventsViewerComponent,
(prevProps, nextProps) =>
prevProps.browserFields === nextProps.browserFields &&
prevProps.columns === nextProps.columns &&
Expand All @@ -247,10 +246,8 @@ export const EventsViewer = React.memo<Props>(
prevProps.itemsPerPageOptions === nextProps.itemsPerPageOptions &&
prevProps.kqlMode === nextProps.kqlMode &&
isEqual(prevProps.query, nextProps.query) &&
prevProps.showInspect === nextProps.showInspect &&
prevProps.start === nextProps.start &&
prevProps.sort === nextProps.sort &&
isEqual(prevProps.timelineTypeContext, nextProps.timelineTypeContext) &&
prevProps.utilityBar === nextProps.utilityBar
);
EventsViewer.displayName = 'EventsViewer';
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { StatefulEventsViewer } from '.';
import { useFetchIndexPatterns } from '../../containers/detection_engine/rules/fetch_index_patterns';
import { mockBrowserFields } from '../../containers/source/mock';
import { eventsDefaultModel } from './default_model';
import { BUTTON_CLASS } from '../inspect';

const mockUseFetchIndexPatterns: jest.Mock = useFetchIndexPatterns as jest.Mock;
jest.mock('../../containers/detection_engine/rules/fetch_index_patterns');
Expand Down Expand Up @@ -74,12 +75,9 @@ describe('StatefulEventsViewer', () => {
await wait();
wrapper.update();

expect(
wrapper
.find(`[data-test-subj="transparent-inspect-container"]`)
.first()
.exists()
).toBe(true);
expect(wrapper.find(`InspectButtonContainer`)).toHaveStyleRule('opacity', '0', {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like these tests should live in inspect.index.test.tsx, no? If we truly want a test for the integration here, I would just keep the exists() check.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then we won't need to pass around BUTTON_CLASS, either.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about that as well, please let me know if it was done how you thought so :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great! I'd love to get rid of the BUTTON_CLASS constant entirely but I couldn't immediately think of a way to get that to work. I was hoping we could use the css helper described here, but no such luck.

modifier: `.${BUTTON_CLASS}`,
});
});

test('it renders an opaque inspect button when it has mouse focus', async () => {
Expand All @@ -99,14 +97,8 @@ describe('StatefulEventsViewer', () => {
await wait();
wrapper.update();

wrapper.simulate('mouseenter');
wrapper.update();

expect(
wrapper
.find(`[data-test-subj="opaque-inspect-container"]`)
.first()
.exists()
).toBe(true);
expect(wrapper.find(`InspectButtonContainer`)).toHaveStyleRule('opacity', '1', {
modifier: `:hover .${BUTTON_CLASS}`,
});
});
});
Loading