mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
Add date picker in timeline + clean up queryDate in draggable wrapper… (#35340)
* Add date picker in timeline + clean up queryDate in draggable wrapper + remove all the poll stuff * fix api integration test * review I * review II
This commit is contained in:
parent
75012de975
commit
1fae72b272
131 changed files with 1307 additions and 1901 deletions
|
@ -14,10 +14,6 @@ exports[`DraggableWrapper rendering it renders against the snapshot 1`] = `
|
|||
"id": "id-Provider 1",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 1",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 1",
|
||||
|
|
|
@ -8,12 +8,16 @@ import * as React from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import { pure } from 'recompose';
|
||||
import { Dispatch } from 'redux';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
|
||||
import { History } from '../../../lib/history';
|
||||
import { Note } from '../../../lib/note';
|
||||
import {
|
||||
appActions,
|
||||
appSelectors,
|
||||
inputsActions,
|
||||
inputsModel,
|
||||
inputsSelectors,
|
||||
State,
|
||||
timelineActions,
|
||||
timelineModel,
|
||||
|
@ -30,18 +34,17 @@ interface OwnProps {
|
|||
}
|
||||
|
||||
interface StateReduxProps {
|
||||
description?: string;
|
||||
description: string;
|
||||
getNotesByIds: (noteIds: string[]) => Note[];
|
||||
history?: History[];
|
||||
isFavorite?: boolean;
|
||||
isLive?: boolean;
|
||||
isFavorite: boolean;
|
||||
isDatepickerLocked: boolean;
|
||||
noteIds: string[];
|
||||
title?: string;
|
||||
width?: number;
|
||||
title: string;
|
||||
width: number;
|
||||
}
|
||||
|
||||
interface DispatchProps {
|
||||
associateNote?: (noteId: string) => void;
|
||||
associateNote: (noteId: string) => void;
|
||||
applyDeltaToWidth?: (
|
||||
{
|
||||
id,
|
||||
|
@ -57,12 +60,12 @@ interface DispatchProps {
|
|||
minWidthPixels: number;
|
||||
}
|
||||
) => void;
|
||||
createTimeline?: ({ id, show }: { id: string; show?: boolean }) => void;
|
||||
updateDescription?: ({ id, description }: { id: string; description: string }) => void;
|
||||
updateIsFavorite?: ({ id, isFavorite }: { id: string; isFavorite: boolean }) => void;
|
||||
updateIsLive?: ({ id, isLive }: { id: string; isLive: boolean }) => void;
|
||||
updateNote?: UpdateNote;
|
||||
updateTitle?: ({ id, title }: { id: string; title: string }) => void;
|
||||
createTimeline: ActionCreator<{ id: string; show?: boolean }>;
|
||||
toggleLock: ActionCreator<{ linkToId: inputsModel.InputsModelId }>;
|
||||
updateDescription: ActionCreator<{ id: string; description: string }>;
|
||||
updateIsFavorite: ActionCreator<{ id: string; isFavorite: boolean }>;
|
||||
updateNote: UpdateNote;
|
||||
updateTitle: ActionCreator<{ id: string; title: string }>;
|
||||
}
|
||||
|
||||
type Props = OwnProps & StateReduxProps & DispatchProps;
|
||||
|
@ -73,36 +76,34 @@ const statefulFlyoutHeader = pure<Props>(
|
|||
createTimeline,
|
||||
description,
|
||||
getNotesByIds,
|
||||
history,
|
||||
isFavorite,
|
||||
isLive,
|
||||
isDatepickerLocked,
|
||||
title,
|
||||
width = defaultWidth,
|
||||
noteIds,
|
||||
timelineId,
|
||||
toggleLock,
|
||||
updateDescription,
|
||||
updateIsFavorite,
|
||||
updateIsLive,
|
||||
updateNote,
|
||||
updateTitle,
|
||||
usersViewing,
|
||||
}) => (
|
||||
<Properties
|
||||
associateNote={associateNote!}
|
||||
createTimeline={createTimeline!}
|
||||
description={description!}
|
||||
associateNote={associateNote}
|
||||
createTimeline={createTimeline}
|
||||
description={description}
|
||||
getNotesByIds={getNotesByIds}
|
||||
history={history!}
|
||||
isFavorite={isFavorite!}
|
||||
isLive={isLive!}
|
||||
title={title!}
|
||||
isDatepickerLocked={isDatepickerLocked}
|
||||
isFavorite={isFavorite}
|
||||
title={title}
|
||||
noteIds={noteIds}
|
||||
timelineId={timelineId}
|
||||
updateDescription={updateDescription!}
|
||||
updateIsFavorite={updateIsFavorite!}
|
||||
updateIsLive={updateIsLive!}
|
||||
updateTitle={updateTitle!}
|
||||
updateNote={updateNote!}
|
||||
toggleLock={toggleLock}
|
||||
updateDescription={updateDescription}
|
||||
updateIsFavorite={updateIsFavorite}
|
||||
updateTitle={updateTitle}
|
||||
updateNote={updateNote}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
|
@ -114,13 +115,13 @@ const emptyHistory: History[] = []; // stable reference
|
|||
const makeMapStateToProps = () => {
|
||||
const getTimeline = timelineSelectors.getTimelineByIdSelector();
|
||||
const getNotesByIds = appSelectors.notesByIdsSelector();
|
||||
const getGlobalInput = inputsSelectors.globalSelector();
|
||||
const mapStateToProps = (state: State, { timelineId }: OwnProps) => {
|
||||
const timeline: timelineModel.TimelineModel = getTimeline(state, timelineId);
|
||||
|
||||
const globalInput: inputsModel.InputsRange = getGlobalInput(state);
|
||||
const {
|
||||
description = '',
|
||||
isFavorite = false,
|
||||
isLive = false,
|
||||
title = '',
|
||||
noteIds = [],
|
||||
width = defaultWidth,
|
||||
|
@ -133,7 +134,7 @@ const makeMapStateToProps = () => {
|
|||
getNotesByIds: getNotesByIds(state),
|
||||
history,
|
||||
isFavorite,
|
||||
isLive,
|
||||
isDatepickerLocked: globalInput.linkTo.includes('timeline'),
|
||||
noteIds,
|
||||
title,
|
||||
width,
|
||||
|
@ -187,6 +188,9 @@ const mapDispatchToProps = (dispatch: Dispatch, { timelineId }: OwnProps) => ({
|
|||
updateTitle: ({ id, title }: { id: string; title: string }) => {
|
||||
dispatch(timelineActions.updateTitle({ id, title }));
|
||||
},
|
||||
toggleLock: ({ linkToId }: { linkToId: inputsModel.InputsModelId }) => {
|
||||
dispatch(inputsActions.toggleTimelineLinkTo({ linkToId }));
|
||||
},
|
||||
});
|
||||
|
||||
export const FlyoutHeader = connect(
|
||||
|
|
|
@ -18,7 +18,7 @@ import { FlyoutHeader } from '../header';
|
|||
|
||||
import * as i18n from './translations';
|
||||
|
||||
const minWidthPixels = 440; // do not allow the flyout to shrink below this width (pixels)
|
||||
const minWidthPixels = 550; // do not allow the flyout to shrink below this width (pixels)
|
||||
const maxWidthPercent = 95; // do not allow the flyout to grow past this percentage of the view
|
||||
interface OwnProps {
|
||||
children: React.ReactNode;
|
||||
|
@ -60,6 +60,9 @@ const EuiFlyoutContainer = styled.div<{ headerHeight: number; width: number }>`
|
|||
.timeline-flyout-body {
|
||||
overflow-y: hidden;
|
||||
padding: 0;
|
||||
.euiFlyoutBody__overflow {
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -68,11 +71,12 @@ const FlyoutHeaderContainer = styled.div`
|
|||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
// manually wrap the close button because EuiButtonIcon can't be a wrapped `styled`
|
||||
const WrappedCloseButton = styled.div`
|
||||
margin-right: 10px;
|
||||
margin-right: 5px;
|
||||
`;
|
||||
|
||||
const FlyoutHeaderWithCloseButton = pure<{
|
||||
|
|
|
@ -25,10 +25,14 @@ export const LocalizedDateTooltip = pure<{
|
|||
</EuiFlexItem>
|
||||
) : null}
|
||||
<EuiFlexItem grow={false}>
|
||||
<FormattedRelative data-test-subj="humanized-relative-date" value={date} />
|
||||
<FormattedRelative
|
||||
data-test-subj="humanized-relative-date"
|
||||
value={moment.utc(date).toDate()}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem data-test-subj="with-day-of-week" grow={false}>
|
||||
{moment(date)
|
||||
{moment
|
||||
.utc(date)
|
||||
.local()
|
||||
.format('llll')}
|
||||
</EuiFlexItem>
|
||||
|
|
|
@ -104,7 +104,6 @@ exports[`Authentication Table Component rendering it renders the default Authent
|
|||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="aa7ca589f1b8220002f2fc61c64cfbf1"
|
||||
startDate={1546965070707}
|
||||
totalCount={4}
|
||||
type="page"
|
||||
/>
|
||||
|
|
|
@ -18,7 +18,6 @@ import { mockData } from './mock';
|
|||
|
||||
describe('Authentication Table Component', () => {
|
||||
const loadMore = jest.fn();
|
||||
const startDate = new Date('2019-01-08T16:31:10.707Z').valueOf();
|
||||
const state: State = mockGlobalState;
|
||||
|
||||
let store = createStore(state);
|
||||
|
@ -38,7 +37,6 @@ describe('Authentication Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.Authentications.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.Authentications.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
|
|
|
@ -30,7 +30,6 @@ interface OwnProps {
|
|||
nextCursor: string;
|
||||
totalCount: number;
|
||||
loadMore: (cursor: string) => void;
|
||||
startDate: number;
|
||||
type: hostsModel.HostsType;
|
||||
}
|
||||
|
||||
|
@ -75,11 +74,10 @@ const AuthenticationTableComponent = pure<AuthenticationTableProps>(
|
|||
totalCount,
|
||||
nextCursor,
|
||||
updateLimitPagination,
|
||||
startDate,
|
||||
type,
|
||||
}) => (
|
||||
<LoadMoreTable
|
||||
columns={getAuthenticationColumns(startDate)}
|
||||
columns={getAuthenticationColumns()}
|
||||
loadingTitle={i18n.AUTHENTICATIONS}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
|
@ -114,7 +112,7 @@ export const AuthenticationTable = connect(
|
|||
}
|
||||
)(AuthenticationTableComponent);
|
||||
|
||||
const getAuthenticationColumns = (startDate: number): Array<Columns<AuthenticationsEdges>> => [
|
||||
const getAuthenticationColumns = (): Array<Columns<AuthenticationsEdges>> => [
|
||||
{
|
||||
name: i18n.USER,
|
||||
truncateText: false,
|
||||
|
@ -137,10 +135,6 @@ const getAuthenticationColumns = (startDate: number): Array<Columns<Authenticati
|
|||
field: 'user.name',
|
||||
value: userName,
|
||||
},
|
||||
queryDate: {
|
||||
from: startDate,
|
||||
to: Date.now(),
|
||||
},
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
@ -180,10 +174,6 @@ const getAuthenticationColumns = (startDate: number): Array<Columns<Authenticati
|
|||
field: 'event.type',
|
||||
value: 'authentication_failure',
|
||||
},
|
||||
queryDate: {
|
||||
from: startDate,
|
||||
to: Date.now(),
|
||||
},
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
@ -235,10 +225,6 @@ const getAuthenticationColumns = (startDate: number): Array<Columns<Authenticati
|
|||
field: 'source.ip',
|
||||
value: sourceIp,
|
||||
},
|
||||
queryDate: {
|
||||
from: startDate,
|
||||
to: Date.now(),
|
||||
},
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
@ -279,7 +265,6 @@ const getAuthenticationColumns = (startDate: number): Array<Columns<Authenticati
|
|||
field: 'host.name',
|
||||
value: hostName,
|
||||
},
|
||||
queryDate: { from: startDate, to: Date.now() },
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
@ -319,10 +304,6 @@ const getAuthenticationColumns = (startDate: number): Array<Columns<Authenticati
|
|||
field: 'event.type',
|
||||
value: 'authentication_success',
|
||||
},
|
||||
queryDate: {
|
||||
from: startDate,
|
||||
to: Date.now(),
|
||||
},
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
@ -374,10 +355,6 @@ const getAuthenticationColumns = (startDate: number): Array<Columns<Authenticati
|
|||
field: 'source.ip',
|
||||
value: sourceIp,
|
||||
},
|
||||
queryDate: {
|
||||
from: startDate,
|
||||
to: Date.now(),
|
||||
},
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
@ -418,7 +395,6 @@ const getAuthenticationColumns = (startDate: number): Array<Columns<Authenticati
|
|||
field: 'host.name',
|
||||
value: hostName,
|
||||
},
|
||||
queryDate: { from: startDate, to: Date.now() },
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
|
|
@ -86,7 +86,6 @@ exports[`Load More Events Table Component rendering it renders the default Event
|
|||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="1546878704036"
|
||||
startDate={1546878704036}
|
||||
tiebreaker="10624"
|
||||
totalCount={15546}
|
||||
type="page"
|
||||
|
|
|
@ -44,7 +44,6 @@ describe('Load More Events Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.Events.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={1546878704036}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
|
|
|
@ -30,7 +30,6 @@ interface OwnProps {
|
|||
tiebreaker: string;
|
||||
totalCount: number;
|
||||
loadMore: (cursor: string, tiebreaker: string) => void;
|
||||
startDate: number;
|
||||
type: hostsModel.HostsType;
|
||||
}
|
||||
|
||||
|
@ -74,11 +73,10 @@ const EventsTableComponent = pure<EventsTableProps>(
|
|||
totalCount,
|
||||
nextCursor,
|
||||
updateLimitPagination,
|
||||
startDate,
|
||||
type,
|
||||
}) => (
|
||||
<LoadMoreTable
|
||||
columns={getEventsColumns(startDate)}
|
||||
columns={getEventsColumns()}
|
||||
loadingTitle={i18n.EVENTS}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
|
@ -113,7 +111,7 @@ export const EventsTable = connect(
|
|||
}
|
||||
)(EventsTableComponent);
|
||||
|
||||
const getEventsColumns = (startDate: number): Array<Columns<EcsEdges>> => [
|
||||
const getEventsColumns = (): Array<Columns<EcsEdges>> => [
|
||||
{
|
||||
name: i18n.HOST_NAME,
|
||||
sortable: true,
|
||||
|
@ -137,10 +135,6 @@ const getEventsColumns = (startDate: number): Array<Columns<EcsEdges>> => [
|
|||
field: 'host.name',
|
||||
value: hostName,
|
||||
},
|
||||
queryDate: {
|
||||
from: startDate,
|
||||
to: Date.now(),
|
||||
},
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { cloneDeep } from 'lodash/fp';
|
||||
import * as React from 'react';
|
||||
import { MockedProvider } from 'react-apollo/test-utils';
|
||||
import { render, waitForElement } from 'react-testing-library';
|
||||
import { render } from 'react-testing-library';
|
||||
|
||||
import { mockFirstLastSeenHostQuery } from '../../../../containers/hosts/first_last_seen/mock';
|
||||
import { wait } from '../../../../lib/helpers';
|
||||
|
@ -147,22 +147,4 @@ describe('FirstLastSeen Component', async () => {
|
|||
await wait();
|
||||
expect(container.textContent).toBe('something-invalid');
|
||||
});
|
||||
|
||||
test('Show error message', async () => {
|
||||
const myErrorMock = cloneDeep(mockFirstLastSeenHostQuery);
|
||||
delete myErrorMock[0].result;
|
||||
myErrorMock[0].result = {
|
||||
errors: [{ message: 'Error!' }],
|
||||
};
|
||||
const { container } = render(
|
||||
<TestProviders>
|
||||
<MockedProvider mocks={myErrorMock} addTypename={false}>
|
||||
<FirstLastSeenHost hostname="kibana-siem" type="last-seen" />
|
||||
</MockedProvider>
|
||||
</TestProviders>
|
||||
);
|
||||
await wait();
|
||||
const alertIcon = await waitForElement(() => container.querySelectorAll('svg'), { container });
|
||||
expect(alertIcon.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -511,9 +511,7 @@ exports[`Host Summary Component rendering it renders the default Host Summary 1`
|
|||
},
|
||||
}
|
||||
}
|
||||
endDate={618472800000}
|
||||
loading={false}
|
||||
startDate={552204000000}
|
||||
/>
|
||||
</Component>
|
||||
`;
|
||||
|
|
|
@ -46,12 +46,7 @@ describe('Host Summary Component', () => {
|
|||
test('it renders the default Host Summary', () => {
|
||||
const wrapper = shallow(
|
||||
<TestProviders>
|
||||
<HostSummary
|
||||
loading={false}
|
||||
data={mockData.Hosts.edges[0].node}
|
||||
startDate={552204000000}
|
||||
endDate={618472800000}
|
||||
/>
|
||||
<HostSummary loading={false} data={mockData.Hosts.edges[0].node} />
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
|
@ -61,13 +56,7 @@ describe('Host Summary Component', () => {
|
|||
|
||||
describe('#createDraggable', () => {
|
||||
test('if it creates a draggable component', () => {
|
||||
const draggable = createDraggable(
|
||||
'debian',
|
||||
'Platform',
|
||||
552204000000,
|
||||
618472800000,
|
||||
'siem-kibana'
|
||||
);
|
||||
const draggable = createDraggable('debian', 'Platform', 'siem-kibana');
|
||||
const wrapper = mountWithIntl(<TestProviders>{draggable}</TestProviders>);
|
||||
expect(wrapper.text()).toBe('debian');
|
||||
});
|
||||
|
@ -75,7 +64,7 @@ describe('Host Summary Component', () => {
|
|||
const { container } = render(
|
||||
<TestProviders>
|
||||
<MockedProvider mocks={mockFirstLastSeenHostQuery} addTypename={false}>
|
||||
{createDraggable(null, 'firstSeen', 552204000000, 618472800000, 'kibana-siem')}
|
||||
{createDraggable(null, 'firstSeen', 'kibana-siem')}
|
||||
</MockedProvider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
@ -85,23 +74,17 @@ describe('Host Summary Component', () => {
|
|||
);
|
||||
});
|
||||
test('if it returns an empty value', () => {
|
||||
const draggable = createDraggable(
|
||||
null,
|
||||
'Platform',
|
||||
552204000000,
|
||||
618472800000,
|
||||
'siem-kibana'
|
||||
);
|
||||
const draggable = createDraggable(null, 'Platform', 'siem-kibana');
|
||||
const wrapper = mountWithIntl(<TestProviders>{draggable}</TestProviders>);
|
||||
expect(wrapper.text()).toBe(getEmptyValue());
|
||||
});
|
||||
test('if it returns a string placeholder with an empty string', () => {
|
||||
const draggable = createDraggable('', 'Platform', 552204000000, 618472800000, 'siem-kibana');
|
||||
const draggable = createDraggable('', 'Platform', 'siem-kibana');
|
||||
const wrapper = mountWithIntl(<TestProviders>{draggable}</TestProviders>);
|
||||
expect(wrapper.text()).toBe(getEmptyString());
|
||||
});
|
||||
test('if works with a string with a single space as a valid value and NOT an empty value', () => {
|
||||
const draggable = createDraggable(' ', 'Platform', 552204000000, 618472800000, 'siem-kibana');
|
||||
const draggable = createDraggable(' ', 'Platform', 'siem-kibana');
|
||||
const wrapper = mountWithIntl(<TestProviders>{draggable}</TestProviders>);
|
||||
expect(wrapper.text()).toBe(' ');
|
||||
});
|
||||
|
@ -114,7 +97,7 @@ describe('Host Summary Component', () => {
|
|||
const { container } = render(
|
||||
<TestProviders>
|
||||
<MockedProvider mocks={mockFirstLastSeenHostQuery} addTypename={false}>
|
||||
{getEuiDescriptionList(myMockData, 552204000000, 618472800000, true)}
|
||||
{getEuiDescriptionList(myMockData, true)}
|
||||
</MockedProvider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
@ -127,7 +110,7 @@ describe('Host Summary Component', () => {
|
|||
const { container } = render(
|
||||
<TestProviders>
|
||||
<MockedProvider mocks={mockFirstLastSeenHostQuery} addTypename={false}>
|
||||
{getEuiDescriptionList(myMockData, 552204000000, 618472800000, false)}
|
||||
{getEuiDescriptionList(myMockData, false)}
|
||||
</MockedProvider>
|
||||
</TestProviders>
|
||||
);
|
||||
|
@ -135,7 +118,7 @@ describe('Host Summary Component', () => {
|
|||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
test('if it creates an empty description list', () => {
|
||||
const euiDescriptionList = getEuiDescriptionList(null, 552204000000, 618472800000, false);
|
||||
const euiDescriptionList = getEuiDescriptionList(null, false);
|
||||
const wrapper = mountWithIntl(<TestProviders>{euiDescriptionList}</TestProviders>);
|
||||
expect(wrapper.text()).toBe(
|
||||
'Name--First Seen--Last Seen--Id--IP Address--MAC Addr--Type--Platform--OS Name--Family--Version--Architecture--'
|
||||
|
|
|
@ -32,13 +32,11 @@ import * as i18n from './translations';
|
|||
interface OwnProps {
|
||||
data: HostItem;
|
||||
loading: boolean;
|
||||
startDate: number;
|
||||
endDate: number;
|
||||
}
|
||||
|
||||
type HostSummaryProps = OwnProps;
|
||||
|
||||
export const HostSummary = pure<HostSummaryProps>(({ data, startDate, endDate, loading }) => (
|
||||
export const HostSummary = pure<HostSummaryProps>(({ data, loading }) => (
|
||||
<EuiFlexGroup>
|
||||
<StyledEuiFlexItem>
|
||||
<EuiPanel>
|
||||
|
@ -46,7 +44,7 @@ export const HostSummary = pure<HostSummaryProps>(({ data, startDate, endDate, l
|
|||
<h3>{i18n.SUMMARY}</h3>
|
||||
</EuiTitle>
|
||||
<EuiHorizontalRule margin="xs" />
|
||||
{getEuiDescriptionList(data, startDate, endDate, loading)}
|
||||
{getEuiDescriptionList(data, loading)}
|
||||
</EuiPanel>
|
||||
</StyledEuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
@ -67,12 +65,7 @@ const fieldTitleMapping: Readonly<Record<string, string>> = {
|
|||
'host.architecture': i18n.ARCHITECTURE,
|
||||
};
|
||||
|
||||
export const getEuiDescriptionList = (
|
||||
host: HostItem | null,
|
||||
startDate: number,
|
||||
endDate: number,
|
||||
loading: boolean
|
||||
): JSX.Element => (
|
||||
export const getEuiDescriptionList = (host: HostItem | null, loading: boolean): JSX.Element => (
|
||||
<EuiDescriptionList type="column" compressed>
|
||||
{Object.entries(fieldTitleMapping).map(([field, title]) => {
|
||||
const summaryValue: string | string[] | null = get(field, host);
|
||||
|
@ -88,11 +81,11 @@ export const getEuiDescriptionList = (
|
|||
getEmptyTagValue()
|
||||
) : (
|
||||
summaryValue.map((value: string) =>
|
||||
createDraggable(value, field, startDate, endDate, get('host.name', host))
|
||||
createDraggable(value, field, get('host.name', host))
|
||||
)
|
||||
)
|
||||
) : (
|
||||
createDraggable(summaryValue, field, startDate, endDate, get('host.name', host))
|
||||
createDraggable(summaryValue, field, get('host.name', host))
|
||||
)}
|
||||
</div>
|
||||
</React.Fragment>
|
||||
|
@ -104,8 +97,6 @@ export const getEuiDescriptionList = (
|
|||
export const createDraggable = (
|
||||
summaryValue: string | null,
|
||||
field: string,
|
||||
startDate: number,
|
||||
endDate: number,
|
||||
hostName: string | null | undefined
|
||||
) =>
|
||||
summaryValue == null && hostName != null && ['firstSeen', 'lastSeen'].includes(field) ? (
|
||||
|
@ -123,7 +114,6 @@ export const createDraggable = (
|
|||
name: summaryValue,
|
||||
kqlQuery: '',
|
||||
queryMatch: { field, value: summaryValue },
|
||||
queryDate: { from: startDate, to: endDate },
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
|
|
@ -49,7 +49,6 @@ exports[`Load More Table Component rendering it renders the default Hosts table
|
|||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="aa7ca589f1b8220002f2fc61c64cfbf1"
|
||||
startDate={1546965070707}
|
||||
totalCount={4}
|
||||
type="page"
|
||||
/>
|
||||
|
|
|
@ -24,10 +24,7 @@ import { AddToKql } from '../../add_to_kql';
|
|||
|
||||
import * as i18n from './translations';
|
||||
|
||||
export const getHostsColumns = (
|
||||
startDate: number,
|
||||
type: hostsModel.HostsType
|
||||
): Array<Columns<ValueOf<HostItem>>> => [
|
||||
export const getHostsColumns = (type: hostsModel.HostsType): Array<Columns<ValueOf<HostItem>>> => [
|
||||
{
|
||||
field: 'node.host.name',
|
||||
name: i18n.NAME,
|
||||
|
@ -48,7 +45,6 @@ export const getHostsColumns = (
|
|||
name: hostName,
|
||||
kqlQuery: '',
|
||||
queryMatch: { field: 'host.name', value: hostName },
|
||||
queryDate: { from: startDate, to: Date.now() },
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
|
|
@ -20,7 +20,6 @@ import { mockData } from './mock';
|
|||
|
||||
describe('Load More Table Component', () => {
|
||||
const loadMore = jest.fn();
|
||||
const startDate = new Date('2019-01-08T16:31:10.707Z').valueOf();
|
||||
const state: State = mockGlobalState;
|
||||
|
||||
let store = createStore(state);
|
||||
|
@ -41,7 +40,6 @@ describe('Load More Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.Hosts.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.Hosts.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</KibanaConfigContext.Provider>
|
||||
|
@ -62,7 +60,6 @@ describe('Load More Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.Hosts.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.Hosts.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -80,7 +77,6 @@ describe('Load More Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.Hosts.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.Hosts.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
|
|
@ -24,7 +24,6 @@ interface OwnProps {
|
|||
loading: boolean;
|
||||
hasNextPage: boolean;
|
||||
nextCursor: string;
|
||||
startDate: number;
|
||||
totalCount: number;
|
||||
loadMore: (cursor: string) => void;
|
||||
type: hostsModel.HostsType;
|
||||
|
@ -86,13 +85,12 @@ class HostsTableComponent extends React.PureComponent<HostsTableProps> {
|
|||
totalCount,
|
||||
nextCursor,
|
||||
updateLimitPagination,
|
||||
startDate,
|
||||
sortField,
|
||||
type,
|
||||
} = this.props;
|
||||
return (
|
||||
<LoadMoreTable
|
||||
columns={getHostsColumns(startDate, type)}
|
||||
columns={getHostsColumns(type)}
|
||||
loadingTitle={i18n.HOSTS}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export * from './authentications_table';
|
||||
export * from './events_table';
|
||||
export * from './hosts_table';
|
||||
export * from './types_bar';
|
||||
export * from './uncommon_process_table';
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`TypeBar Component rendering it renders the default TypesBar 1`] = `
|
||||
<Component
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"x": 12,
|
||||
"y": "user_login",
|
||||
},
|
||||
Object {
|
||||
"x": 7,
|
||||
"y": "user_err",
|
||||
},
|
||||
Object {
|
||||
"x": 3,
|
||||
"y": "user_start",
|
||||
},
|
||||
Object {
|
||||
"x": 1,
|
||||
"y": "cred_acq",
|
||||
},
|
||||
Object {
|
||||
"x": 1,
|
||||
"y": "cred_disp",
|
||||
},
|
||||
]
|
||||
}
|
||||
loading={false}
|
||||
/>
|
||||
`;
|
|
@ -1,22 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import toJson from 'enzyme-to-json';
|
||||
import * as React from 'react';
|
||||
|
||||
import { TypesBar } from './index';
|
||||
import { mockData } from './mock';
|
||||
|
||||
describe('TypeBar Component', () => {
|
||||
describe('rendering', () => {
|
||||
test('it renders the default TypesBar', () => {
|
||||
const wrapper = shallow(<TypesBar loading={false} data={mockData} />);
|
||||
|
||||
expect(toJson(wrapper)).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,27 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { HorizontalBarChart, HorizontalBarChartData } from '../../../horizontal_bar_chart';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
||||
interface TypesBarProps {
|
||||
data: HorizontalBarChartData[];
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export const TypesBar = pure<TypesBarProps>(({ data, loading }) => (
|
||||
<HorizontalBarChart
|
||||
loading={loading}
|
||||
title={i18n.KPI_EVENT_TYPES}
|
||||
width={490}
|
||||
height={279}
|
||||
barChartdata={data}
|
||||
/>
|
||||
));
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { HorizontalBarChartData } from '../../../horizontal_bar_chart';
|
||||
|
||||
export const mockData: HorizontalBarChartData[] = [
|
||||
{
|
||||
y: 'user_login',
|
||||
x: 12,
|
||||
},
|
||||
{
|
||||
y: 'user_err',
|
||||
x: 7,
|
||||
},
|
||||
{
|
||||
y: 'user_start',
|
||||
x: 3,
|
||||
},
|
||||
{
|
||||
y: 'cred_acq',
|
||||
x: 1,
|
||||
},
|
||||
{
|
||||
y: 'cred_disp',
|
||||
x: 1,
|
||||
},
|
||||
];
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
export const KPI_EVENT_TYPES = i18n.translate('xpack.siem.typesBar.KpiEventTypes', {
|
||||
defaultMessage: 'KPI Event Types',
|
||||
});
|
|
@ -205,7 +205,6 @@ exports[`UncommonProcess Table Component rendering it renders the default Uncomm
|
|||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="aa7ca589f1b8220002f2fc61c64cfbf1"
|
||||
startDate={1546965070707}
|
||||
totalCount={5}
|
||||
type="page"
|
||||
/>
|
||||
|
|
|
@ -18,7 +18,6 @@ import { mockData } from './mock';
|
|||
|
||||
describe('UncommonProcess Table Component', () => {
|
||||
const loadMore = jest.fn();
|
||||
const startDate = new Date('2019-01-08T16:31:10.707Z').valueOf();
|
||||
|
||||
describe('rendering', () => {
|
||||
test('it renders the default Uncommon process table', () => {
|
||||
|
@ -31,7 +30,6 @@ describe('UncommonProcess Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.UncommonProcess.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.UncommonProcess.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -50,7 +48,6 @@ describe('UncommonProcess Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.UncommonProcess.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.UncommonProcess.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -75,7 +72,6 @@ describe('UncommonProcess Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.UncommonProcess.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.UncommonProcess.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -101,7 +97,6 @@ describe('UncommonProcess Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.UncommonProcess.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.UncommonProcess.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -127,7 +122,6 @@ describe('UncommonProcess Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.UncommonProcess.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.UncommonProcess.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -152,7 +146,6 @@ describe('UncommonProcess Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.UncommonProcess.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.UncommonProcess.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={hostsModel.HostsType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
|
|
@ -29,7 +29,6 @@ interface OwnProps {
|
|||
nextCursor: string;
|
||||
totalCount: number;
|
||||
loadMore: (cursor: string) => void;
|
||||
startDate: number;
|
||||
type: hostsModel.HostsType;
|
||||
}
|
||||
|
||||
|
@ -82,11 +81,10 @@ const UncommonProcessTableComponent = pure<UncommonProcessTableProps>(
|
|||
totalCount,
|
||||
nextCursor,
|
||||
updateLimitPagination,
|
||||
startDate,
|
||||
type,
|
||||
}) => (
|
||||
<LoadMoreTable
|
||||
columns={getUncommonColumns(startDate)}
|
||||
columns={getUncommonColumns()}
|
||||
loadingTitle={i18n.UNCOMMON_PROCESSES}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
|
@ -120,7 +118,7 @@ export const UncommonProcessTable = connect(
|
|||
}
|
||||
)(UncommonProcessTableComponent);
|
||||
|
||||
const getUncommonColumns = (startDate: number): Array<Columns<UncommonProcessesEdges>> => [
|
||||
const getUncommonColumns = (): Array<Columns<UncommonProcessesEdges>> => [
|
||||
{
|
||||
name: i18n.NAME,
|
||||
truncateText: false,
|
||||
|
@ -145,10 +143,6 @@ const getUncommonColumns = (startDate: number): Array<Columns<UncommonProcessesE
|
|||
field: 'process.name',
|
||||
value: processName,
|
||||
},
|
||||
queryDate: {
|
||||
from: startDate,
|
||||
to: Date.now(),
|
||||
},
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
@ -188,10 +182,6 @@ const getUncommonColumns = (startDate: number): Array<Columns<UncommonProcessesE
|
|||
field: 'user.name',
|
||||
value: userName,
|
||||
},
|
||||
queryDate: {
|
||||
from: startDate,
|
||||
to: Date.now(),
|
||||
},
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
@ -256,7 +246,6 @@ const getUncommonColumns = (startDate: number): Array<Columns<UncommonProcessesE
|
|||
field: 'host.name',
|
||||
value: name[0],
|
||||
},
|
||||
queryDate: { from: startDate, to: Date.now() },
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
|
|
@ -63,14 +63,12 @@ exports[`Domains Table Component Rendering it renders the default Domains table
|
|||
},
|
||||
]
|
||||
}
|
||||
endDate={1549643470707}
|
||||
flowTarget="source"
|
||||
hasNextPage={false}
|
||||
ip="10.10.10.10"
|
||||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="10"
|
||||
startDate={1546965070707}
|
||||
totalCount={1}
|
||||
type="details"
|
||||
/>
|
||||
|
|
|
@ -33,8 +33,6 @@ import * as i18n from './translations';
|
|||
|
||||
export const getDomainsColumns = (
|
||||
ip: string,
|
||||
startDate: number,
|
||||
endDate: number,
|
||||
flowDirection: FlowDirection,
|
||||
flowTarget: FlowTarget,
|
||||
type: networkModel.NetworkType,
|
||||
|
@ -63,7 +61,6 @@ export const getDomainsColumns = (
|
|||
excluded: false,
|
||||
kqlQuery: '',
|
||||
queryMatch: { field: domainNameAttr, value: domainName },
|
||||
queryDate: { from: startDate, to: endDate },
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
|
|
@ -19,8 +19,6 @@ import { DomainsTable } from '.';
|
|||
import { mockDomainsData } from './mock';
|
||||
|
||||
describe('Domains Table Component', () => {
|
||||
const startDate = new Date('2019-01-08T16:31:10.707Z').valueOf();
|
||||
const endDate = new Date('2019-02-08T16:31:10.707Z').valueOf();
|
||||
const loadMore = jest.fn();
|
||||
const ip = '10.10.10.10';
|
||||
const state: State = mockGlobalState;
|
||||
|
@ -44,8 +42,6 @@ describe('Domains Table Component', () => {
|
|||
flowTarget={FlowTarget.source}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockDomainsData.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockDomainsData.pageInfo)!}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
type={networkModel.NetworkType.details}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
|
@ -69,8 +65,6 @@ describe('Domains Table Component', () => {
|
|||
flowTarget={FlowTarget.source}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockDomainsData.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockDomainsData.pageInfo)!}
|
||||
startDate={startDate}
|
||||
endDate={endDate}
|
||||
type={networkModel.NetworkType.details}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
|
|
@ -35,8 +35,6 @@ interface OwnProps {
|
|||
nextCursor: string;
|
||||
totalCount: number;
|
||||
loadMore: (cursor: string) => void;
|
||||
endDate: number;
|
||||
startDate: number;
|
||||
type: networkModel.NetworkType;
|
||||
}
|
||||
|
||||
|
@ -97,8 +95,6 @@ class DomainsTableComponent extends React.PureComponent<DomainsTableProps> {
|
|||
totalCount,
|
||||
nextCursor,
|
||||
updateDomainsLimit,
|
||||
startDate,
|
||||
endDate,
|
||||
flowDirection,
|
||||
flowTarget,
|
||||
type,
|
||||
|
@ -106,15 +102,7 @@ class DomainsTableComponent extends React.PureComponent<DomainsTableProps> {
|
|||
|
||||
return (
|
||||
<LoadMoreTable
|
||||
columns={getDomainsColumns(
|
||||
ip,
|
||||
startDate,
|
||||
endDate,
|
||||
flowDirection,
|
||||
flowTarget,
|
||||
type,
|
||||
DomainsTableId
|
||||
)}
|
||||
columns={getDomainsColumns(ip, flowDirection, flowTarget, type, DomainsTableId)}
|
||||
loadingTitle={i18n.DOMAINS}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
import { cloneDeep } from 'lodash/fp';
|
||||
import * as React from 'react';
|
||||
import { MockedProvider } from 'react-apollo/test-utils';
|
||||
import { render, waitForElement } from 'react-testing-library';
|
||||
import { render } from 'react-testing-library';
|
||||
|
||||
import { mockFirstLastSeenDomainQuery } from '../../../../containers/domains/first_last_seen_domain/mock';
|
||||
import { FlowTarget } from '../../../../graphql/types';
|
||||
|
@ -184,27 +184,4 @@ describe('FirstLastSeen Component', async () => {
|
|||
await wait();
|
||||
expect(container.textContent).toBe('something-invalid');
|
||||
});
|
||||
|
||||
test('Show error message', async () => {
|
||||
const myErrorMock = cloneDeep(mockFirstLastSeenDomainQuery);
|
||||
delete myErrorMock[0].result;
|
||||
myErrorMock[0].result = {
|
||||
errors: [{ message: 'Error!' }],
|
||||
};
|
||||
const { container } = render(
|
||||
<TestProviders>
|
||||
<MockedProvider mocks={myErrorMock} addTypename={false}>
|
||||
<FirstLastSeenDomain
|
||||
ip={ip}
|
||||
domainName={domainName}
|
||||
flowTarget={FlowTarget.source}
|
||||
type="last-seen"
|
||||
/>
|
||||
</MockedProvider>
|
||||
</TestProviders>
|
||||
);
|
||||
await wait();
|
||||
const alertIcon = await waitForElement(() => container.querySelectorAll('svg'), { container });
|
||||
expect(alertIcon.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -140,7 +140,6 @@ exports[`NetworkTopNFlow Table Component rendering it renders the default Networ
|
|||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="10"
|
||||
startDate={1546965070707}
|
||||
totalCount={80}
|
||||
type="page"
|
||||
/>
|
||||
|
|
|
@ -20,7 +20,6 @@ import { Provider } from '../../../timeline/data_providers/provider';
|
|||
import * as i18n from './translations';
|
||||
|
||||
export const getNetworkDnsColumns = (
|
||||
startDate: number,
|
||||
type: networkModel.NetworkType
|
||||
): Array<Columns<ValueOf<NetworkDnsItem>>> => [
|
||||
{
|
||||
|
@ -43,7 +42,6 @@ export const getNetworkDnsColumns = (
|
|||
excluded: false,
|
||||
kqlQuery: '',
|
||||
queryMatch: { field: 'dns.question.etld_plus_one', value: escapeQueryValue(dnsName) },
|
||||
queryDate: { from: startDate, to: Date.now() },
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
|
|
@ -18,7 +18,6 @@ import { NetworkDnsTable } from '.';
|
|||
import { mockData } from './mock';
|
||||
|
||||
describe('NetworkTopNFlow Table Component', () => {
|
||||
const startDate = new Date('2019-01-08T16:31:10.707Z').valueOf();
|
||||
const loadMore = jest.fn();
|
||||
const state: State = mockGlobalState;
|
||||
|
||||
|
@ -39,7 +38,6 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.NetworkDns.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.NetworkDns.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={networkModel.NetworkType.page}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
|
@ -61,7 +59,6 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.NetworkDns.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.NetworkDns.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={networkModel.NetworkType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
|
|
@ -26,7 +26,6 @@ interface OwnProps {
|
|||
hasNextPage: boolean;
|
||||
nextCursor: string;
|
||||
loadMore: (cursor: string) => void;
|
||||
startDate: number;
|
||||
totalCount: number;
|
||||
type: networkModel.NetworkType;
|
||||
}
|
||||
|
@ -89,14 +88,13 @@ class NetworkDnsTableComponent extends React.PureComponent<NetworkDnsTableProps>
|
|||
loading,
|
||||
loadMore,
|
||||
nextCursor,
|
||||
startDate,
|
||||
totalCount,
|
||||
type,
|
||||
updateDnsLimit,
|
||||
} = this.props;
|
||||
return (
|
||||
<LoadMoreTable
|
||||
columns={getNetworkDnsColumns(startDate, type)}
|
||||
columns={getNetworkDnsColumns(type)}
|
||||
loadingTitle={i18n.TOP_DNS_DOMAINS}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
|
|
|
@ -55,7 +55,6 @@ exports[`NetworkTopNFlow Table Component rendering it renders the default Networ
|
|||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="10"
|
||||
startDate={1546965070707}
|
||||
totalCount={524}
|
||||
type="page"
|
||||
/>
|
||||
|
|
|
@ -31,7 +31,6 @@ import { AddToKql } from '../../add_to_kql';
|
|||
import * as i18n from './translations';
|
||||
|
||||
export const getNetworkTopNFlowColumns = (
|
||||
startDate: number,
|
||||
flowDirection: FlowDirection,
|
||||
flowTarget: FlowTarget,
|
||||
type: networkModel.NetworkType,
|
||||
|
@ -57,7 +56,6 @@ export const getNetworkTopNFlowColumns = (
|
|||
excluded: false,
|
||||
kqlQuery: '',
|
||||
queryMatch: { field: ipAttr, value: ip },
|
||||
queryDate: { from: startDate, to: Date.now() },
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
@ -101,7 +99,6 @@ export const getNetworkTopNFlowColumns = (
|
|||
excluded: false,
|
||||
kqlQuery: '',
|
||||
queryMatch: { field: domainAttr, value: domain },
|
||||
queryDate: { from: startDate, to: Date.now() },
|
||||
}}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
|
|
|
@ -19,7 +19,6 @@ import { NetworkTopNFlowTable, NetworkTopNFlowTableId } from '.';
|
|||
import { mockData } from './mock';
|
||||
|
||||
describe('NetworkTopNFlow Table Component', () => {
|
||||
const startDate = new Date('2019-01-08T16:31:10.707Z').valueOf();
|
||||
const loadMore = jest.fn();
|
||||
const state: State = mockGlobalState;
|
||||
|
||||
|
@ -40,7 +39,6 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.NetworkTopNFlow.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.NetworkTopNFlow.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={networkModel.NetworkType.page}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
|
@ -66,7 +64,6 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.NetworkTopNFlow.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.NetworkTopNFlow.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={networkModel.NetworkType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -100,7 +97,6 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.NetworkTopNFlow.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.NetworkTopNFlow.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={networkModel.NetworkType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
@ -141,7 +137,6 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
hasNextPage={getOr(false, 'hasNextPage', mockData.NetworkTopNFlow.pageInfo)!}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.NetworkTopNFlow.pageInfo)!}
|
||||
loadMore={loadMore}
|
||||
startDate={startDate}
|
||||
type={networkModel.NetworkType.page}
|
||||
/>
|
||||
</TestProviders>
|
||||
|
|
|
@ -34,7 +34,6 @@ interface OwnProps {
|
|||
nextCursor: string;
|
||||
totalCount: number;
|
||||
loadMore: (cursor: string) => void;
|
||||
startDate: number;
|
||||
type: networkModel.NetworkType;
|
||||
}
|
||||
|
||||
|
@ -99,7 +98,6 @@ class NetworkTopNFlowTableComponent extends React.PureComponent<NetworkTopNFlowT
|
|||
totalCount,
|
||||
nextCursor,
|
||||
updateTopNFlowLimit,
|
||||
startDate,
|
||||
flowDirection,
|
||||
topNFlowSort,
|
||||
flowTarget,
|
||||
|
@ -114,13 +112,7 @@ class NetworkTopNFlowTableComponent extends React.PureComponent<NetworkTopNFlowT
|
|||
|
||||
return (
|
||||
<LoadMoreTable
|
||||
columns={getNetworkTopNFlowColumns(
|
||||
startDate,
|
||||
flowDirection,
|
||||
flowTarget,
|
||||
type,
|
||||
NetworkTopNFlowTableId
|
||||
)}
|
||||
columns={getNetworkTopNFlowColumns(flowDirection, flowTarget, type, NetworkTopNFlowTableId)}
|
||||
loadingTitle={i18n.TOP_TALKERS}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
|
||||
import { manageQuery } from '../../../../components/page/manage_query';
|
||||
import { OverviewHostQuery } from '../../../../containers/overview/overview_host';
|
||||
|
@ -23,15 +22,16 @@ import { inputsModel } from '../../../../store/inputs';
|
|||
import { OverviewHostStats } from '../overview_host_stats';
|
||||
|
||||
export interface OwnProps {
|
||||
poll: number;
|
||||
startDate: number;
|
||||
endDate: number;
|
||||
setQuery: ActionCreator<{ id: string; loading: boolean; refetch: inputsModel.Refetch }>;
|
||||
setQuery: (
|
||||
{ id, loading, refetch }: { id: string; loading: boolean; refetch: inputsModel.Refetch }
|
||||
) => void;
|
||||
}
|
||||
|
||||
const OverviewHostStatsManage = manageQuery(OverviewHostStats);
|
||||
type OverviewHostProps = OwnProps;
|
||||
export const OverviewHost = pure<OverviewHostProps>(({ endDate, poll, startDate, setQuery }) => (
|
||||
export const OverviewHost = pure<OverviewHostProps>(({ endDate, startDate, setQuery }) => (
|
||||
<EuiFlexItem>
|
||||
<EuiPanel>
|
||||
<EuiFlexGroup alignItems="center">
|
||||
|
@ -55,7 +55,7 @@ export const OverviewHost = pure<OverviewHostProps>(({ endDate, poll, startDate,
|
|||
|
||||
<EuiHorizontalRule />
|
||||
|
||||
<OverviewHostQuery endDate={endDate} poll={poll} sourceId="default" startDate={startDate}>
|
||||
<OverviewHostQuery endDate={endDate} sourceId="default" startDate={startDate}>
|
||||
{({ overviewHost, loading, id, refetch }) => (
|
||||
<OverviewHostStatsManage
|
||||
loading={loading}
|
||||
|
|
|
@ -15,7 +15,6 @@ import {
|
|||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
|
||||
import { manageQuery } from '../../../../components/page/manage_query';
|
||||
import { OverviewNetworkQuery } from '../../../../containers/overview/overview_network';
|
||||
|
@ -23,15 +22,16 @@ import { inputsModel } from '../../../../store/inputs';
|
|||
import { OverviewNetworkStats } from '../overview_network_stats';
|
||||
|
||||
export interface OwnProps {
|
||||
poll: number;
|
||||
startDate: number;
|
||||
endDate: number;
|
||||
setQuery: ActionCreator<{ id: string; loading: boolean; refetch: inputsModel.Refetch }>;
|
||||
setQuery: (
|
||||
{ id, loading, refetch }: { id: string; loading: boolean; refetch: inputsModel.Refetch }
|
||||
) => void;
|
||||
}
|
||||
|
||||
const OverviewNetworkStatsManage = manageQuery(OverviewNetworkStats);
|
||||
|
||||
export const OverviewNetwork = pure<OwnProps>(({ endDate, poll, startDate, setQuery }) => (
|
||||
export const OverviewNetwork = pure<OwnProps>(({ endDate, startDate, setQuery }) => (
|
||||
<EuiFlexItem>
|
||||
<EuiPanel>
|
||||
<EuiFlexGroup alignItems="center">
|
||||
|
@ -58,7 +58,7 @@ export const OverviewNetwork = pure<OwnProps>(({ endDate, poll, startDate, setQu
|
|||
|
||||
<EuiHorizontalRule />
|
||||
|
||||
<OverviewNetworkQuery endDate={endDate} poll={poll} sourceId="default" startDate={startDate}>
|
||||
<OverviewNetworkQuery endDate={endDate} sourceId="default" startDate={startDate}>
|
||||
{({ overviewNetwork, loading, id, refetch }) => (
|
||||
<OverviewNetworkStatsManage
|
||||
loading={loading}
|
||||
|
|
|
@ -6,8 +6,9 @@
|
|||
|
||||
import { mount } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
|
||||
import { mockGlobalState, TestProviders } from '../../mock';
|
||||
import { mockGlobalState } from '../../mock';
|
||||
import { createStore, State } from '../../store';
|
||||
|
||||
import { SuperDatePicker } from '.';
|
||||
|
@ -18,20 +19,21 @@ describe('SIEM Super Date Picker', () => {
|
|||
let store = createStore(state);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
store = createStore(state);
|
||||
});
|
||||
|
||||
describe('Pick Relative Date', () => {
|
||||
let wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<SuperDatePicker id="global" />
|
||||
</TestProviders>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<SuperDatePicker id="global" />
|
||||
</TestProviders>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
wrapper
|
||||
.find('[data-test-subj="superDatePickerToggleQuickMenuButton"]')
|
||||
|
@ -112,15 +114,15 @@ describe('SIEM Super Date Picker', () => {
|
|||
|
||||
describe('Recently used date ranges', () => {
|
||||
let wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<SuperDatePicker id="global" />
|
||||
</TestProviders>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<SuperDatePicker id="global" />
|
||||
</TestProviders>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
wrapper
|
||||
.find('[data-test-subj="superDatePickerToggleQuickMenuButton"]')
|
||||
|
@ -243,15 +245,15 @@ describe('SIEM Super Date Picker', () => {
|
|||
|
||||
describe('Refresh Every', () => {
|
||||
let wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<SuperDatePicker id="global" />
|
||||
</TestProviders>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<SuperDatePicker id="global" />
|
||||
</TestProviders>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
wrapper
|
||||
.find('[data-test-subj="superDatePickerToggleQuickMenuButton"]')
|
||||
|
@ -299,15 +301,15 @@ describe('SIEM Super Date Picker', () => {
|
|||
|
||||
describe('Pick Absolute Date', () => {
|
||||
let wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<SuperDatePicker id="global" />
|
||||
</TestProviders>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
beforeEach(() => {
|
||||
wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<SuperDatePicker id="global" />
|
||||
</TestProviders>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
wrapper
|
||||
.find('[data-test-subj="superDatePickerShowDatesButton"]')
|
||||
|
|
|
@ -50,20 +50,24 @@ interface SuperDatePickerStateRedux {
|
|||
}
|
||||
|
||||
interface SuperDatePickerDispatchProps {
|
||||
setAbsoluteSuperDatePicker: ActionCreator<{ id: string; from: number; to: number }>;
|
||||
setAbsoluteSuperDatePicker: ActionCreator<{
|
||||
id: inputsModel.InputsModelId;
|
||||
from: number;
|
||||
to: number;
|
||||
}>;
|
||||
setRelativeSuperDatePicker: ActionCreator<{
|
||||
id: string;
|
||||
id: inputsModel.InputsModelId;
|
||||
fromStr: string;
|
||||
from: number;
|
||||
to: number;
|
||||
toStr: string;
|
||||
}>;
|
||||
startAutoReload: ActionCreator<{ id: string }>;
|
||||
stopAutoReload: ActionCreator<{ id: string }>;
|
||||
setDuration: ActionCreator<{ id: string; duration: number }>;
|
||||
startAutoReload: ActionCreator<{ id: inputsModel.InputsModelId }>;
|
||||
stopAutoReload: ActionCreator<{ id: inputsModel.InputsModelId }>;
|
||||
setDuration: ActionCreator<{ id: inputsModel.InputsModelId; duration: number }>;
|
||||
}
|
||||
interface OwnProps {
|
||||
id: string;
|
||||
id: inputsModel.InputsModelId;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
|
@ -77,11 +81,8 @@ export type SuperDatePickerProps = OwnProps &
|
|||
SuperDatePickerStateRedux;
|
||||
|
||||
export interface SuperDatePickerState {
|
||||
isAutoRefreshOnly: boolean;
|
||||
isPaused: boolean;
|
||||
isQuickSelection: boolean;
|
||||
recentlyUsedRanges: TimeArgs[];
|
||||
refreshInterval: number;
|
||||
showUpdateButton: boolean;
|
||||
}
|
||||
|
||||
|
@ -93,17 +94,14 @@ export const SuperDatePickerComponent = class extends Component<
|
|||
super(props);
|
||||
|
||||
this.state = {
|
||||
isAutoRefreshOnly: false,
|
||||
isPaused: true,
|
||||
isQuickSelection: true,
|
||||
recentlyUsedRanges: [],
|
||||
refreshInterval: 300000,
|
||||
showUpdateButton: true,
|
||||
};
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { end, start, kind, fromStr, toStr, isLoading } = this.props;
|
||||
const { duration, end, start, kind, fromStr, policy, toStr, isLoading } = this.props;
|
||||
const endDate = kind === 'relative' ? toStr : new Date(end).toISOString();
|
||||
const startDate = kind === 'relative' ? fromStr : new Date(start).toISOString();
|
||||
|
||||
|
@ -111,12 +109,12 @@ export const SuperDatePickerComponent = class extends Component<
|
|||
<MyEuiSuperDatePicker
|
||||
end={endDate}
|
||||
isLoading={isLoading}
|
||||
isPaused={this.state.isPaused}
|
||||
isPaused={policy === 'manual'}
|
||||
onTimeChange={this.onTimeChange}
|
||||
onRefreshChange={this.onRefreshChange}
|
||||
onRefresh={this.onRefresh}
|
||||
recentlyUsedRanges={this.state.recentlyUsedRanges}
|
||||
refreshInterval={this.state.refreshInterval}
|
||||
refreshInterval={duration}
|
||||
showUpdateButton={this.state.showUpdateButton}
|
||||
start={startDate}
|
||||
/>
|
||||
|
@ -129,7 +127,16 @@ export const SuperDatePickerComponent = class extends Component<
|
|||
isQuickSelection: this.state.isQuickSelection,
|
||||
isInvalid: false,
|
||||
});
|
||||
this.refetchQuery(this.props.refetch);
|
||||
const currentStart = this.formatDate(start);
|
||||
const currentEnd = this.state.isQuickSelection
|
||||
? this.formatDate(end, { roundUp: true })
|
||||
: this.formatDate(end);
|
||||
if (
|
||||
!this.state.isQuickSelection ||
|
||||
(this.props.start === currentStart && this.props.end === currentEnd)
|
||||
) {
|
||||
this.refetchQuery(this.props.refetch);
|
||||
}
|
||||
};
|
||||
|
||||
private onRefreshChange = ({ isPaused, refreshInterval }: OnRefreshChangeProps): void => {
|
||||
|
@ -151,11 +158,6 @@ export const SuperDatePickerComponent = class extends Component<
|
|||
) {
|
||||
this.refetchQuery(this.props.refetch);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
...this.state,
|
||||
isPaused,
|
||||
});
|
||||
};
|
||||
|
||||
private refetchQuery = (query: inputsModel.Refetch[]) => {
|
||||
|
|
|
@ -469,10 +469,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 1",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 1",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 1",
|
||||
|
@ -485,10 +481,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 2",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 2",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 2",
|
||||
|
@ -501,10 +493,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 3",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 3",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 3",
|
||||
|
@ -517,10 +505,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 4",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 4",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 4",
|
||||
|
@ -533,10 +517,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 5",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 5",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 5",
|
||||
|
@ -549,10 +529,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 6",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 6",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 6",
|
||||
|
@ -565,10 +541,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 7",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 7",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 7",
|
||||
|
@ -581,10 +553,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 8",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 8",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 8",
|
||||
|
@ -597,10 +565,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 9",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 9",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 9",
|
||||
|
@ -613,10 +577,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"id": "id-Provider 10",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 10",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 10",
|
||||
|
@ -624,6 +584,7 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
},
|
||||
]
|
||||
}
|
||||
end={1521862432253}
|
||||
flyoutHeaderHeight={48}
|
||||
flyoutHeight={980}
|
||||
id="foo"
|
||||
|
@ -712,6 +673,7 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"title": "filebeat-*,auditbeat-*,packetbeat-*",
|
||||
}
|
||||
}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={
|
||||
Array [
|
||||
|
@ -735,5 +697,6 @@ Can be one or multiple IPv4 or IPv6 addresses.",
|
|||
"sortDirection": "desc",
|
||||
}
|
||||
}
|
||||
start={1521830963132}
|
||||
/>
|
||||
`;
|
||||
|
|
|
@ -11,10 +11,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 1",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 1",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 1",
|
||||
|
@ -27,10 +23,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 2",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 2",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 2",
|
||||
|
@ -43,10 +35,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 3",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 3",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 3",
|
||||
|
@ -59,10 +47,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 4",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 4",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 4",
|
||||
|
@ -75,10 +59,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 5",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 5",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 5",
|
||||
|
@ -91,10 +71,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 6",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 6",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 6",
|
||||
|
@ -107,10 +83,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 7",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 7",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 7",
|
||||
|
@ -123,10 +95,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 8",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 8",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 8",
|
||||
|
@ -139,10 +107,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 9",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 9",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 9",
|
||||
|
@ -155,10 +119,6 @@ exports[`DataProviders rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 10",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 10",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 10",
|
||||
|
|
|
@ -10,10 +10,6 @@ exports[`Provider rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 1",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 1",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 1",
|
||||
|
|
|
@ -14,10 +14,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 1",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 1",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 1",
|
||||
|
@ -30,10 +26,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 2",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 2",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 2",
|
||||
|
@ -46,10 +38,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 3",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 3",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 3",
|
||||
|
@ -62,10 +50,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 4",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 4",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 4",
|
||||
|
@ -78,10 +62,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 5",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 5",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 5",
|
||||
|
@ -94,10 +74,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 6",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 6",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 6",
|
||||
|
@ -110,10 +86,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 7",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 7",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 7",
|
||||
|
@ -126,10 +98,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 8",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 8",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 8",
|
||||
|
@ -142,10 +110,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 9",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 9",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 9",
|
||||
|
@ -158,10 +122,6 @@ exports[`Providers rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 10",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 10",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 10",
|
||||
|
|
|
@ -4,11 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
export interface QueryDate {
|
||||
from: number;
|
||||
to: number;
|
||||
}
|
||||
|
||||
/** Represents the Timeline data providers */
|
||||
export interface DataProvider {
|
||||
/** Uniquely identifies a data provider */
|
||||
|
@ -38,7 +33,6 @@ export interface DataProvider {
|
|||
value: string | number;
|
||||
displayValue?: string | number;
|
||||
};
|
||||
queryDate?: QueryDate;
|
||||
/**
|
||||
* Additional query clauses that are ANDed with this query to narrow results
|
||||
*/
|
||||
|
|
|
@ -49,10 +49,6 @@ export const mockDataProviders: DataProvider[] = Object.keys(mockSourceNameToEve
|
|||
field: 'name',
|
||||
value: name,
|
||||
},
|
||||
queryDate: {
|
||||
from: 1521830963132,
|
||||
to: 1521862432253,
|
||||
},
|
||||
and: [],
|
||||
})
|
||||
);
|
||||
|
|
|
@ -23,7 +23,6 @@ export const Provider = pure<OwnProps>(({ dataProvider }) => (
|
|||
isEnabled={dataProvider.enabled}
|
||||
isExcluded={dataProvider.excluded}
|
||||
providerId={dataProvider.id}
|
||||
queryDate={dataProvider.queryDate}
|
||||
toggleEnabledProvider={noop}
|
||||
toggleExcludedProvider={noop}
|
||||
val={dataProvider.queryMatch.displayValue || dataProvider.queryMatch.value}
|
||||
|
|
|
@ -4,15 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiBadge, EuiIcon, EuiToolTip } from '@elastic/eui';
|
||||
import { EuiBadge } from '@elastic/eui';
|
||||
import classNames from 'classnames';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { QueryDate } from './data_provider';
|
||||
import * as i18n from './translations';
|
||||
|
||||
const ProviderBadgeStyled = styled(EuiBadge)`
|
||||
|
@ -43,13 +40,12 @@ interface ProviderBadgeProps {
|
|||
isEnabled: boolean;
|
||||
isExcluded: boolean;
|
||||
providerId: string;
|
||||
queryDate?: QueryDate;
|
||||
togglePopover?: () => void;
|
||||
val: string | number;
|
||||
}
|
||||
|
||||
export const ProviderBadge = pure<ProviderBadgeProps>(
|
||||
({ deleteProvider, field, isEnabled, isExcluded, queryDate, providerId, togglePopover, val }) => {
|
||||
({ deleteProvider, field, isEnabled, isExcluded, providerId, togglePopover, val }) => {
|
||||
const deleteFilter: React.MouseEventHandler<HTMLButtonElement> = (
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) => {
|
||||
|
@ -67,10 +63,6 @@ export const ProviderBadge = pure<ProviderBadgeProps>(
|
|||
|
||||
const title = `${field}: "${val}"`;
|
||||
|
||||
const tooltipStr = isEmpty(queryDate)
|
||||
? null
|
||||
: `${moment(queryDate!.from).format('L LTS')} - ${moment(queryDate!.to).format('L LTS')}`;
|
||||
|
||||
return (
|
||||
<ProviderBadgeStyled
|
||||
id={`${providerId}-${field}-${val}`}
|
||||
|
@ -90,11 +82,6 @@ export const ProviderBadge = pure<ProviderBadgeProps>(
|
|||
}}
|
||||
data-test-subj="providerBadge"
|
||||
>
|
||||
{tooltipStr !== null && (
|
||||
<EuiToolTip data-test-subj="add-tool-tip" content={tooltipStr} position="bottom">
|
||||
<EuiIcon type="calendar" />
|
||||
</EuiToolTip>
|
||||
)}
|
||||
{prefix}
|
||||
<span className="field-value">{field}: </span>
|
||||
<span className="field-value">"{val}"</span>
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import React, { PureComponent } from 'react';
|
||||
|
||||
import { QueryDate } from './data_provider';
|
||||
import { ProviderBadge } from './provider_badge';
|
||||
import { ProviderItemActions } from './provider_item_actions';
|
||||
|
||||
|
@ -17,7 +16,6 @@ interface ProviderItemBadgeProps {
|
|||
isEnabled: boolean;
|
||||
isExcluded: boolean;
|
||||
providerId: string;
|
||||
queryDate?: QueryDate;
|
||||
toggleEnabledProvider: () => void;
|
||||
toggleExcludedProvider: () => void;
|
||||
val: string | number;
|
||||
|
@ -33,16 +31,7 @@ export class ProviderItemBadge extends PureComponent<ProviderItemBadgeProps, Own
|
|||
};
|
||||
|
||||
public render() {
|
||||
const {
|
||||
deleteProvider,
|
||||
field,
|
||||
kqlQuery,
|
||||
isEnabled,
|
||||
isExcluded,
|
||||
queryDate,
|
||||
providerId,
|
||||
val,
|
||||
} = this.props;
|
||||
const { deleteProvider, field, kqlQuery, isEnabled, isExcluded, providerId, val } = this.props;
|
||||
|
||||
const badge = (
|
||||
<ProviderBadge
|
||||
|
@ -52,7 +41,6 @@ export class ProviderItemBadge extends PureComponent<ProviderItemBadgeProps, Own
|
|||
isEnabled={isEnabled}
|
||||
isExcluded={isExcluded}
|
||||
providerId={providerId}
|
||||
queryDate={queryDate}
|
||||
togglePopover={this.togglePopover}
|
||||
val={val}
|
||||
/>
|
||||
|
|
|
@ -149,7 +149,6 @@ export const Providers = pure<Props>(
|
|||
toggleEnabledProvider={toggleEnabledProvider}
|
||||
toggleExcludedProvider={toggleExcludedProvider}
|
||||
providerId={dataProvider.id}
|
||||
queryDate={dataProvider.queryDate}
|
||||
val={
|
||||
dataProvider.queryMatch.displayValue || dataProvider.queryMatch.value
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ describe('Footer Timeline Component', () => {
|
|||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Events.pageInfo)!}
|
||||
height={100}
|
||||
isLive={false}
|
||||
isLoading={false}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={2}
|
||||
|
@ -49,6 +50,7 @@ describe('Footer Timeline Component', () => {
|
|||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={false}
|
||||
height={100}
|
||||
isLive={false}
|
||||
isLoading={true}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={2}
|
||||
|
@ -72,6 +74,7 @@ describe('Footer Timeline Component', () => {
|
|||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Events.pageInfo)!}
|
||||
height={100}
|
||||
isLive={false}
|
||||
isLoading={false}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={2}
|
||||
|
@ -96,6 +99,7 @@ describe('Footer Timeline Component', () => {
|
|||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Events.pageInfo)!}
|
||||
height={100}
|
||||
isLive={false}
|
||||
isLoading={true}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={2}
|
||||
|
@ -129,6 +133,7 @@ describe('Footer Timeline Component', () => {
|
|||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={false}
|
||||
height={100}
|
||||
isLive={false}
|
||||
isLoading={true}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={2}
|
||||
|
@ -152,6 +157,7 @@ describe('Footer Timeline Component', () => {
|
|||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Events.pageInfo)!}
|
||||
height={100}
|
||||
isLive={false}
|
||||
isLoading={false}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={1}
|
||||
|
@ -182,6 +188,7 @@ describe('Footer Timeline Component', () => {
|
|||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Events.pageInfo)!}
|
||||
height={100}
|
||||
isLive={false}
|
||||
isLoading={false}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={2}
|
||||
|
@ -211,6 +218,7 @@ describe('Footer Timeline Component', () => {
|
|||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Events.pageInfo)!}
|
||||
height={100}
|
||||
isLive={false}
|
||||
isLoading={false}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={1}
|
||||
|
@ -236,5 +244,57 @@ describe('Footer Timeline Component', () => {
|
|||
.simulate('click');
|
||||
expect(onChangeItemsPerPage).toBeCalled();
|
||||
});
|
||||
|
||||
test('it does render the auto-refresh message instead of load more button when stream live is on', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<Footer
|
||||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Events.pageInfo)!}
|
||||
height={100}
|
||||
isLive={true}
|
||||
isLoading={false}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={2}
|
||||
itemsPerPageOptions={[1, 5, 10, 20]}
|
||||
onChangeItemsPerPage={onChangeItemsPerPage}
|
||||
onLoadMore={loadMore}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!}
|
||||
tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)!}
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
width={width}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="paging-control"]').exists()).toBeFalsy();
|
||||
expect(wrapper.find('[data-test-subj="is-live-on-message"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
||||
test('it does render the load more button when stream live is off', () => {
|
||||
const wrapper = mount(
|
||||
<TestProviders>
|
||||
<Footer
|
||||
serverSideEventCount={mockData.Events.totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', mockData.Events.pageInfo)!}
|
||||
height={100}
|
||||
isLive={false}
|
||||
isLoading={false}
|
||||
itemsCount={mockData.Events.edges.length}
|
||||
itemsPerPage={2}
|
||||
itemsPerPageOptions={[1, 5, 10, 20]}
|
||||
onChangeItemsPerPage={onChangeItemsPerPage}
|
||||
onLoadMore={loadMore}
|
||||
nextCursor={getOr(null, 'endCursor.value', mockData.Events.pageInfo)!}
|
||||
tieBreaker={getOr(null, 'endCursor.tiebreaker', mockData.Events.pageInfo)!}
|
||||
getUpdatedAt={getUpdatedAt}
|
||||
width={width}
|
||||
/>
|
||||
</TestProviders>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="paging-control"]').exists()).toBeTruthy();
|
||||
expect(wrapper.find('[data-test-subj="is-live-on-message"]').exists()).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -12,9 +12,12 @@ import {
|
|||
EuiContextMenuPanel,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiIconTip,
|
||||
EuiPopover,
|
||||
EuiText,
|
||||
EuiToolTip,
|
||||
} from '@elastic/eui';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import * as React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
|
@ -61,6 +64,7 @@ export const isCompactFooter = (width: number): boolean => width < 600;
|
|||
|
||||
interface FooterProps {
|
||||
itemsCount: number;
|
||||
isLive: boolean;
|
||||
isLoading: boolean;
|
||||
itemsPerPage: number;
|
||||
itemsPerPageOptions: number[];
|
||||
|
@ -173,6 +177,7 @@ export class Footer extends React.PureComponent<FooterProps, FooterState> {
|
|||
public render() {
|
||||
const {
|
||||
height,
|
||||
isLive,
|
||||
isLoading,
|
||||
itemsCount,
|
||||
itemsPerPage,
|
||||
|
@ -247,12 +252,32 @@ export class Footer extends React.PureComponent<FooterProps, FooterState> {
|
|||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem data-test-subj="paging-control-container" grow={false}>
|
||||
<PagingControl
|
||||
data-test-subj="paging-control"
|
||||
hasNextPage={hasNextPage}
|
||||
isLoading={isLoading}
|
||||
loadMore={this.loadMore}
|
||||
/>
|
||||
{isLive ? (
|
||||
<EuiText size="s" data-test-subj="is-live-on-message">
|
||||
<b>
|
||||
{i18n.AUTO_REFRESH_ACTIVE}
|
||||
<EuiIconTip
|
||||
content={
|
||||
<FormattedMessage
|
||||
id="xpack.siem.footer.autoRefreshActiveTooltip"
|
||||
defaultMessage="While auto-refresh is enabled, timeline will show you the latest {numberOfItems} events that match your query."
|
||||
values={{
|
||||
numberOfItems: itemsCount,
|
||||
}}
|
||||
/>
|
||||
}
|
||||
position="top"
|
||||
/>
|
||||
</b>
|
||||
</EuiText>
|
||||
) : (
|
||||
<PagingControl
|
||||
data-test-subj="paging-control"
|
||||
hasNextPage={hasNextPage}
|
||||
isLoading={isLoading}
|
||||
loadMore={this.loadMore}
|
||||
/>
|
||||
)}
|
||||
</EuiFlexItem>
|
||||
|
||||
<EuiFlexItem data-test-subj="last-updated-container" grow={false}>
|
||||
|
|
|
@ -37,3 +37,10 @@ export const TOTAL_COUNT_OF_EVENTS = i18n.translate('xpack.siem.footer.totalCoun
|
|||
export const UPDATED = i18n.translate('xpack.siem.footer.updated', {
|
||||
defaultMessage: 'Updated',
|
||||
});
|
||||
|
||||
export const AUTO_REFRESH_ACTIVE = i18n.translate(
|
||||
'xpack.siem.footer.autoRefreshActiveDescription',
|
||||
{
|
||||
defaultMessage: 'Auto-Refresh Active',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -11,10 +11,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 1",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 1",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 1",
|
||||
|
@ -27,10 +23,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 2",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 2",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 2",
|
||||
|
@ -43,10 +35,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 3",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 3",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 3",
|
||||
|
@ -59,10 +47,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 4",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 4",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 4",
|
||||
|
@ -75,10 +59,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 5",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 5",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 5",
|
||||
|
@ -91,10 +71,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 6",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 6",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 6",
|
||||
|
@ -107,10 +83,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 7",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 7",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 7",
|
||||
|
@ -123,10 +95,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 8",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 8",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 8",
|
||||
|
@ -139,10 +107,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 9",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 9",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 9",
|
||||
|
@ -155,10 +119,6 @@ exports[`Header rendering renders correctly against snapshot 1`] = `
|
|||
"id": "id-Provider 10",
|
||||
"kqlQuery": "",
|
||||
"name": "Provider 10",
|
||||
"queryDate": Object {
|
||||
"from": 1521830963132,
|
||||
"to": 1521862432253,
|
||||
},
|
||||
"queryMatch": Object {
|
||||
"field": "name",
|
||||
"value": "Provider 10",
|
||||
|
|
|
@ -12,31 +12,27 @@ import { mockDataProviders } from './data_providers/mock/mock_data_providers';
|
|||
import { buildGlobalQuery, combineQueries } from './helpers';
|
||||
|
||||
const cleanUpKqlQuery = (str: string) => str.replace(/\n/g, '').replace(/\s\s+/g, ' ');
|
||||
const startDate = new Date('2018-03-23T18:49:23.132Z').valueOf();
|
||||
const endDate = new Date('2018-03-24T03:33:52.253Z').valueOf();
|
||||
|
||||
describe('Build KQL Query', () => {
|
||||
test('Buld KQL query with one data provider', () => {
|
||||
const dataProviders = mockDataProviders.slice(0, 1);
|
||||
const kqlQuery = buildGlobalQuery(dataProviders);
|
||||
expect(cleanUpKqlQuery(kqlQuery)).toEqual(
|
||||
'( name : Provider 1 and @timestamp >= 1521830963132 and @timestamp <= 1521862432253 )'
|
||||
);
|
||||
expect(cleanUpKqlQuery(kqlQuery)).toEqual('( name : Provider 1 )');
|
||||
});
|
||||
|
||||
test('Buld KQL query with two data provider', () => {
|
||||
const dataProviders = mockDataProviders.slice(0, 2);
|
||||
const kqlQuery = buildGlobalQuery(dataProviders);
|
||||
expect(cleanUpKqlQuery(kqlQuery)).toEqual(
|
||||
'( name : Provider 1 and @timestamp >= 1521830963132 and @timestamp <= 1521862432253 ) or ( name : Provider 2 and @timestamp >= 1521830963132 and @timestamp <= 1521862432253 )'
|
||||
);
|
||||
expect(cleanUpKqlQuery(kqlQuery)).toEqual('( name : Provider 1 ) or ( name : Provider 2 )');
|
||||
});
|
||||
|
||||
test('Buld KQL query with one data provider and one and', () => {
|
||||
const dataProviders = cloneDeep(mockDataProviders.slice(0, 1));
|
||||
dataProviders[0].and = mockDataProviders.slice(1, 2);
|
||||
const kqlQuery = buildGlobalQuery(dataProviders);
|
||||
expect(cleanUpKqlQuery(kqlQuery)).toEqual(
|
||||
'( name : Provider 1 and @timestamp >= 1521830963132 and @timestamp <= 1521862432253 and name : Provider 2)'
|
||||
);
|
||||
expect(cleanUpKqlQuery(kqlQuery)).toEqual('( name : Provider 1 and name : Provider 2)');
|
||||
});
|
||||
|
||||
test('Buld KQL query with two data provider and mutiple and', () => {
|
||||
|
@ -45,28 +41,42 @@ describe('Build KQL Query', () => {
|
|||
dataProviders[1].and = mockDataProviders.slice(4, 5);
|
||||
const kqlQuery = buildGlobalQuery(dataProviders);
|
||||
expect(cleanUpKqlQuery(kqlQuery)).toEqual(
|
||||
'( name : Provider 1 and @timestamp >= 1521830963132 and @timestamp <= 1521862432253 and name : Provider 3 and name : Provider 4) or ( name : Provider 2 and @timestamp >= 1521830963132 and @timestamp <= 1521862432253 and name : Provider 5)'
|
||||
'( name : Provider 1 and name : Provider 3 and name : Provider 4) or ( name : Provider 2 and name : Provider 5)'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Combined Queries', () => {
|
||||
test('No Data Provider & No kqlQuery', () => {
|
||||
expect(combineQueries([], mockIndexPattern, '', 'search')).toBeNull();
|
||||
expect(combineQueries([], mockIndexPattern, '', 'search', startDate, endDate)).toBeNull();
|
||||
});
|
||||
|
||||
test('Only Data Provider', () => {
|
||||
const dataProviders = mockDataProviders.slice(0, 1);
|
||||
const { filterQuery } = combineQueries(dataProviders, mockIndexPattern, '', 'search')!;
|
||||
const { filterQuery } = combineQueries(
|
||||
dataProviders,
|
||||
mockIndexPattern,
|
||||
'',
|
||||
'search',
|
||||
startDate,
|
||||
endDate
|
||||
)!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"filter":[{"bool":{"should":[{"match":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}'
|
||||
);
|
||||
});
|
||||
|
||||
test('Only KQL search/filter query', () => {
|
||||
const { filterQuery } = combineQueries([], mockIndexPattern, 'host.name: "host-1"', 'search')!;
|
||||
const { filterQuery } = combineQueries(
|
||||
[],
|
||||
mockIndexPattern,
|
||||
'host.name: "host-1"',
|
||||
'search',
|
||||
startDate,
|
||||
endDate
|
||||
)!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}'
|
||||
'{"bool":{"filter":[{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -76,10 +86,12 @@ describe('Combined Queries', () => {
|
|||
dataProviders,
|
||||
mockIndexPattern,
|
||||
'host.name: "host-1"',
|
||||
'search'
|
||||
'search',
|
||||
startDate,
|
||||
endDate
|
||||
)!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"should":[{"bool":{"filter":[{"bool":{"should":[{"match":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"minimum_should_match":1}}'
|
||||
'{"bool":{"filter":[{"bool":{"should":[{"bool":{"should":[{"match":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}'
|
||||
);
|
||||
});
|
||||
|
||||
|
@ -89,10 +101,12 @@ describe('Combined Queries', () => {
|
|||
dataProviders,
|
||||
mockIndexPattern,
|
||||
'host.name: "host-1"',
|
||||
'filter'
|
||||
'filter',
|
||||
startDate,
|
||||
endDate
|
||||
)!;
|
||||
expect(filterQuery).toEqual(
|
||||
'{"bool":{"filter":[{"bool":{"filter":[{"bool":{"should":[{"match":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}]}}'
|
||||
'{"bool":{"filter":[{"bool":{"filter":[{"bool":{"should":[{"match":{"name":"Provider 1"}}],"minimum_should_match":1}},{"bool":{"should":[{"match_phrase":{"host.name":"host-1"}}],"minimum_should_match":1}}]}},{"bool":{"filter":[{"bool":{"should":[{"range":{"@timestamp":{"gte":1521830963132}}}],"minimum_should_match":1}},{"bool":{"should":[{"range":{"@timestamp":{"lte":1521862432253}}}],"minimum_should_match":1}}]}}]}}'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -22,11 +22,6 @@ const buildQueryMatch = (dataProvider: DataProvider) =>
|
|||
: ''
|
||||
}`.trim();
|
||||
|
||||
const buildQueryDate = (dataProvider: DataProvider) =>
|
||||
dataProvider.queryDate
|
||||
? `@timestamp >= ${dataProvider.queryDate.from} and @timestamp <= ${dataProvider.queryDate.to}`
|
||||
: '';
|
||||
|
||||
const buildQueryForAndProvider = (dataAndProviders: DataProvider[]) =>
|
||||
dataAndProviders
|
||||
.reduce((andQuery, andDataProvider) => {
|
||||
|
@ -44,7 +39,6 @@ export const buildGlobalQuery = (dataProviders: DataProvider[]) =>
|
|||
return dataProvider.enabled
|
||||
? `${prepend(query)}(
|
||||
${buildQueryMatch(dataProvider)}
|
||||
${dataProvider.queryDate ? ` and ${buildQueryDate(dataProvider)}` : ''}
|
||||
${
|
||||
dataProvider.and.length > 0 ? ` and ${buildQueryForAndProvider(dataProvider.and)}` : ''
|
||||
})`.trim()
|
||||
|
@ -56,22 +50,34 @@ export const combineQueries = (
|
|||
dataProviders: DataProvider[],
|
||||
indexPattern: StaticIndexPattern,
|
||||
kqlQuery: string,
|
||||
kqlMode: string
|
||||
kqlMode: string,
|
||||
start: number,
|
||||
end: number
|
||||
): { filterQuery: string } | null => {
|
||||
if (isEmpty(dataProviders) && isEmpty(kqlQuery)) {
|
||||
return null;
|
||||
} else if (isEmpty(dataProviders) && !isEmpty(kqlQuery)) {
|
||||
return {
|
||||
filterQuery: convertKueryToElasticSearchQuery(kqlQuery, indexPattern),
|
||||
filterQuery: convertKueryToElasticSearchQuery(
|
||||
`(${kqlQuery}) and @timestamp >= ${start} and @timestamp <= ${end}`,
|
||||
indexPattern
|
||||
),
|
||||
};
|
||||
} else if (!isEmpty(dataProviders) && isEmpty(kqlQuery)) {
|
||||
return {
|
||||
filterQuery: convertKueryToElasticSearchQuery(buildGlobalQuery(dataProviders), indexPattern),
|
||||
filterQuery: convertKueryToElasticSearchQuery(
|
||||
`((${buildGlobalQuery(
|
||||
dataProviders
|
||||
)}) and @timestamp >= ${start} and @timestamp <= ${end})`,
|
||||
indexPattern
|
||||
),
|
||||
};
|
||||
}
|
||||
const operatorKqlQuery = kqlMode === 'filter' ? 'and' : 'or';
|
||||
const postpend = (q: string) => `${!isEmpty(q) ? ` ${operatorKqlQuery} (${q})` : ''}`;
|
||||
const globalQuery = `${buildGlobalQuery(dataProviders)}${postpend(kqlQuery)}`;
|
||||
const globalQuery = `((${buildGlobalQuery(dataProviders)}${postpend(
|
||||
kqlQuery
|
||||
)}) and @timestamp >= ${start} and @timestamp <= ${end})`;
|
||||
|
||||
return {
|
||||
filterQuery: convertKueryToElasticSearchQuery(globalQuery, indexPattern),
|
||||
|
|
|
@ -10,7 +10,14 @@ import { ActionCreator } from 'typescript-fsa';
|
|||
|
||||
import { WithSource } from '../../containers/source';
|
||||
import { IndexType } from '../../graphql/types';
|
||||
import { State, timelineActions, timelineModel, timelineSelectors } from '../../store';
|
||||
import {
|
||||
inputsModel,
|
||||
inputsSelectors,
|
||||
State,
|
||||
timelineActions,
|
||||
timelineModel,
|
||||
timelineSelectors,
|
||||
} from '../../store';
|
||||
|
||||
import { ColumnHeader } from './body/column_headers/column_header';
|
||||
import { Sort } from './body/sort';
|
||||
|
@ -35,12 +42,15 @@ interface StateReduxProps {
|
|||
activePage?: number;
|
||||
columns: ColumnHeader[];
|
||||
dataProviders?: DataProvider[];
|
||||
end: number;
|
||||
isLive: boolean;
|
||||
itemsPerPage?: number;
|
||||
itemsPerPageOptions?: number[];
|
||||
kqlMode: timelineModel.KqlMode;
|
||||
kqlQueryExpression: string;
|
||||
pageCount?: number;
|
||||
sort?: Sort;
|
||||
start: number;
|
||||
show?: boolean;
|
||||
}
|
||||
|
||||
|
@ -107,14 +117,17 @@ class StatefulTimelineComponent extends React.PureComponent<Props> {
|
|||
const {
|
||||
columns,
|
||||
dataProviders,
|
||||
end,
|
||||
flyoutHeight,
|
||||
flyoutHeaderHeight,
|
||||
id,
|
||||
isLive,
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions,
|
||||
kqlMode,
|
||||
kqlQueryExpression,
|
||||
show,
|
||||
start,
|
||||
sort,
|
||||
} = this.props;
|
||||
|
||||
|
@ -126,9 +139,11 @@ class StatefulTimelineComponent extends React.PureComponent<Props> {
|
|||
columns={columns}
|
||||
id={id}
|
||||
dataProviders={dataProviders!}
|
||||
end={end}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
flyoutHeight={flyoutHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={isLive}
|
||||
itemsPerPage={itemsPerPage!}
|
||||
itemsPerPageOptions={itemsPerPageOptions!}
|
||||
kqlMode={kqlMode}
|
||||
|
@ -140,6 +155,7 @@ class StatefulTimelineComponent extends React.PureComponent<Props> {
|
|||
onToggleDataProviderEnabled={this.onToggleDataProviderEnabled}
|
||||
onToggleDataProviderExcluded={this.onToggleDataProviderExcluded}
|
||||
show={show!}
|
||||
start={start}
|
||||
sort={sort!}
|
||||
/>
|
||||
)}
|
||||
|
@ -189,8 +205,10 @@ class StatefulTimelineComponent extends React.PureComponent<Props> {
|
|||
const makeMapStateToProps = () => {
|
||||
const getTimeline = timelineSelectors.getTimelineByIdSelector();
|
||||
const getKqlQueryTimeline = timelineSelectors.getKqlFilterQuerySelector();
|
||||
const getInputsTimeline = inputsSelectors.getTimelineSelector();
|
||||
const mapStateToProps = (state: State, { id }: OwnProps) => {
|
||||
const timeline: timelineModel.TimelineModel = getTimeline(state, id);
|
||||
const input: inputsModel.InputsRange = getInputsTimeline(state);
|
||||
const {
|
||||
columns,
|
||||
dataProviders,
|
||||
|
@ -205,12 +223,15 @@ const makeMapStateToProps = () => {
|
|||
return {
|
||||
columns,
|
||||
dataProviders,
|
||||
end: input.timerange.to,
|
||||
id,
|
||||
isLive: input.policy.kind === 'interval',
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions,
|
||||
kqlMode,
|
||||
kqlQueryExpression,
|
||||
sort,
|
||||
start: input.timerange.from,
|
||||
show,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,112 +0,0 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Properties renders correctly against snapshot 1`] = `
|
||||
<styled.div
|
||||
data-test-subj="timeline-properties"
|
||||
width={940}
|
||||
>
|
||||
<Styled(EuiFlexGroup)
|
||||
alignItems="center"
|
||||
data-test-subj="properties-left"
|
||||
gutterSize="s"
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<pure(Component)
|
||||
isFavorite={false}
|
||||
timelineId="abc"
|
||||
updateIsFavorite={[MockFunction]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<pure(Component)
|
||||
timelineId="abc"
|
||||
title=""
|
||||
updateTitle={[MockFunction]}
|
||||
/>
|
||||
<EuiFlexItem
|
||||
grow={true}
|
||||
>
|
||||
<pure(Component)
|
||||
description=""
|
||||
timelineId="abc"
|
||||
updateDescription={[MockFunction]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<pure(Component)
|
||||
animate={true}
|
||||
associateNote={[MockFunction]}
|
||||
getNotesByIds={[MockFunction]}
|
||||
noteIds={Array []}
|
||||
showNotes={false}
|
||||
size="l"
|
||||
text="Notes"
|
||||
toggleShowNotes={[Function]}
|
||||
toolTip="Add and review notes about this Timeline. Notes may also be added to events."
|
||||
updateNote={[MockFunction]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
</Styled(EuiFlexGroup)>
|
||||
<Styled(EuiFlexGroup)
|
||||
alignItems="center"
|
||||
data-test-subj="properties-right"
|
||||
gutterSize="s"
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<pure(Component)
|
||||
history={Array []}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<pure(Component)
|
||||
isLive={false}
|
||||
timelineId="abc"
|
||||
updateIsLive={[MockFunction]}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<EuiPopover
|
||||
anchorPosition="downRight"
|
||||
button={
|
||||
<EuiIcon
|
||||
data-test-subj="settings-gear"
|
||||
onClick={[Function]}
|
||||
size="l"
|
||||
type="gear"
|
||||
/>
|
||||
}
|
||||
closePopover={[Function]}
|
||||
hasArrow={true}
|
||||
id="timelineSettingsPopover"
|
||||
isOpen={false}
|
||||
ownFocus={false}
|
||||
panelPaddingSize="m"
|
||||
>
|
||||
<EuiForm>
|
||||
<EuiFormRow
|
||||
describedByIds={Array []}
|
||||
fullWidth={false}
|
||||
hasEmptyLabelSpace={false}
|
||||
labelType="label"
|
||||
>
|
||||
<pure(Component)
|
||||
createTimeline={[MockFunction]}
|
||||
onClosePopover={[Function]}
|
||||
timelineId="abc"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiForm>
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
</Styled(EuiFlexGroup)>
|
||||
</styled.div>
|
||||
`;
|
|
@ -17,7 +17,6 @@ import * as React from 'react';
|
|||
import { pure } from 'recompose';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import { History } from '../../../lib/history';
|
||||
import { Note } from '../../../lib/note';
|
||||
import { Notes } from '../../notes';
|
||||
import { AssociateNote, UpdateNote } from '../../notes/helpers';
|
||||
|
@ -25,7 +24,6 @@ import { AssociateNote, UpdateNote } from '../../notes/helpers';
|
|||
import {
|
||||
ButtonContainer,
|
||||
DescriptionContainer,
|
||||
HistoryButtonLabel,
|
||||
LabelText,
|
||||
NameField,
|
||||
NotesButtonLabel,
|
||||
|
@ -44,7 +42,6 @@ export const NOTES_PANEL_HEIGHT = 633;
|
|||
|
||||
type CreateTimeline = ({ id, show }: { id: string; show?: boolean }) => void;
|
||||
type UpdateIsFavorite = ({ id, isFavorite }: { id: string; isFavorite: boolean }) => void;
|
||||
type UpdateIsLive = ({ id, isLive }: { id: string; isLive: boolean }) => void;
|
||||
type UpdateTitle = ({ id, title }: { id: string; title: string }) => void;
|
||||
type UpdateDescription = ({ id, description }: { id: string; description: string }) => void;
|
||||
|
||||
|
@ -260,44 +257,3 @@ export const NotesButton = pure<NotesButtonProps>(
|
|||
</EuiToolTip>
|
||||
)
|
||||
);
|
||||
|
||||
export const HistoryButton = pure<{ history: History[] }>(({ history }) => (
|
||||
<EuiToolTip data-test-subj="timeline-history-tool-tip" content={i18n.HISTORY_TOOL_TIP}>
|
||||
<EuiButton
|
||||
data-test-subj="timeline-history"
|
||||
iconType="arrowDown"
|
||||
iconSide="right"
|
||||
isDisabled={true}
|
||||
onClick={() => window.alert('Show history')}
|
||||
size="l"
|
||||
>
|
||||
<HistoryButtonLabel>
|
||||
<EuiBadge data-test-subj="history-count" color="hollow">
|
||||
{history.length}
|
||||
</EuiBadge>
|
||||
<LabelText>{i18n.HISTORY}</LabelText>
|
||||
</HistoryButtonLabel>
|
||||
</EuiButton>
|
||||
</EuiToolTip>
|
||||
));
|
||||
|
||||
export const StreamLive = pure<{ isLive: boolean; timelineId: string; updateIsLive: UpdateIsLive }>(
|
||||
({ isLive, timelineId, updateIsLive }) => (
|
||||
<EuiToolTip data-test-subj="timeline-stream-tool-tip" content={i18n.STREAM_LIVE_TOOL_TIP}>
|
||||
<ButtonContainer animate={true}>
|
||||
<EuiButton
|
||||
data-test-subj="timeline-stream-live"
|
||||
color={isLive ? 'secondary' : 'primary'}
|
||||
fill={isLive ? true : false}
|
||||
iconType="play"
|
||||
iconSide="left"
|
||||
isDisabled={true}
|
||||
onClick={() => updateIsLive({ id: timelineId, isLive: !isLive })}
|
||||
size="l"
|
||||
>
|
||||
{i18n.STREAM_LIVE}
|
||||
</EuiButton>
|
||||
</ButtonContainer>
|
||||
</EuiToolTip>
|
||||
)
|
||||
);
|
||||
|
|
|
@ -4,66 +4,74 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import toJson from 'enzyme-to-json';
|
||||
import { mount } from 'enzyme';
|
||||
import * as React from 'react';
|
||||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
|
||||
import {
|
||||
Properties,
|
||||
showDescriptionThreshold,
|
||||
showHistoryThreshold,
|
||||
showStreamLiveThreshold,
|
||||
} from '.';
|
||||
import { mockGlobalState } from '../../../mock';
|
||||
import { createStore, State } from '../../../store';
|
||||
|
||||
import { Properties, showDescriptionThreshold, showNotesThreshold } from '.';
|
||||
|
||||
describe('Properties', () => {
|
||||
const usersViewing = ['elastic'];
|
||||
|
||||
test('renders correctly against snapshot', () => {
|
||||
const wrapper = shallow(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
const state: State = mockGlobalState;
|
||||
let store = createStore(state);
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
store = createStore(state);
|
||||
});
|
||||
|
||||
test('renders correctly', () => {
|
||||
const wrapper = mount(
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
expect(toJson(wrapper)).toMatchSnapshot();
|
||||
expect(wrapper.find('[data-test-subj="timeline-properties"]').exists()).toEqual(true);
|
||||
});
|
||||
|
||||
test('it renders an empty star icon when it is NOT a favorite', () => {
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="timeline-favorite-empty-star"]').exists()).toEqual(true);
|
||||
|
@ -71,25 +79,26 @@ describe('Properties', () => {
|
|||
|
||||
test('it renders a filled star icon when it is a favorite', () => {
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={true}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={true}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="timeline-favorite-filled-star"]').exists()).toEqual(true);
|
||||
|
@ -99,25 +108,26 @@ describe('Properties', () => {
|
|||
const title = 'foozle';
|
||||
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title={title}
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title={title}
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
|
@ -128,30 +138,125 @@ describe('Properties', () => {
|
|||
).toEqual(title);
|
||||
});
|
||||
|
||||
test('it renders the date picker with the lock icon', () => {
|
||||
const wrapper = mount(
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find('[data-test-subj="properties-left"]')
|
||||
.find('[data-test-subj="timeline-date-picker-container"]')
|
||||
.exists()
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('it renders the lock icon when isDatepickerLocked is true', () => {
|
||||
const wrapper = mount(
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={true}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('[data-test-subj="properties-left"]')
|
||||
.find('[data-test-subj="timeline-date-picker-lock-button"]')
|
||||
.exists()
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('it renders the unlock icon when isDatepickerLocked is false', () => {
|
||||
const wrapper = mount(
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
expect(
|
||||
wrapper
|
||||
.find('[data-test-subj="properties-left"]')
|
||||
.find('[data-test-subj="timeline-date-picker-unlock-button"]')
|
||||
.exists()
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('it renders a description on the left when the width is at least as wide as the threshold', () => {
|
||||
const description = 'strange';
|
||||
const width = showDescriptionThreshold;
|
||||
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description={description}
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description={description}
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
|
@ -168,25 +273,26 @@ describe('Properties', () => {
|
|||
const width = showDescriptionThreshold - 1;
|
||||
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description={description}
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description={description}
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
|
@ -197,29 +303,30 @@ describe('Properties', () => {
|
|||
).toEqual(false);
|
||||
});
|
||||
|
||||
test('it renders a notes button on the left', () => {
|
||||
const width = showDescriptionThreshold - 1;
|
||||
test('it renders a notes button on the left when the width is at least as wide as the threshold', () => {
|
||||
const width = showNotesThreshold;
|
||||
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
|
@ -230,159 +337,62 @@ describe('Properties', () => {
|
|||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('it renders a history button on the right when the width is at least as wide as the threshold', () => {
|
||||
const width = showHistoryThreshold;
|
||||
test('it does NOT render a a notes button on the left when the width is less than the threshold', () => {
|
||||
const width = showNotesThreshold - 1;
|
||||
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find('[data-test-subj="properties-right"]')
|
||||
.find('[data-test-subj="timeline-history"]')
|
||||
.exists()
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('it does NOT renders a history button on the right when the width is less than the threshold', () => {
|
||||
const width = showHistoryThreshold - 1;
|
||||
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find('[data-test-subj="properties-right"]')
|
||||
.find('[data-test-subj="timeline-history"]')
|
||||
.exists()
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
test('it renders a stream live button on the right when the width is at least as wide as the threshold', () => {
|
||||
const width = showStreamLiveThreshold;
|
||||
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find('[data-test-subj="properties-right"]')
|
||||
.find('[data-test-subj="timeline-stream-live"]')
|
||||
.exists()
|
||||
).toEqual(true);
|
||||
});
|
||||
|
||||
test('it does NOT render a stream live button on the right when the width is less than the threshold', () => {
|
||||
const width = showStreamLiveThreshold - 1;
|
||||
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={width}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
wrapper
|
||||
.find('[data-test-subj="properties-right"]')
|
||||
.find('[data-test-subj="timeline-stream-live"]')
|
||||
.find('[data-test-subj="properties-left"]')
|
||||
.find('[data-test-subj="timeline-notes-button-large"]')
|
||||
.exists()
|
||||
).toEqual(false);
|
||||
});
|
||||
|
||||
test('it renders a settings icon', () => {
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="settings-gear"]').exists()).toEqual(true);
|
||||
|
@ -392,25 +402,26 @@ describe('Properties', () => {
|
|||
const title = 'port scan';
|
||||
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title={title}
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title={title}
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="avatar"]').exists()).toEqual(true);
|
||||
|
@ -418,25 +429,26 @@ describe('Properties', () => {
|
|||
|
||||
test('it does NOT render an avatar for the current user viewing the timeline when it does NOT have a title', () => {
|
||||
const wrapper = mount(
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isFavorite={false}
|
||||
isLive={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
history={[]}
|
||||
timelineId="abc"
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateIsLive={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
<ReduxStoreProvider store={store}>
|
||||
<Properties
|
||||
associateNote={jest.fn()}
|
||||
createTimeline={jest.fn()}
|
||||
isDatepickerLocked={false}
|
||||
isFavorite={false}
|
||||
title=""
|
||||
description=""
|
||||
getNotesByIds={jest.fn()}
|
||||
noteIds={[]}
|
||||
timelineId="abc"
|
||||
toggleLock={jest.fn()}
|
||||
updateDescription={jest.fn()}
|
||||
updateIsFavorite={jest.fn()}
|
||||
updateTitle={jest.fn()}
|
||||
updateNote={jest.fn()}
|
||||
usersViewing={usersViewing}
|
||||
width={1000}
|
||||
/>
|
||||
</ReduxStoreProvider>
|
||||
);
|
||||
|
||||
expect(wrapper.find('[data-test-subj="avatar"]').exists()).toEqual(false);
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
import {
|
||||
EuiAvatar,
|
||||
EuiButtonIcon,
|
||||
EuiFlexGroup,
|
||||
EuiFlexItem,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
|
@ -16,27 +18,26 @@ import {
|
|||
import * as React from 'react';
|
||||
import styled, { injectGlobal } from 'styled-components';
|
||||
|
||||
import { History } from '../../../lib/history';
|
||||
import { Note } from '../../../lib/note';
|
||||
import { inputsModel } from '../../../store';
|
||||
import { AssociateNote, UpdateNote } from '../../notes/helpers';
|
||||
import { SuperDatePicker } from '../../super_date_picker';
|
||||
|
||||
import { Description, Name, NewTimeline, NotesButton, StarIcon } from './helpers';
|
||||
import {
|
||||
Description,
|
||||
HistoryButton,
|
||||
Name,
|
||||
NewTimeline,
|
||||
NotesButton,
|
||||
StarIcon,
|
||||
StreamLive,
|
||||
} from './helpers';
|
||||
import { PropertiesLeft, PropertiesRight, TimelineProperties } from './styles';
|
||||
DatePicker,
|
||||
PropertiesLeft,
|
||||
PropertiesRight,
|
||||
TimelineProperties,
|
||||
LockIconContainer,
|
||||
} from './styles';
|
||||
import * as i18n from './translations';
|
||||
|
||||
type CreateTimeline = ({ id, show }: { id: string; show?: boolean }) => void;
|
||||
type UpdateIsFavorite = ({ id, isFavorite }: { id: string; isFavorite: boolean }) => void;
|
||||
type UpdateIsLive = ({ id, isLive }: { id: string; isLive: boolean }) => void;
|
||||
type UpdateTitle = ({ id, title }: { id: string; title: string }) => void;
|
||||
type UpdateDescription = ({ id, description }: { id: string; description: string }) => void;
|
||||
type ToggleLock = ({ linkToId }: { linkToId: inputsModel.InputsModelId }) => void;
|
||||
|
||||
// SIDE EFFECT: the following `injectGlobal` overrides `EuiPopover`
|
||||
// and `EuiToolTip` global styles:
|
||||
|
@ -57,17 +58,16 @@ const Avatar = styled(EuiAvatar)`
|
|||
interface Props {
|
||||
associateNote: AssociateNote;
|
||||
createTimeline: CreateTimeline;
|
||||
isDatepickerLocked: boolean;
|
||||
isFavorite: boolean;
|
||||
isLive: boolean;
|
||||
title: string;
|
||||
description: string;
|
||||
getNotesByIds: (noteIds: string[]) => Note[];
|
||||
noteIds: string[];
|
||||
history: History[];
|
||||
timelineId: string;
|
||||
toggleLock: ToggleLock;
|
||||
updateDescription: UpdateDescription;
|
||||
updateIsFavorite: UpdateIsFavorite;
|
||||
updateIsLive: UpdateIsLive;
|
||||
updateTitle: UpdateTitle;
|
||||
updateNote: UpdateNote;
|
||||
usersViewing: string[];
|
||||
|
@ -80,9 +80,15 @@ interface State {
|
|||
}
|
||||
|
||||
const rightGutter = 60; // px
|
||||
export const showDescriptionThreshold = 610;
|
||||
export const showHistoryThreshold = 760;
|
||||
export const showStreamLiveThreshold = 900;
|
||||
export const datePickerThreshold = 600;
|
||||
export const showNotesThreshold = 810;
|
||||
export const showDescriptionThreshold = 970;
|
||||
|
||||
const starIconWidth = 30;
|
||||
const nameWidth = 155;
|
||||
const descriptionWidth = 165;
|
||||
const noteWidth = 110;
|
||||
const settingsWidth = 50;
|
||||
|
||||
/** Displays the properties of a timeline, i.e. name, description, notes, etc */
|
||||
export class Properties extends React.PureComponent<Props, State> {
|
||||
|
@ -118,22 +124,29 @@ export class Properties extends React.PureComponent<Props, State> {
|
|||
description,
|
||||
getNotesByIds,
|
||||
isFavorite,
|
||||
isLive,
|
||||
history,
|
||||
isDatepickerLocked,
|
||||
title,
|
||||
noteIds,
|
||||
timelineId,
|
||||
updateDescription,
|
||||
updateIsFavorite,
|
||||
updateIsLive,
|
||||
updateTitle,
|
||||
updateNote,
|
||||
usersViewing,
|
||||
width,
|
||||
} = this.props;
|
||||
|
||||
const datePickerWidth =
|
||||
width -
|
||||
rightGutter -
|
||||
starIconWidth -
|
||||
nameWidth -
|
||||
(width >= showDescriptionThreshold ? descriptionWidth : 0) -
|
||||
noteWidth -
|
||||
settingsWidth;
|
||||
|
||||
return (
|
||||
<TimelineProperties data-test-subj="timeline-properties" width={width - rightGutter}>
|
||||
<TimelineProperties data-test-subj="timeline-properties" width={width}>
|
||||
<PropertiesLeft alignItems="center" data-test-subj="properties-left" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<StarIcon
|
||||
|
@ -146,7 +159,7 @@ export class Properties extends React.PureComponent<Props, State> {
|
|||
<Name timelineId={timelineId} title={title} updateTitle={updateTitle} />
|
||||
|
||||
{width >= showDescriptionThreshold ? (
|
||||
<EuiFlexItem grow={true}>
|
||||
<EuiFlexItem grow={2}>
|
||||
<Description
|
||||
description={description}
|
||||
timelineId={timelineId}
|
||||
|
@ -155,35 +168,67 @@ export class Properties extends React.PureComponent<Props, State> {
|
|||
</EuiFlexItem>
|
||||
) : null}
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<NotesButton
|
||||
animate={true}
|
||||
associateNote={associateNote}
|
||||
getNotesByIds={getNotesByIds}
|
||||
noteIds={noteIds}
|
||||
showNotes={this.state.showNotes}
|
||||
size="l"
|
||||
text={i18n.NOTES}
|
||||
toggleShowNotes={this.onToggleShowNotes}
|
||||
toolTip={i18n.NOTES_TOOL_TIP}
|
||||
updateNote={updateNote}
|
||||
/>
|
||||
{width >= showNotesThreshold ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<NotesButton
|
||||
animate={true}
|
||||
associateNote={associateNote}
|
||||
getNotesByIds={getNotesByIds}
|
||||
noteIds={noteIds}
|
||||
showNotes={this.state.showNotes}
|
||||
size="l"
|
||||
text={i18n.NOTES}
|
||||
toggleShowNotes={this.onToggleShowNotes}
|
||||
toolTip={i18n.NOTES_TOOL_TIP}
|
||||
updateNote={updateNote}
|
||||
/>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
|
||||
<EuiFlexItem grow={1}>
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
gutterSize="none"
|
||||
data-test-subj="timeline-date-picker-container"
|
||||
>
|
||||
<LockIconContainer grow={false}>
|
||||
<EuiToolTip
|
||||
data-test-subj="timeline-date-picker-lock-tooltip"
|
||||
position="top"
|
||||
content={
|
||||
isDatepickerLocked
|
||||
? i18n.LOCK_SYNC_MAIN_DATE_PICKER_TOOL_TIP
|
||||
: i18n.UNLOCK_SYNC_MAIN_DATE_PICKER_TOOL_TIP
|
||||
}
|
||||
>
|
||||
<EuiButtonIcon
|
||||
data-test-subj={`timeline-date-picker-${
|
||||
isDatepickerLocked ? 'lock' : 'unlock'
|
||||
}-button`}
|
||||
color="primary"
|
||||
onClick={this.toggleLock}
|
||||
iconType={isDatepickerLocked ? 'lock' : 'lockOpen'}
|
||||
aria-label={
|
||||
isDatepickerLocked
|
||||
? i18n.UNLOCK_SYNC_MAIN_DATE_PICKER_ARIA
|
||||
: i18n.LOCK_SYNC_MAIN_DATE_PICKER_ARIA
|
||||
}
|
||||
/>
|
||||
</EuiToolTip>
|
||||
</LockIconContainer>
|
||||
<DatePicker
|
||||
grow={1}
|
||||
width={
|
||||
datePickerWidth > datePickerThreshold ? datePickerThreshold : datePickerWidth
|
||||
}
|
||||
>
|
||||
<SuperDatePicker id="timeline" />
|
||||
</DatePicker>
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</PropertiesLeft>
|
||||
|
||||
<PropertiesRight alignItems="center" data-test-subj="properties-right" gutterSize="s">
|
||||
{width >= showHistoryThreshold ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<HistoryButton history={history} />
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
|
||||
{width >= showStreamLiveThreshold ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<StreamLive isLive={isLive} timelineId={timelineId} updateIsLive={updateIsLive} />
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiPopover
|
||||
anchorPosition="downRight"
|
||||
|
@ -208,6 +253,23 @@ export class Properties extends React.PureComponent<Props, State> {
|
|||
/>
|
||||
</EuiFormRow>
|
||||
|
||||
{width < showNotesThreshold ? (
|
||||
<EuiFormRow>
|
||||
<NotesButton
|
||||
animate={true}
|
||||
associateNote={associateNote}
|
||||
getNotesByIds={getNotesByIds}
|
||||
noteIds={noteIds}
|
||||
showNotes={this.state.showNotes}
|
||||
size="l"
|
||||
text={i18n.NOTES}
|
||||
toggleShowNotes={this.onToggleShowNotes}
|
||||
toolTip={i18n.NOTES_TOOL_TIP}
|
||||
updateNote={updateNote}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
) : null}
|
||||
|
||||
{width < showDescriptionThreshold ? (
|
||||
<EuiFormRow label={i18n.DESCRIPTION}>
|
||||
<Description
|
||||
|
@ -217,22 +279,6 @@ export class Properties extends React.PureComponent<Props, State> {
|
|||
/>
|
||||
</EuiFormRow>
|
||||
) : null}
|
||||
|
||||
{width < showHistoryThreshold ? (
|
||||
<EuiFormRow>
|
||||
<HistoryButton history={history} />
|
||||
</EuiFormRow>
|
||||
) : null}
|
||||
|
||||
{width < showStreamLiveThreshold ? (
|
||||
<EuiFormRow>
|
||||
<StreamLive
|
||||
isLive={isLive}
|
||||
timelineId={timelineId}
|
||||
updateIsLive={updateIsLive}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
) : null}
|
||||
</EuiForm>
|
||||
</EuiPopover>
|
||||
</EuiFlexItem>
|
||||
|
@ -253,4 +299,8 @@ export class Properties extends React.PureComponent<Props, State> {
|
|||
</TimelineProperties>
|
||||
);
|
||||
}
|
||||
|
||||
private toggleLock = () => {
|
||||
this.props.toggleLock({ linkToId: 'timeline' });
|
||||
};
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiFieldText, EuiFlexGroup, EuiIcon } from '@elastic/eui';
|
||||
import { EuiFieldText, EuiFlexGroup, EuiFlexItem, EuiIcon } from '@elastic/eui';
|
||||
import styled, { keyframes } from 'styled-components';
|
||||
|
||||
const fadeInEffect = keyframes`
|
||||
|
@ -21,6 +21,10 @@ export const TimelineProperties = styled.div<{ width: number }>`
|
|||
width: ${({ width }) => `${width}px`};
|
||||
`;
|
||||
|
||||
export const DatePicker = styled(EuiFlexItem)<{ width: number }>`
|
||||
width: ${({ width }) => `${width}px`};
|
||||
`;
|
||||
|
||||
export const NameField = styled(EuiFieldText)`
|
||||
width: 150px;
|
||||
margin-right: 5px;
|
||||
|
@ -40,7 +44,7 @@ export const NotesButtonLabel = styled.div`
|
|||
|
||||
export const NotesIconContainer = styled.div`
|
||||
position: relative;
|
||||
margin-left: 5px;
|
||||
margin-left: 3px;
|
||||
`;
|
||||
|
||||
export const PositionedNotesIcon = styled.div<{ size: 'l' | 's' }>`
|
||||
|
@ -59,17 +63,12 @@ export const ButtonContainer = styled.div<{ animate: boolean }>`
|
|||
animation: ${fadeInEffect} ${({ animate }) => (animate ? '0.3s' : '0s')};
|
||||
`;
|
||||
|
||||
export const HistoryButtonLabel = styled.div`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
`;
|
||||
|
||||
export const LabelText = styled.div`
|
||||
margin-left: 10px;
|
||||
`;
|
||||
|
||||
export const StyledStar = styled(EuiIcon)`
|
||||
margin-right: 10px;
|
||||
margin-right: 5px;
|
||||
cursor: pointer;
|
||||
`;
|
||||
|
||||
|
@ -78,7 +77,7 @@ export const PropertiesLeft = styled(EuiFlexGroup)`
|
|||
`;
|
||||
|
||||
export const PropertiesRight = styled(EuiFlexGroup)`
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
`;
|
||||
|
||||
export const Facet = styled.div`
|
||||
|
@ -96,3 +95,7 @@ export const Facet = styled.div`
|
|||
padding-right: 8px;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
export const LockIconContainer = styled(EuiFlexItem)`
|
||||
margin-right: 2px;
|
||||
`;
|
||||
|
|
|
@ -94,3 +94,33 @@ export const NEW_TIMELINE_TOOL_TIP = i18n.translate(
|
|||
defaultMessage: 'Create a new timeline',
|
||||
}
|
||||
);
|
||||
|
||||
export const LOCK_SYNC_MAIN_DATE_PICKER_TOOL_TIP = i18n.translate(
|
||||
'xpack.siem.timeline.properties.lockDatePickerTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'Disable syncing of date/time range between the currently viewed page and your timeline',
|
||||
}
|
||||
);
|
||||
|
||||
export const UNLOCK_SYNC_MAIN_DATE_PICKER_TOOL_TIP = i18n.translate(
|
||||
'xpack.siem.timeline.properties.unlockDatePickerTooltip',
|
||||
{
|
||||
defaultMessage:
|
||||
'Enable syncing of date/time range between the currently viewed page and your timeline',
|
||||
}
|
||||
);
|
||||
|
||||
export const LOCK_SYNC_MAIN_DATE_PICKER_ARIA = i18n.translate(
|
||||
'xpack.siem.timeline.properties.lockDatePickerDescription',
|
||||
{
|
||||
defaultMessage: 'Lock date picker to global date picker',
|
||||
}
|
||||
);
|
||||
|
||||
export const UNLOCK_SYNC_MAIN_DATE_PICKER_ARIA = i18n.translate(
|
||||
'xpack.siem.timeline.properties.unlockDatePickerDescription',
|
||||
{
|
||||
defaultMessage: 'Unlock date picker to global date picker',
|
||||
}
|
||||
);
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
|
||||
import { inputsActions, inputsModel } from '../../store';
|
||||
|
||||
interface TimelineRefetchDispatch {
|
||||
setTimelineQuery: ActionCreator<{
|
||||
inputId: inputsModel.InputsModelId;
|
||||
id: string;
|
||||
loading: boolean;
|
||||
refetch: inputsModel.Refetch;
|
||||
}>;
|
||||
}
|
||||
|
||||
interface TimelineRefetchProps {
|
||||
children: React.ReactNode;
|
||||
id: string;
|
||||
loading: boolean;
|
||||
refetch: inputsModel.Refetch;
|
||||
}
|
||||
|
||||
type OwnProps = TimelineRefetchDispatch & TimelineRefetchProps;
|
||||
|
||||
class TimelineRefetchComponent extends React.PureComponent<OwnProps> {
|
||||
public componentDidUpdate(prevProps: OwnProps) {
|
||||
const { loading, id, refetch } = this.props;
|
||||
if (prevProps.loading !== loading) {
|
||||
this.props.setTimelineQuery({ inputId: 'timeline', id, loading, refetch });
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return <>{this.props.children}</>;
|
||||
}
|
||||
}
|
||||
|
||||
export const TimelineRefetch = connect(
|
||||
null,
|
||||
{
|
||||
setTimelineQuery: inputsActions.setQuery,
|
||||
}
|
||||
)(TimelineRefetchComponent);
|
|
@ -28,6 +28,8 @@ describe('Timeline', () => {
|
|||
columnId: '@timestamp',
|
||||
sortDirection: Direction.desc,
|
||||
};
|
||||
const startDate = new Date('2018-03-23T18:49:23.132Z').valueOf();
|
||||
const endDate = new Date('2018-03-24T03:33:52.253Z').valueOf();
|
||||
|
||||
const indexPattern = mockIndexPattern;
|
||||
|
||||
|
@ -43,9 +45,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={mockDataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -57,6 +61,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
);
|
||||
|
@ -72,9 +77,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={mockDataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -86,6 +93,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -104,9 +112,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={mockDataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -118,6 +128,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -136,9 +147,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={mockDataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -150,6 +163,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -173,9 +187,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={mockDataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -187,6 +203,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -212,9 +229,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={mockDataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -226,6 +245,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -259,9 +279,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={mockDataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -273,6 +295,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={mockOnToggleDataProviderEnabled}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -310,9 +333,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={mockDataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -324,6 +349,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={mockOnToggleDataProviderExcluded}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -362,9 +388,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={dataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -376,6 +404,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -406,9 +435,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={dataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -420,6 +451,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -454,9 +486,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={dataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -468,6 +502,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={mockOnToggleDataProviderEnabled}
|
||||
onToggleDataProviderExcluded={jest.fn()}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
@ -506,9 +541,11 @@ describe('Timeline', () => {
|
|||
columns={defaultHeaders}
|
||||
id="foo"
|
||||
dataProviders={dataProviders}
|
||||
end={endDate}
|
||||
flyoutHeight={testFlyoutHeight}
|
||||
flyoutHeaderHeight={flyoutHeaderHeight}
|
||||
indexPattern={indexPattern}
|
||||
isLive={false}
|
||||
itemsPerPage={5}
|
||||
itemsPerPageOptions={[5, 10, 20]}
|
||||
kqlMode="search"
|
||||
|
@ -520,6 +557,7 @@ describe('Timeline', () => {
|
|||
onToggleDataProviderEnabled={jest.fn()}
|
||||
onToggleDataProviderExcluded={mockOnToggleDataProviderExcluded}
|
||||
show={true}
|
||||
start={startDate}
|
||||
sort={sort}
|
||||
/>
|
||||
</MockedProvider>
|
||||
|
|
|
@ -33,6 +33,7 @@ import {
|
|||
import { Footer, footerHeight } from './footer';
|
||||
import { TimelineHeader } from './header';
|
||||
import { calculateBodyHeight, combineQueries } from './helpers';
|
||||
import { TimelineRefetch } from './refetch_timeline';
|
||||
|
||||
const WrappedByAutoSizer = styled.div`
|
||||
width: 100%;
|
||||
|
@ -50,10 +51,12 @@ interface Props {
|
|||
browserFields: BrowserFields;
|
||||
columns: ColumnHeader[];
|
||||
dataProviders: DataProvider[];
|
||||
end: number;
|
||||
flyoutHeaderHeight: number;
|
||||
flyoutHeight: number;
|
||||
id: string;
|
||||
indexPattern: StaticIndexPattern;
|
||||
isLive: boolean;
|
||||
itemsPerPage: number;
|
||||
itemsPerPageOptions: number[];
|
||||
kqlMode: timelineModel.KqlMode;
|
||||
|
@ -65,6 +68,7 @@ interface Props {
|
|||
onToggleDataProviderEnabled: OnToggleDataProviderEnabled;
|
||||
onToggleDataProviderExcluded: OnToggleDataProviderExcluded;
|
||||
show: boolean;
|
||||
start: number;
|
||||
sort: Sort;
|
||||
}
|
||||
|
||||
|
@ -74,10 +78,12 @@ export const Timeline = pure<Props>(
|
|||
browserFields,
|
||||
columns,
|
||||
dataProviders,
|
||||
end,
|
||||
flyoutHeaderHeight,
|
||||
flyoutHeight,
|
||||
id,
|
||||
indexPattern,
|
||||
isLive,
|
||||
itemsPerPage,
|
||||
itemsPerPageOptions,
|
||||
kqlMode,
|
||||
|
@ -89,13 +95,16 @@ export const Timeline = pure<Props>(
|
|||
onToggleDataProviderEnabled,
|
||||
onToggleDataProviderExcluded,
|
||||
show,
|
||||
start,
|
||||
sort,
|
||||
}) => {
|
||||
const combinedQueries = combineQueries(
|
||||
dataProviders,
|
||||
indexPattern,
|
||||
kqlQueryExpression,
|
||||
kqlMode
|
||||
kqlMode,
|
||||
start,
|
||||
end
|
||||
);
|
||||
const columnsHeader = isEmpty(columns) ? defaultHeaders : columns;
|
||||
return (
|
||||
|
@ -133,8 +142,8 @@ export const Timeline = pure<Props>(
|
|||
direction: sort.sortDirection as Direction,
|
||||
}}
|
||||
>
|
||||
{({ events, loading, totalCount, pageInfo, loadMore, getUpdatedAt }) => (
|
||||
<>
|
||||
{({ events, loading, totalCount, pageInfo, loadMore, getUpdatedAt, refetch }) => (
|
||||
<TimelineRefetch loading={loading} id={id} refetch={refetch}>
|
||||
<StatefulBody
|
||||
browserFields={browserFields}
|
||||
data={events}
|
||||
|
@ -153,6 +162,7 @@ export const Timeline = pure<Props>(
|
|||
serverSideEventCount={totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
height={footerHeight}
|
||||
isLive={isLive}
|
||||
isLoading={loading}
|
||||
itemsCount={events.length}
|
||||
itemsPerPage={itemsPerPage}
|
||||
|
@ -164,7 +174,7 @@ export const Timeline = pure<Props>(
|
|||
getUpdatedAt={getUpdatedAt}
|
||||
width={width}
|
||||
/>
|
||||
</>
|
||||
</TimelineRefetch>
|
||||
)}
|
||||
</TimelineQuery>
|
||||
) : null}
|
||||
|
|
|
@ -1,79 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiPanel } from '@elastic/eui';
|
||||
import { range } from 'lodash/fp';
|
||||
import * as React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { Dispatch } from 'redux';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { WhoAmI } from '../containers/who_am_i';
|
||||
|
||||
import { DragEffects, DraggableWrapper } from './drag_and_drop/draggable_wrapper';
|
||||
import { mockDataProviders } from './timeline/data_providers/mock/mock_data_providers';
|
||||
import { Provider } from './timeline/data_providers/provider';
|
||||
|
||||
export const VisualizationPlaceholder = styled(EuiPanel)`
|
||||
&& {
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 5px;
|
||||
padding: 5px 5px 5px 10px;
|
||||
width: 500px;
|
||||
height: 320px;
|
||||
user-select: none;
|
||||
}
|
||||
`;
|
||||
|
||||
export const ProviderContainer = styled.div`
|
||||
margin: 5px;
|
||||
user-select: none;
|
||||
`;
|
||||
|
||||
interface Props {
|
||||
count: number;
|
||||
myRoute: string;
|
||||
dispatch: Dispatch;
|
||||
}
|
||||
|
||||
/** TODO: delete this stub */
|
||||
class PlaceholdersComponent extends React.PureComponent<Props> {
|
||||
public render() {
|
||||
const { count, myRoute } = this.props;
|
||||
|
||||
return (
|
||||
<>
|
||||
{range(0, count).map(i => (
|
||||
<VisualizationPlaceholder
|
||||
data-test-subj="visualizationPlaceholder"
|
||||
key={`visualizationPlaceholder-${i}`}
|
||||
>
|
||||
<WhoAmI data-test-subj="whoAmI" sourceId="default">
|
||||
{() => <div>{myRoute}</div>}
|
||||
</WhoAmI>
|
||||
<DraggableWrapper
|
||||
dataProvider={mockDataProviders[i]}
|
||||
render={(dataProvider, _, snapshot) =>
|
||||
snapshot.isDragging ? (
|
||||
<DragEffects>
|
||||
<Provider dataProvider={dataProvider} />
|
||||
</DragEffects>
|
||||
) : (
|
||||
mockDataProviders[i].name
|
||||
)
|
||||
}
|
||||
/>
|
||||
</VisualizationPlaceholder>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export const Placeholders = connect()(PlaceholdersComponent);
|
|
@ -51,13 +51,11 @@ class AuthenticationsComponentQuery extends QueryTemplate<
|
|||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
poll,
|
||||
} = this.props;
|
||||
return (
|
||||
<Query<GetAuthenticationsQuery.Query, GetAuthenticationsQuery.Variables>
|
||||
query={authenticationsQuery}
|
||||
fetchPolicy={getDefaultFetchPolicy()}
|
||||
pollInterval={poll}
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{
|
||||
sourceId,
|
||||
|
|
|
@ -58,7 +58,6 @@ class DomainsComponentQuery extends QueryTemplate<
|
|||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
poll,
|
||||
flowTarget,
|
||||
flowDirection,
|
||||
} = this.props;
|
||||
|
@ -66,7 +65,6 @@ class DomainsComponentQuery extends QueryTemplate<
|
|||
<Query<GetDomainsQuery.Query, GetDomainsQuery.Variables>
|
||||
query={domainsQuery}
|
||||
fetchPolicy="cache-and-network"
|
||||
pollInterval={poll}
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{
|
||||
sourceId,
|
||||
|
|
|
@ -48,7 +48,6 @@ class EventsComponentQuery extends QueryTemplate<
|
|||
filterQuery,
|
||||
id = 'eventsQuery',
|
||||
limit,
|
||||
poll,
|
||||
sourceId,
|
||||
startDate,
|
||||
endDate,
|
||||
|
@ -58,7 +57,6 @@ class EventsComponentQuery extends QueryTemplate<
|
|||
query={eventsQuery}
|
||||
fetchPolicy={getDefaultFetchPolicy()}
|
||||
notifyOnNetworkStatusChange
|
||||
pollInterval={poll}
|
||||
variables={{
|
||||
filterQuery: createFilter(filterQuery),
|
||||
sourceId,
|
||||
|
|
|
@ -11,10 +11,11 @@ import { ActionCreator } from 'typescript-fsa';
|
|||
import { inputsActions, inputsModel, inputsSelectors, State } from '../../store';
|
||||
|
||||
interface GlobalTimeArgs {
|
||||
poll: number;
|
||||
from: number;
|
||||
to: number;
|
||||
setQuery: ActionCreator<{ id: string; loading: boolean; refetch: inputsModel.Refetch }>;
|
||||
setQuery: (
|
||||
{ id, loading, refetch }: { id: string; loading: boolean; refetch: inputsModel.Refetch }
|
||||
) => void;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
|
@ -22,32 +23,36 @@ interface OwnProps {
|
|||
}
|
||||
|
||||
interface GlobalTimeDispatch {
|
||||
setQuery: ActionCreator<{ id: string; loading: boolean; refetch: inputsModel.Refetch }>;
|
||||
deleteAllQuery: () => void;
|
||||
setGlobalQuery: ActionCreator<{
|
||||
inputId: inputsModel.InputsModelId;
|
||||
id: string;
|
||||
loading: boolean;
|
||||
refetch: inputsModel.Refetch;
|
||||
}>;
|
||||
deleteAllQuery: ActionCreator<{ id: inputsModel.InputsModelId }>;
|
||||
}
|
||||
|
||||
interface GlobalTimeReduxState {
|
||||
from: number;
|
||||
to: number;
|
||||
poll: number;
|
||||
}
|
||||
|
||||
type GlobalTimeProps = OwnProps & GlobalTimeReduxState & GlobalTimeDispatch;
|
||||
|
||||
class GlobalTimeComponent extends React.PureComponent<GlobalTimeProps> {
|
||||
public componentDidMount() {
|
||||
this.props.deleteAllQuery();
|
||||
this.props.deleteAllQuery({ id: 'global' });
|
||||
}
|
||||
|
||||
public render() {
|
||||
const { children, poll, from, to, setQuery } = this.props;
|
||||
const { children, from, to, setGlobalQuery } = this.props;
|
||||
return (
|
||||
<>
|
||||
{children({
|
||||
poll,
|
||||
from,
|
||||
to,
|
||||
setQuery,
|
||||
setQuery: ({ id, loading, refetch }) =>
|
||||
setGlobalQuery({ inputId: 'global', id, loading, refetch }),
|
||||
})}
|
||||
</>
|
||||
);
|
||||
|
@ -56,9 +61,7 @@ class GlobalTimeComponent extends React.PureComponent<GlobalTimeProps> {
|
|||
|
||||
const mapStateToProps = (state: State) => {
|
||||
const timerange: inputsModel.TimeRange = inputsSelectors.globalTimeRangeSelector(state);
|
||||
const policy: inputsModel.Policy = inputsSelectors.globalPolicySelector(state);
|
||||
return {
|
||||
poll: policy.kind === 'interval' && timerange.kind === 'absolute' ? policy.duration : 0,
|
||||
from: timerange.from,
|
||||
to: timerange.to,
|
||||
};
|
||||
|
@ -68,6 +71,6 @@ export const GlobalTime = connect(
|
|||
mapStateToProps,
|
||||
{
|
||||
deleteAllQuery: inputsActions.deleteAllQuery,
|
||||
setQuery: inputsActions.setQuery,
|
||||
setGlobalQuery: inputsActions.setQuery,
|
||||
}
|
||||
)(GlobalTimeComponent);
|
||||
|
|
|
@ -17,7 +17,7 @@ import {
|
|||
PageInfo,
|
||||
} from '../../graphql/types';
|
||||
import { hostsModel, hostsSelectors, inputsModel, State } from '../../store';
|
||||
import { createFilter, getDefaultFetchPolicy } from '../helpers';
|
||||
import { createFilter } from '../helpers';
|
||||
import { QueryTemplate, QueryTemplateProps } from '../query_template';
|
||||
|
||||
import { HostsTableQuery } from './hosts_table.gql_query';
|
||||
|
@ -64,7 +64,6 @@ class HostsComponentQuery extends QueryTemplate<
|
|||
filterQuery,
|
||||
endDate,
|
||||
limit,
|
||||
poll,
|
||||
startDate,
|
||||
sourceId,
|
||||
sortField,
|
||||
|
@ -72,8 +71,7 @@ class HostsComponentQuery extends QueryTemplate<
|
|||
return (
|
||||
<Query<GetHostsTableQuery.Query, GetHostsTableQuery.Variables>
|
||||
query={HostsTableQuery}
|
||||
fetchPolicy={getDefaultFetchPolicy()}
|
||||
pollInterval={poll}
|
||||
fetchPolicy="cache-first"
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{
|
||||
sourceId,
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const kpiEventsQuery = gql`
|
||||
query GetKpiEventsQuery(
|
||||
$sourceId: ID!
|
||||
$timerange: TimerangeInput!
|
||||
$filterQuery: String
|
||||
$pagination: PaginationInput!
|
||||
$sortField: SortField!
|
||||
) {
|
||||
source(id: $sourceId) {
|
||||
id
|
||||
Events(
|
||||
timerange: $timerange
|
||||
filterQuery: $filterQuery
|
||||
pagination: $pagination
|
||||
sortField: $sortField
|
||||
) {
|
||||
kpiEventType {
|
||||
value
|
||||
count
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -1,73 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getOr } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { Query } from 'react-apollo';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { ESQuery } from '../../../common/typed_json';
|
||||
import { Direction, GetKpiEventsQuery, KpiItem } from '../../graphql/types';
|
||||
import { inputsModel } from '../../store';
|
||||
import { createFilter, getDefaultFetchPolicy } from '../helpers';
|
||||
|
||||
import { kpiEventsQuery } from './index.gql_query';
|
||||
|
||||
export interface KpiEventsArgs {
|
||||
id: string;
|
||||
kpiEventType: KpiItem[];
|
||||
loading: boolean;
|
||||
refetch: inputsModel.Refetch;
|
||||
}
|
||||
|
||||
export interface OwnProps {
|
||||
children?: (args: KpiEventsArgs) => React.ReactNode;
|
||||
id?: string;
|
||||
filterQuery?: ESQuery | string;
|
||||
poll: number;
|
||||
sourceId: string;
|
||||
startDate: number;
|
||||
endDate: number;
|
||||
}
|
||||
|
||||
export const KpiEventsQuery = pure<OwnProps>(
|
||||
({ children, filterQuery, id = 'kpiEventsQuery', poll, sourceId, startDate, endDate }) => (
|
||||
<Query<GetKpiEventsQuery.Query, GetKpiEventsQuery.Variables>
|
||||
query={kpiEventsQuery}
|
||||
fetchPolicy={getDefaultFetchPolicy()}
|
||||
notifyOnNetworkStatusChange
|
||||
pollInterval={poll}
|
||||
variables={{
|
||||
filterQuery: createFilter(filterQuery),
|
||||
sourceId,
|
||||
pagination: {
|
||||
limit: 0,
|
||||
cursor: null,
|
||||
tiebreaker: null,
|
||||
},
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
from: startDate,
|
||||
to: endDate,
|
||||
},
|
||||
sortField: {
|
||||
sortFieldId: 'timestamp',
|
||||
direction: Direction.desc,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{({ data, loading, fetchMore, refetch }) => {
|
||||
const kpiEventType = getOr([], 'source.Events.kpiEventType', data);
|
||||
return children!({
|
||||
id,
|
||||
refetch,
|
||||
loading,
|
||||
kpiEventType,
|
||||
});
|
||||
}}
|
||||
</Query>
|
||||
)
|
||||
);
|
|
@ -60,13 +60,11 @@ class NetworkDnsComponentQuery extends QueryTemplate<
|
|||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
poll,
|
||||
} = this.props;
|
||||
return (
|
||||
<Query<GetNetworkDnsQuery.Query, GetNetworkDnsQuery.Variables>
|
||||
query={networkDnsQuery}
|
||||
fetchPolicy="cache-and-network"
|
||||
pollInterval={poll}
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{
|
||||
sourceId,
|
||||
|
|
|
@ -61,7 +61,6 @@ class NetworkTopNFlowComponentQuery extends QueryTemplate<
|
|||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
poll,
|
||||
flowDirection,
|
||||
topNFlowSort,
|
||||
flowTarget,
|
||||
|
@ -70,7 +69,6 @@ class NetworkTopNFlowComponentQuery extends QueryTemplate<
|
|||
<Query<GetNetworkTopNFlowQuery.Query, GetNetworkTopNFlowQuery.Variables>
|
||||
query={networkTopNFlowQuery}
|
||||
fetchPolicy="cache-and-network"
|
||||
pollInterval={poll}
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{
|
||||
sourceId,
|
||||
|
|
|
@ -27,7 +27,6 @@ export interface OverviewHostProps extends QueryTemplateProps {
|
|||
children: (args: OverviewHostArgs) => React.ReactNode;
|
||||
sourceId: string;
|
||||
endDate: number;
|
||||
poll: number;
|
||||
startDate: number;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ export interface OverviewNetworkProps extends QueryTemplateProps {
|
|||
children: (args: OverviewNetworkArgs) => React.ReactNode;
|
||||
sourceId: string;
|
||||
endDate: number;
|
||||
poll: number;
|
||||
startDate: number;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ export interface QueryTemplateProps {
|
|||
startDate?: number;
|
||||
endDate?: number;
|
||||
filterQuery?: ESQuery | string;
|
||||
poll?: number;
|
||||
}
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
type FetchMoreOptionsArgs<TData, TVariables> = FetchMoreQueryOptions<any, any> &
|
||||
|
|
|
@ -60,7 +60,6 @@ export class TimelineQuery extends QueryTemplate<
|
|||
limit,
|
||||
fields,
|
||||
filterQuery,
|
||||
poll,
|
||||
sourceId,
|
||||
sortField,
|
||||
} = this.props;
|
||||
|
@ -76,7 +75,6 @@ export class TimelineQuery extends QueryTemplate<
|
|||
query={timelineQuery}
|
||||
fetchPolicy="network-only"
|
||||
notifyOnNetworkStatusChange
|
||||
pollInterval={poll}
|
||||
variables={variables}
|
||||
>
|
||||
{({ data, loading, fetchMore, refetch }) => {
|
||||
|
@ -98,7 +96,7 @@ export class TimelineQuery extends QueryTemplate<
|
|||
...fetchMoreResult,
|
||||
source: {
|
||||
...fetchMoreResult.source,
|
||||
Events: {
|
||||
Timeline: {
|
||||
...fetchMoreResult.source.Timeline,
|
||||
edges: [
|
||||
...prev.source.Timeline.edges,
|
||||
|
|
|
@ -51,13 +51,11 @@ class UncommonProcessesComponentQuery extends QueryTemplate<
|
|||
startDate,
|
||||
endDate,
|
||||
limit,
|
||||
poll,
|
||||
} = this.props;
|
||||
return (
|
||||
<Query<GetUncommonProcessesQuery.Query, GetUncommonProcessesQuery.Variables>
|
||||
query={uncommonProcessesQuery}
|
||||
fetchPolicy={getDefaultFetchPolicy()}
|
||||
pollInterval={poll}
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{
|
||||
sourceId,
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import gql from 'graphql-tag';
|
||||
|
||||
export const whoAmIQuery = gql`
|
||||
query WhoAmIQuery($sourceId: ID!) {
|
||||
source(id: $sourceId) {
|
||||
whoAmI {
|
||||
appName
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getOr } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { Query } from 'react-apollo';
|
||||
|
||||
import { WhoAmIQuery } from '../../graphql/types';
|
||||
|
||||
import { whoAmIQuery } from './index.gql_query';
|
||||
|
||||
interface WhoAmIArgs {
|
||||
appName: string;
|
||||
}
|
||||
|
||||
interface WHoAmIProps {
|
||||
children: (args: WhoAmIArgs) => React.ReactNode;
|
||||
sourceId: string;
|
||||
}
|
||||
|
||||
export const WhoAmI = ({ children, sourceId }: WHoAmIProps) => (
|
||||
<Query<WhoAmIQuery.Query, WhoAmIQuery.Variables>
|
||||
query={whoAmIQuery}
|
||||
fetchPolicy="no-cache"
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{ sourceId }}
|
||||
>
|
||||
{({ data }) =>
|
||||
children({
|
||||
appName: getOr('', 'source.whoAmI.appName', data),
|
||||
})
|
||||
}
|
||||
</Query>
|
||||
);
|
|
@ -90,8 +90,15 @@ export const mockGlobalState: State = {
|
|||
inputs: {
|
||||
global: {
|
||||
timerange: { kind: 'relative', fromStr: 'now-24h', toStr: 'now', from: 0, to: 1 },
|
||||
linkTo: ['timeline'],
|
||||
query: [],
|
||||
policy: { kind: 'manual', duration: 5000 },
|
||||
policy: { kind: 'manual', duration: 300000 },
|
||||
},
|
||||
timeline: {
|
||||
timerange: { kind: 'relative', fromStr: 'now-24h', toStr: 'now', from: 0, to: 1 },
|
||||
linkTo: ['global'],
|
||||
query: [],
|
||||
policy: { kind: 'manual', duration: 300000 },
|
||||
},
|
||||
},
|
||||
dragAndDrop: { dataProviders: {} },
|
||||
|
|
|
@ -62,7 +62,7 @@ const HostDetailsComponent = pure<HostDetailsComponentProps>(
|
|||
<PageContent data-test-subj="pageContent" panelPaddingSize="none">
|
||||
<PageContentBody data-test-subj="pane1ScrollContainer">
|
||||
<GlobalTime>
|
||||
{({ poll, to, from, setQuery }) => (
|
||||
{({ to, from, setQuery }) => (
|
||||
<>
|
||||
<HostDetailsByNameQuery
|
||||
sourceId="default"
|
||||
|
@ -75,8 +75,6 @@ const HostDetailsComponent = pure<HostDetailsComponentProps>(
|
|||
id={id}
|
||||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
startDate={from}
|
||||
endDate={to}
|
||||
data={hostDetails}
|
||||
loading={loading}
|
||||
/>
|
||||
|
@ -86,7 +84,6 @@ const HostDetailsComponent = pure<HostDetailsComponentProps>(
|
|||
sourceId="default"
|
||||
startDate={from}
|
||||
endDate={to}
|
||||
poll={poll}
|
||||
filterQuery={getFilterQuery(hostName, filterQueryExpression, indexPattern)}
|
||||
type={type}
|
||||
>
|
||||
|
@ -104,7 +101,6 @@ const HostDetailsComponent = pure<HostDetailsComponentProps>(
|
|||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
loading={loading}
|
||||
startDate={from}
|
||||
data={authentications}
|
||||
totalCount={totalCount}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
|
@ -118,7 +114,6 @@ const HostDetailsComponent = pure<HostDetailsComponentProps>(
|
|||
sourceId="default"
|
||||
startDate={from}
|
||||
endDate={to}
|
||||
poll={poll}
|
||||
filterQuery={getFilterQuery(hostName, filterQueryExpression, indexPattern)}
|
||||
type={type}
|
||||
>
|
||||
|
@ -136,7 +131,6 @@ const HostDetailsComponent = pure<HostDetailsComponentProps>(
|
|||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
loading={loading}
|
||||
startDate={from}
|
||||
data={uncommonProcesses}
|
||||
totalCount={totalCount}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
|
@ -149,7 +143,6 @@ const HostDetailsComponent = pure<HostDetailsComponentProps>(
|
|||
<EventsQuery
|
||||
endDate={to}
|
||||
filterQuery={getFilterQuery(hostName, filterQueryExpression, indexPattern)}
|
||||
poll={poll}
|
||||
sourceId="default"
|
||||
startDate={from}
|
||||
type={type}
|
||||
|
@ -161,7 +154,6 @@ const HostDetailsComponent = pure<HostDetailsComponentProps>(
|
|||
setQuery={setQuery}
|
||||
data={events!}
|
||||
loading={loading}
|
||||
startDate={from}
|
||||
totalCount={totalCount}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
tiebreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)!}
|
||||
|
|
|
@ -49,14 +49,13 @@ const HostsComponent = pure<HostsComponentProps>(({ filterQuery }) => (
|
|||
<PageContent data-test-subj="pageContent" panelPaddingSize="none">
|
||||
<PageContentBody data-test-subj="pane1ScrollContainer">
|
||||
<GlobalTime>
|
||||
{({ poll, to, from, setQuery }) => (
|
||||
{({ to, from, setQuery }) => (
|
||||
<>
|
||||
<HostsQuery
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
sourceId="default"
|
||||
startDate={from}
|
||||
poll={poll}
|
||||
type={hostsModel.HostsType.page}
|
||||
>
|
||||
{({ hosts, totalCount, loading, pageInfo, loadMore, id, refetch }) => (
|
||||
|
@ -65,7 +64,6 @@ const HostsComponent = pure<HostsComponentProps>(({ filterQuery }) => (
|
|||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
loading={loading}
|
||||
startDate={from}
|
||||
data={hosts}
|
||||
totalCount={totalCount}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
|
@ -78,7 +76,6 @@ const HostsComponent = pure<HostsComponentProps>(({ filterQuery }) => (
|
|||
<UncommonProcessesQuery
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
poll={poll}
|
||||
sourceId="default"
|
||||
startDate={from}
|
||||
type={hostsModel.HostsType.page}
|
||||
|
@ -97,7 +94,6 @@ const HostsComponent = pure<HostsComponentProps>(({ filterQuery }) => (
|
|||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
loading={loading}
|
||||
startDate={from}
|
||||
data={uncommonProcesses}
|
||||
totalCount={totalCount}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
|
@ -110,7 +106,6 @@ const HostsComponent = pure<HostsComponentProps>(({ filterQuery }) => (
|
|||
<AuthenticationsQuery
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
poll={poll}
|
||||
sourceId="default"
|
||||
startDate={from}
|
||||
type={hostsModel.HostsType.page}
|
||||
|
@ -129,7 +124,6 @@ const HostsComponent = pure<HostsComponentProps>(({ filterQuery }) => (
|
|||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
loading={loading}
|
||||
startDate={from}
|
||||
data={authentications}
|
||||
totalCount={totalCount}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
|
@ -142,7 +136,6 @@ const HostsComponent = pure<HostsComponentProps>(({ filterQuery }) => (
|
|||
<EventsQuery
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
poll={poll}
|
||||
sourceId="default"
|
||||
startDate={from}
|
||||
type={hostsModel.HostsType.page}
|
||||
|
@ -154,7 +147,6 @@ const HostsComponent = pure<HostsComponentProps>(({ filterQuery }) => (
|
|||
setQuery={setQuery}
|
||||
data={events!}
|
||||
loading={loading}
|
||||
startDate={from}
|
||||
totalCount={totalCount}
|
||||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
tiebreaker={getOr(null, 'endCursor.tiebreaker', pageInfo)!}
|
||||
|
|
|
@ -66,7 +66,7 @@ const IPDetailsComponent = pure<IPDetailsComponentProps>(
|
|||
<PageContent data-test-subj="pageContent" panelPaddingSize="none">
|
||||
<PageContentBody data-test-subj="pane1ScrollContainer">
|
||||
<GlobalTime>
|
||||
{({ poll, to, from, setQuery }) => (
|
||||
{({ to, from, setQuery }) => (
|
||||
<>
|
||||
<IpOverviewQuery
|
||||
sourceId="default"
|
||||
|
@ -95,7 +95,6 @@ const IPDetailsComponent = pure<IPDetailsComponentProps>(
|
|||
filterQuery={filterQuery}
|
||||
flowTarget={flowTarget}
|
||||
ip={decodeIpv6(ip)}
|
||||
poll={poll}
|
||||
sourceId="default"
|
||||
startDate={from}
|
||||
type={networkModel.NetworkType.details}
|
||||
|
@ -112,8 +111,6 @@ const IPDetailsComponent = pure<IPDetailsComponentProps>(
|
|||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
startDate={from}
|
||||
endDate={to}
|
||||
totalCount={totalCount}
|
||||
type={networkModel.NetworkType.details}
|
||||
/>
|
||||
|
|
|
@ -46,12 +46,11 @@ const NetworkComponent = pure<NetworkComponentProps>(({ filterQuery }) => (
|
|||
<PageContent data-test-subj="pageContent" panelPaddingSize="none">
|
||||
<PageContentBody data-test-subj="pane1ScrollContainer">
|
||||
<GlobalTime>
|
||||
{({ poll, to, from, setQuery }) => (
|
||||
{({ to, from, setQuery }) => (
|
||||
<>
|
||||
<KpiNetworkQuery
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
poll={poll}
|
||||
sourceId="default"
|
||||
startDate={from}
|
||||
>
|
||||
|
@ -69,7 +68,6 @@ const NetworkComponent = pure<NetworkComponentProps>(({ filterQuery }) => (
|
|||
<NetworkTopNFlowQuery
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
poll={poll}
|
||||
sourceId="default"
|
||||
startDate={from}
|
||||
type={networkModel.NetworkType.page}
|
||||
|
@ -92,7 +90,6 @@ const NetworkComponent = pure<NetworkComponentProps>(({ filterQuery }) => (
|
|||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
startDate={from}
|
||||
totalCount={totalCount}
|
||||
type={networkModel.NetworkType.page}
|
||||
/>
|
||||
|
@ -102,7 +99,6 @@ const NetworkComponent = pure<NetworkComponentProps>(({ filterQuery }) => (
|
|||
<NetworkDnsQuery
|
||||
endDate={to}
|
||||
filterQuery={filterQuery}
|
||||
poll={poll}
|
||||
sourceId="default"
|
||||
startDate={from}
|
||||
type={networkModel.NetworkType.page}
|
||||
|
@ -117,7 +113,6 @@ const NetworkComponent = pure<NetworkComponentProps>(({ filterQuery }) => (
|
|||
nextCursor={getOr(null, 'endCursor.value', pageInfo)!}
|
||||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
startDate={from}
|
||||
totalCount={totalCount}
|
||||
type={networkModel.NetworkType.page}
|
||||
/>
|
||||
|
|
|
@ -20,11 +20,11 @@ export const OverviewComponent = pure(() => (
|
|||
<Welcome />
|
||||
|
||||
<GlobalTime>
|
||||
{({ poll, to, from, setQuery }) => (
|
||||
{({ to, from, setQuery }) => (
|
||||
<EuiFlexGroup gutterSize="xl">
|
||||
<Summary />
|
||||
<OverviewHost poll={poll} endDate={to} startDate={from} setQuery={setQuery} />
|
||||
<OverviewNetwork poll={poll} endDate={to} startDate={from} setQuery={setQuery} />
|
||||
<OverviewHost endDate={to} startDate={from} setQuery={setQuery} />
|
||||
<OverviewNetwork endDate={to} startDate={from} setQuery={setQuery} />
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
</GlobalTime>
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { combineEpics } from 'redux-observable';
|
||||
|
||||
import { createGlobalTimeEpic } from './inputs';
|
||||
|
||||
export const createRootEpic = <State>() => combineEpics(createGlobalTimeEpic<State>());
|
|
@ -27,7 +27,7 @@ export const initialHostsState: HostsState = {
|
|||
queries: {
|
||||
authentications: { limit: DEFAULT_TABLE_LIMIT },
|
||||
hosts: {
|
||||
limit: 5,
|
||||
limit: DEFAULT_TABLE_LIMIT,
|
||||
direction: Direction.desc,
|
||||
sortField: HostsFields.lastSeen,
|
||||
},
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
*/
|
||||
|
||||
export * from './actions';
|
||||
export * from './epic';
|
||||
export * from './model';
|
||||
export * from './reducer';
|
||||
export * from './selectors';
|
||||
|
|
|
@ -6,32 +6,39 @@
|
|||
|
||||
import actionCreatorFactory from 'typescript-fsa';
|
||||
|
||||
import { Refetch } from './model';
|
||||
import { InputsModelId, Refetch } from './model';
|
||||
|
||||
const actionCreator = actionCreatorFactory('x-pack/siem/local/inputs');
|
||||
|
||||
export const setAbsoluteRangeDatePicker = actionCreator<{
|
||||
id: string;
|
||||
id: InputsModelId;
|
||||
from: number;
|
||||
to: number;
|
||||
}>('SET_ABSOLUTE_RANGE_DATE_PICKER');
|
||||
|
||||
export const setRelativeRangeDatePicker = actionCreator<{
|
||||
id: string;
|
||||
id: InputsModelId;
|
||||
fromStr: string;
|
||||
toStr: string;
|
||||
from: number;
|
||||
to: number;
|
||||
}>('SET_RELATIVE_RANGE_DATE_PICKER');
|
||||
|
||||
export const setDuration = actionCreator<{ id: string; duration: number }>('SET_DURATION');
|
||||
export const setDuration = actionCreator<{ id: InputsModelId; duration: number }>('SET_DURATION');
|
||||
|
||||
export const startAutoReload = actionCreator<{ id: string }>('START_KQL_AUTO_RELOAD');
|
||||
export const startAutoReload = actionCreator<{ id: InputsModelId }>('START_KQL_AUTO_RELOAD');
|
||||
|
||||
export const stopAutoReload = actionCreator<{ id: string }>('STOP_KQL_AUTO_RELOAD');
|
||||
export const stopAutoReload = actionCreator<{ id: InputsModelId }>('STOP_KQL_AUTO_RELOAD');
|
||||
|
||||
export const setQuery = actionCreator<{ id: string; loading: boolean; refetch: Refetch }>(
|
||||
'SET_QUERY'
|
||||
export const setQuery = actionCreator<{
|
||||
inputId: InputsModelId;
|
||||
id: string;
|
||||
loading: boolean;
|
||||
refetch: Refetch;
|
||||
}>('SET_QUERY');
|
||||
|
||||
export const deleteAllQuery = actionCreator<{ id: InputsModelId }>('DELETE_ALL_QUERY');
|
||||
|
||||
export const toggleTimelineLinkTo = actionCreator<{ linkToId: InputsModelId }>(
|
||||
'TOGGLE_TIMELINE_LINK_TO'
|
||||
);
|
||||
|
||||
export const deleteAllQuery = actionCreator('DELETE_ALL_QUERY');
|
||||
|
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import dateMath from '@elastic/datemath';
|
||||
import { get } from 'lodash/fp';
|
||||
import { Action } from 'redux';
|
||||
import { Epic } from 'redux-observable';
|
||||
import { timer } from 'rxjs';
|
||||
import { exhaustMap, filter, map, takeUntil, withLatestFrom } from 'rxjs/operators';
|
||||
|
||||
import { setRelativeRangeDatePicker, startAutoReload, stopAutoReload } from './actions';
|
||||
import { Policy, TimeRange } from './model';
|
||||
|
||||
interface GlobalTimeEpicDependencies<State> {
|
||||
selectGlobalPolicy: (state: State) => Policy;
|
||||
selectGlobalTimeRange: (state: State) => TimeRange;
|
||||
}
|
||||
|
||||
export const createGlobalTimeEpic = <State>(): Epic<
|
||||
Action,
|
||||
Action,
|
||||
State,
|
||||
GlobalTimeEpicDependencies<State>
|
||||
> => (action$, state$, { selectGlobalPolicy, selectGlobalTimeRange }) => {
|
||||
const policy$ = state$.pipe(
|
||||
map(selectGlobalPolicy),
|
||||
filter(isNotNull)
|
||||
);
|
||||
|
||||
const timerange$ = state$.pipe(
|
||||
map(selectGlobalTimeRange),
|
||||
filter(isNotNull)
|
||||
);
|
||||
|
||||
return action$.pipe(
|
||||
filter(startAutoReload.match),
|
||||
withLatestFrom(policy$, timerange$),
|
||||
filter(
|
||||
([action, policy, timerange]) =>
|
||||
timerange.kind === 'relative' && timerange.toStr != null && timerange.toStr === 'now'
|
||||
),
|
||||
exhaustMap(([action, policy, timerange]) =>
|
||||
timer(0, policy.duration).pipe(
|
||||
map(() => {
|
||||
const fromStr = get('fromStr', timerange);
|
||||
const momentDate = fromStr != null ? dateMath.parse(fromStr) : null;
|
||||
return setRelativeRangeDatePicker({
|
||||
id: 'global',
|
||||
fromStr: fromStr != null ? fromStr : '',
|
||||
toStr: 'now',
|
||||
to: Date.now(),
|
||||
from: momentDate != null && momentDate.isValid() ? momentDate.valueOf() : 0,
|
||||
});
|
||||
}),
|
||||
takeUntil(action$.pipe(filter(stopAutoReload.match)))
|
||||
)
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
const isNotNull = <T>(value: T | null): value is T => value !== null;
|
95
x-pack/plugins/siem/public/store/inputs/helpers.test.ts
Normal file
95
x-pack/plugins/siem/public/store/inputs/helpers.test.ts
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { cloneDeep } from 'lodash/fp';
|
||||
|
||||
import { mockGlobalState } from '../../mock';
|
||||
|
||||
import { toggleLockTimeline, updateInputTimerange } from './helpers';
|
||||
import { InputsModel, TimeRange } from './model';
|
||||
|
||||
describe('Inputs', () => {
|
||||
let state = mockGlobalState.inputs;
|
||||
describe('#toggleLockTimeline', () => {
|
||||
beforeEach(() => {
|
||||
state = cloneDeep(mockGlobalState.inputs);
|
||||
});
|
||||
test('remove timeline Lock from inputs', () => {
|
||||
const newState: InputsModel = toggleLockTimeline('timeline', state);
|
||||
expect(newState.timeline.linkTo).toEqual([]);
|
||||
expect(newState.global.linkTo).toEqual([]);
|
||||
});
|
||||
|
||||
test('Add timeline Lock from inputs', () => {
|
||||
state.global.linkTo = [];
|
||||
const newState: InputsModel = toggleLockTimeline('timeline', state);
|
||||
expect(newState.timeline.linkTo).toEqual(['global']);
|
||||
expect(newState.global.linkTo).toEqual(['timeline']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#updateInputTimerange when timeline and global are lock', () => {
|
||||
beforeEach(() => {
|
||||
state = cloneDeep(mockGlobalState.inputs);
|
||||
});
|
||||
|
||||
test('timeline should stay identical when global change', () => {
|
||||
const newTimerange: TimeRange = {
|
||||
kind: 'relative',
|
||||
fromStr: 'now-48h',
|
||||
toStr: 'now',
|
||||
from: 23,
|
||||
to: 26,
|
||||
};
|
||||
const newState: InputsModel = updateInputTimerange('global', newTimerange, state);
|
||||
expect(newState.timeline.timerange).toEqual(newState.global.timerange);
|
||||
});
|
||||
|
||||
test('global should stay identical when timeline change', () => {
|
||||
const newTimerange: TimeRange = {
|
||||
kind: 'relative',
|
||||
fromStr: 'now-68h',
|
||||
toStr: 'NOTnow',
|
||||
from: 29,
|
||||
to: 33,
|
||||
};
|
||||
const newState: InputsModel = updateInputTimerange('timeline', newTimerange, state);
|
||||
expect(newState.timeline.timerange).toEqual(newState.global.timerange);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#updateInputTimerange when timeline and global are NOT lock', () => {
|
||||
beforeEach(() => {
|
||||
state = cloneDeep(toggleLockTimeline('timeline', mockGlobalState.inputs));
|
||||
});
|
||||
|
||||
test('timeline should stay identical when global change', () => {
|
||||
const newTimerange: TimeRange = {
|
||||
kind: 'relative',
|
||||
fromStr: 'now-48h',
|
||||
toStr: 'now',
|
||||
from: 23,
|
||||
to: 26,
|
||||
};
|
||||
const newState: InputsModel = updateInputTimerange('global', newTimerange, state);
|
||||
expect(newState.timeline.timerange).toEqual(state.timeline.timerange);
|
||||
expect(newState.global.timerange).toEqual(newTimerange);
|
||||
});
|
||||
|
||||
test('global should stay identical when timeline change', () => {
|
||||
const newTimerange: TimeRange = {
|
||||
kind: 'relative',
|
||||
fromStr: 'now-68h',
|
||||
toStr: 'NOTnow',
|
||||
from: 29,
|
||||
to: 33,
|
||||
};
|
||||
const newState: InputsModel = updateInputTimerange('timeline', newTimerange, state);
|
||||
expect(newState.timeline.timerange).toEqual(newTimerange);
|
||||
expect(newState.global.timerange).toEqual(state.timeline.timerange);
|
||||
});
|
||||
});
|
||||
});
|
54
x-pack/plugins/siem/public/store/inputs/helpers.ts
Normal file
54
x-pack/plugins/siem/public/store/inputs/helpers.ts
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { get } from 'lodash/fp';
|
||||
|
||||
import { InputsModel, InputsModelId, TimeRange } from './model';
|
||||
|
||||
export const updateInputTimerange = (
|
||||
inputId: InputsModelId,
|
||||
timerange: TimeRange,
|
||||
state: InputsModel
|
||||
): InputsModel => {
|
||||
const input = get(inputId, state);
|
||||
if (input != null) {
|
||||
return {
|
||||
...[inputId, ...input.linkTo].reduce<InputsModel>(
|
||||
(acc: InputsModel, linkToId: InputsModelId) => ({
|
||||
...acc,
|
||||
[linkToId]: {
|
||||
...get(linkToId, state),
|
||||
timerange,
|
||||
},
|
||||
}),
|
||||
inputId === 'timeline' ? { ...state, global: { ...state.global, linkTo: [] } } : state
|
||||
),
|
||||
};
|
||||
}
|
||||
return state;
|
||||
};
|
||||
|
||||
export const toggleLockTimeline = (linkToId: InputsModelId, state: InputsModel): InputsModel => {
|
||||
const linkToIdAlreadyExist = state.global.linkTo.indexOf(linkToId);
|
||||
return {
|
||||
...state,
|
||||
global: {
|
||||
...state.global,
|
||||
timerange: linkToIdAlreadyExist > -1 ? state.global.timerange : state.timeline.timerange,
|
||||
linkTo:
|
||||
linkToIdAlreadyExist > -1
|
||||
? [
|
||||
...state.global.linkTo.slice(0, linkToIdAlreadyExist),
|
||||
...state.global.linkTo.slice(linkToIdAlreadyExist + 1),
|
||||
]
|
||||
: [...state.global.linkTo, linkToId],
|
||||
},
|
||||
timeline: {
|
||||
...state.timeline,
|
||||
linkTo: linkToIdAlreadyExist > -1 ? [] : ['global'],
|
||||
},
|
||||
};
|
||||
};
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue