mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Figure out some bottleneck, in the hosts tab (#35387)
* Figure out some blottleneck, this is making the app more responsive * example how to remove the re-render on the host table * review I * review II * review III
This commit is contained in:
parent
ea5b009b72
commit
8fdf129544
28 changed files with 634 additions and 219 deletions
|
@ -34,7 +34,7 @@ interface AutocompleteFieldState {
|
|||
selectedIndex: number | null;
|
||||
}
|
||||
|
||||
export class AutocompleteField extends React.Component<
|
||||
export class AutocompleteField extends React.PureComponent<
|
||||
AutocompleteFieldProps,
|
||||
AutocompleteFieldState
|
||||
> {
|
||||
|
|
|
@ -18,7 +18,7 @@ interface SuggestionItemProps {
|
|||
suggestion: AutocompleteSuggestion;
|
||||
}
|
||||
|
||||
export class SuggestionItem extends React.Component<SuggestionItemProps> {
|
||||
export class SuggestionItem extends React.PureComponent<SuggestionItemProps> {
|
||||
public static defaultProps: Partial<SuggestionItemProps> = {
|
||||
isSelected: false,
|
||||
};
|
||||
|
|
|
@ -4,11 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import euiDarkVars from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
import { mount, shallow } from 'enzyme';
|
||||
import toJson from 'enzyme-to-json';
|
||||
import * as React from 'react';
|
||||
import { ThemeProvider } from 'styled-components';
|
||||
|
||||
import { Direction } from '../../graphql/types';
|
||||
|
||||
|
@ -61,21 +59,21 @@ describe('Load More Table Component', () => {
|
|||
|
||||
test('it renders the over loading panel after data has been in the table ', () => {
|
||||
const wrapper = mount(
|
||||
<ThemeProvider theme={() => ({ eui: euiDarkVars, darkMode: true })}>
|
||||
<LoadMoreTable
|
||||
columns={getHostsColumns()}
|
||||
loadingTitle="Hosts"
|
||||
loading={true}
|
||||
pageOfItems={mockData.Hosts.edges}
|
||||
loadMore={() => loadMore(mockData.Hosts.pageInfo.endCursor)}
|
||||
limit={1}
|
||||
hasNextPage={mockData.Hosts.pageInfo.hasNextPage!}
|
||||
itemsPerRow={rowItems}
|
||||
updateLimitPagination={newlimit => updateLimitPagination({ limit: newlimit })}
|
||||
title={<h3>Hosts</h3>}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
<LoadMoreTable
|
||||
columns={getHostsColumns()}
|
||||
loadingTitle="Hosts"
|
||||
loading={false}
|
||||
pageOfItems={mockData.Hosts.edges}
|
||||
loadMore={() => loadMore(mockData.Hosts.pageInfo.endCursor)}
|
||||
limit={1}
|
||||
hasNextPage={mockData.Hosts.pageInfo.hasNextPage!}
|
||||
itemsPerRow={rowItems}
|
||||
updateLimitPagination={newlimit => updateLimitPagination({ limit: newlimit })}
|
||||
title={<h3>Hosts</h3>}
|
||||
/>
|
||||
);
|
||||
wrapper.setState({ isEmptyTable: false });
|
||||
wrapper.setProps({ loading: true });
|
||||
|
||||
expect(wrapper.find('[data-test-subj="LoadingPanelLoadMoreTable"]').exists()).toBeTruthy();
|
||||
});
|
||||
|
@ -119,8 +117,9 @@ describe('Load More Table Component', () => {
|
|||
title={<h3>Hosts</h3>}
|
||||
/>
|
||||
);
|
||||
wrapper.setState({ paginationLoading: true, isEmptyTable: false });
|
||||
|
||||
wrapper.setProps({ loading: true });
|
||||
|
||||
expect(
|
||||
wrapper.find('[data-test-subj="InitialLoadingPanelLoadMoreTable"]').exists()
|
||||
).toBeFalsy();
|
||||
|
|
|
@ -15,7 +15,7 @@ import {
|
|||
EuiPopover,
|
||||
EuiTitle,
|
||||
} from '@elastic/eui';
|
||||
import { isEmpty, noop } from 'lodash/fp';
|
||||
import { isEmpty, noop, getOr } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
|
@ -79,21 +79,14 @@ export class LoadMoreTable<T> extends React.PureComponent<BasicTableProps<T>, Ba
|
|||
paginationLoading: false,
|
||||
};
|
||||
|
||||
public componentDidUpdate(prevProps: BasicTableProps<T>) {
|
||||
const { paginationLoading, isEmptyTable } = this.state;
|
||||
const { loading, pageOfItems } = this.props;
|
||||
if (paginationLoading && prevProps.loading && !loading) {
|
||||
this.setState({
|
||||
...this.state,
|
||||
paginationLoading: false,
|
||||
});
|
||||
}
|
||||
if (isEmpty(prevProps.pageOfItems) && !isEmpty(pageOfItems) && isEmptyTable) {
|
||||
this.setState({
|
||||
...this.state,
|
||||
static getDerivedStateFromProps(props: BasicTableProps<any>, state: BasicTableState) {
|
||||
if (state.isEmptyTable && !isEmpty(props.pageOfItems)) {
|
||||
return {
|
||||
...state,
|
||||
isEmptyTable: false,
|
||||
});
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
@ -110,7 +103,7 @@ export class LoadMoreTable<T> extends React.PureComponent<BasicTableProps<T>, Ba
|
|||
title,
|
||||
updateLimitPagination,
|
||||
} = this.props;
|
||||
const { isEmptyTable, paginationLoading } = this.state;
|
||||
const { isEmptyTable } = this.state;
|
||||
|
||||
if (loading && isEmptyTable) {
|
||||
return (
|
||||
|
@ -149,10 +142,9 @@ export class LoadMoreTable<T> extends React.PureComponent<BasicTableProps<T>, Ba
|
|||
{item.text}
|
||||
</EuiContextMenuItem>
|
||||
));
|
||||
|
||||
return (
|
||||
<BasicTableContainer>
|
||||
{!paginationLoading && loading && (
|
||||
{loading && (
|
||||
<>
|
||||
<BackgroundRefetch />
|
||||
<LoadingPanel
|
||||
|
@ -216,7 +208,7 @@ export class LoadMoreTable<T> extends React.PureComponent<BasicTableProps<T>, Ba
|
|||
<EuiButton
|
||||
data-test-subj="loadingMoreButton"
|
||||
isLoading={loading}
|
||||
onClick={this.loadMore}
|
||||
onClick={this.props.loadMore}
|
||||
>
|
||||
{loading ? `${i18n.LOADING}...` : i18n.LOAD_MORE}
|
||||
</EuiButton>
|
||||
|
@ -230,14 +222,6 @@ export class LoadMoreTable<T> extends React.PureComponent<BasicTableProps<T>, Ba
|
|||
);
|
||||
}
|
||||
|
||||
private loadMore = () => {
|
||||
this.setState({
|
||||
...this.state,
|
||||
paginationLoading: true,
|
||||
});
|
||||
this.props.loadMore();
|
||||
};
|
||||
|
||||
private onButtonClick = () => {
|
||||
this.setState({
|
||||
...this.state,
|
||||
|
@ -266,8 +250,12 @@ const FooterAction = styled.div`
|
|||
width: 100%;
|
||||
`;
|
||||
|
||||
/*
|
||||
* The getOr is just there to simplify the test
|
||||
* So we do NOT need to wrap it around TestProvider
|
||||
*/
|
||||
const BackgroundRefetch = styled.div`
|
||||
background-color: ${props => props.theme.eui.euiColorLightShade};
|
||||
background-color: ${props => getOr('#ffffff', 'theme.eui.euiColorLightShade', props)};
|
||||
margin: -5px;
|
||||
height: calc(100% + 10px);
|
||||
opacity: 0.7;
|
||||
|
|
|
@ -17,6 +17,7 @@ export class SiemNavigationComponent extends React.Component<RouteComponentProps
|
|||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public componentWillMount(): void {
|
||||
const { location } = this.props;
|
||||
if (location.pathname) {
|
||||
|
|
|
@ -15,6 +15,91 @@ exports[`AddToKql Component Rendering 1`] = `
|
|||
<pure(Component)
|
||||
componentFilterType="hosts"
|
||||
expression="host.name: siem-kibana"
|
||||
indexPattern={
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@timestamp",
|
||||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@version",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.ephemeral_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.hostname",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test1",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test2",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test3",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test4",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test5",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test6",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test7",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test8",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
],
|
||||
"title": "filebeat-*,auditbeat-*,packetbeat-*",
|
||||
}
|
||||
}
|
||||
type="page"
|
||||
>
|
||||
siem-kibana
|
||||
|
|
|
@ -9,7 +9,7 @@ import toJson from 'enzyme-to-json';
|
|||
import * as React from 'react';
|
||||
|
||||
import { escapeQueryValue } from '../../../lib/keury';
|
||||
import { mockGlobalState, TestProviders } from '../../../mock';
|
||||
import { mockGlobalState, TestProviders, mockIndexPattern } from '../../../mock';
|
||||
import { createStore, hostsModel, networkModel, State } from '../../../store';
|
||||
|
||||
import { AddToKql } from '.';
|
||||
|
@ -26,6 +26,7 @@ describe('AddToKql Component', async () => {
|
|||
const wrapper = shallow(
|
||||
<TestProviders store={store}>
|
||||
<AddToKql
|
||||
indexPattern={mockIndexPattern}
|
||||
expression={`host.name: ${escapeQueryValue('siem-kibana')}`}
|
||||
componentFilterType="hosts"
|
||||
type={hostsModel.HostsType.page}
|
||||
|
@ -42,6 +43,7 @@ describe('AddToKql Component', async () => {
|
|||
const wrapper = shallow(
|
||||
<TestProviders store={store}>
|
||||
<AddToKql
|
||||
indexPattern={mockIndexPattern}
|
||||
expression={`host.name: ${escapeQueryValue('siem-kibana')}`}
|
||||
componentFilterType="hosts"
|
||||
type={hostsModel.HostsType.page}
|
||||
|
@ -60,6 +62,7 @@ describe('AddToKql Component', async () => {
|
|||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<AddToKql
|
||||
indexPattern={mockIndexPattern}
|
||||
expression={`host.name: ${escapeQueryValue('siem-kibana')}`}
|
||||
componentFilterType="hosts"
|
||||
type={hostsModel.HostsType.page}
|
||||
|
@ -112,6 +115,7 @@ describe('AddToKql Component', async () => {
|
|||
const wrapper = mount(
|
||||
<TestProviders store={store}>
|
||||
<AddToKql
|
||||
indexPattern={mockIndexPattern}
|
||||
expression={`host.name: ${escapeQueryValue('siem-kibana')}`}
|
||||
componentFilterType="network"
|
||||
type={networkModel.NetworkType.page}
|
||||
|
|
|
@ -9,11 +9,10 @@ import { isEmpty } from 'lodash/fp';
|
|||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
import { StaticIndexPattern } from 'ui/index_patterns';
|
||||
|
||||
import { HostsFilter } from '../../../containers/hosts';
|
||||
import { NetworkFilter } from '../../../containers/network';
|
||||
import { WithSource } from '../../../containers/source';
|
||||
import { IndexType } from '../../../graphql/types';
|
||||
import { assertUnreachable } from '../../../lib/helpers';
|
||||
import { hostsModel, KueryFilterQuery, networkModel } from '../../../store';
|
||||
import { WithHoverActions } from '../../with_hover_actions';
|
||||
|
@ -69,47 +68,44 @@ const HoverActionsContainer = styled(EuiPanel)`
|
|||
|
||||
interface AddToKqlProps {
|
||||
children: JSX.Element;
|
||||
indexPattern: StaticIndexPattern;
|
||||
expression: string;
|
||||
componentFilterType: 'network' | 'hosts';
|
||||
type: networkModel.NetworkType | hostsModel.HostsType;
|
||||
}
|
||||
|
||||
export const AddToKql = pure<AddToKqlProps>(
|
||||
({ children, expression, type, componentFilterType }) => (
|
||||
<WithSource sourceId="default" indexTypes={[IndexType.FILEBEAT, IndexType.PACKETBEAT]}>
|
||||
{({ indexPattern }) => {
|
||||
switch (componentFilterType) {
|
||||
case 'hosts':
|
||||
return (
|
||||
<HostsFilter indexPattern={indexPattern} type={type as hostsModel.HostsType}>
|
||||
{({ applyFilterQueryFromKueryExpression, filterQueryDraft }) => (
|
||||
<AddToKqlComponent
|
||||
applyFilterQueryFromKueryExpression={applyFilterQueryFromKueryExpression}
|
||||
expression={expression}
|
||||
filterQueryDraft={filterQueryDraft}
|
||||
>
|
||||
{children}
|
||||
</AddToKqlComponent>
|
||||
)}
|
||||
</HostsFilter>
|
||||
);
|
||||
case 'network':
|
||||
return (
|
||||
<NetworkFilter indexPattern={indexPattern} type={type as networkModel.NetworkType}>
|
||||
{({ applyFilterQueryFromKueryExpression, filterQueryDraft }) => (
|
||||
<AddToKqlComponent
|
||||
applyFilterQueryFromKueryExpression={applyFilterQueryFromKueryExpression}
|
||||
expression={expression}
|
||||
filterQueryDraft={filterQueryDraft}
|
||||
>
|
||||
{children}
|
||||
</AddToKqlComponent>
|
||||
)}
|
||||
</NetworkFilter>
|
||||
);
|
||||
}
|
||||
assertUnreachable(componentFilterType, 'Unknown Filter Type in switch statement');
|
||||
}}
|
||||
</WithSource>
|
||||
)
|
||||
({ children, expression, type, componentFilterType, indexPattern }) => {
|
||||
switch (componentFilterType) {
|
||||
case 'hosts':
|
||||
return (
|
||||
<HostsFilter indexPattern={indexPattern} type={type as hostsModel.HostsType}>
|
||||
{({ applyFilterQueryFromKueryExpression, filterQueryDraft }) => (
|
||||
<AddToKqlComponent
|
||||
applyFilterQueryFromKueryExpression={applyFilterQueryFromKueryExpression}
|
||||
expression={expression}
|
||||
filterQueryDraft={filterQueryDraft}
|
||||
>
|
||||
{children}
|
||||
</AddToKqlComponent>
|
||||
)}
|
||||
</HostsFilter>
|
||||
);
|
||||
case 'network':
|
||||
return (
|
||||
<NetworkFilter indexPattern={indexPattern} type={type as networkModel.NetworkType}>
|
||||
{({ applyFilterQueryFromKueryExpression, filterQueryDraft }) => (
|
||||
<AddToKqlComponent
|
||||
applyFilterQueryFromKueryExpression={applyFilterQueryFromKueryExpression}
|
||||
expression={expression}
|
||||
filterQueryDraft={filterQueryDraft}
|
||||
>
|
||||
{children}
|
||||
</AddToKqlComponent>
|
||||
)}
|
||||
</NetworkFilter>
|
||||
);
|
||||
}
|
||||
assertUnreachable(componentFilterType, 'Unknown Filter Type in switch statement');
|
||||
}
|
||||
);
|
||||
|
|
|
@ -46,6 +46,91 @@ exports[`Load More Table Component rendering it renders the default Hosts table
|
|||
]
|
||||
}
|
||||
hasNextPage={true}
|
||||
indexPattern={
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@timestamp",
|
||||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@version",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.ephemeral_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.hostname",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test1",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test2",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test3",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test4",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test5",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test6",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test7",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test8",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
],
|
||||
"title": "filebeat-*,auditbeat-*,packetbeat-*",
|
||||
}
|
||||
}
|
||||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="aa7ca589f1b8220002f2fc61c64cfbf1"
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
import { EuiIcon, EuiToolTip } from '@elastic/eui';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { StaticIndexPattern } from 'ui/index_patterns';
|
||||
|
||||
import { HostItem } from '../../../../graphql/types';
|
||||
import { ValueOf } from '../../../../lib/helpers';
|
||||
|
@ -24,7 +25,10 @@ import { AddToKql } from '../../add_to_kql';
|
|||
|
||||
import * as i18n from './translations';
|
||||
|
||||
export const getHostsColumns = (type: hostsModel.HostsType): Array<Columns<ValueOf<HostItem>>> => [
|
||||
export const getHostsColumns = (
|
||||
type: hostsModel.HostsType,
|
||||
indexPattern: StaticIndexPattern
|
||||
): Array<Columns<ValueOf<HostItem>>> => [
|
||||
{
|
||||
field: 'node.host.name',
|
||||
name: i18n.NAME,
|
||||
|
@ -53,6 +57,7 @@ export const getHostsColumns = (type: hostsModel.HostsType): Array<Columns<Value
|
|||
</DragEffects>
|
||||
) : (
|
||||
<AddToKql
|
||||
indexPattern={indexPattern}
|
||||
expression={`host.name: ${escapeQueryValue(hostName)}`}
|
||||
componentFilterType="hosts"
|
||||
type={type}
|
||||
|
@ -101,6 +106,7 @@ export const getHostsColumns = (type: hostsModel.HostsType): Array<Columns<Value
|
|||
if (hostOsName != null) {
|
||||
return (
|
||||
<AddToKql
|
||||
indexPattern={indexPattern}
|
||||
expression={`host.os.name: ${escapeQueryValue(hostOsName)}`}
|
||||
componentFilterType="hosts"
|
||||
type={type}
|
||||
|
@ -122,6 +128,7 @@ export const getHostsColumns = (type: hostsModel.HostsType): Array<Columns<Value
|
|||
if (hostOsVersion != null) {
|
||||
return (
|
||||
<AddToKql
|
||||
indexPattern={indexPattern}
|
||||
expression={`host.os.version: ${escapeQueryValue(hostOsVersion)}`}
|
||||
componentFilterType="hosts"
|
||||
type={type}
|
||||
|
|
|
@ -11,7 +11,7 @@ import * as React from 'react';
|
|||
import { MockedProvider } from 'react-apollo/test-utils';
|
||||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
|
||||
import { mockFrameworks, mockGlobalState, TestProviders } from '../../../../mock';
|
||||
import { mockFrameworks, mockIndexPattern, mockGlobalState, TestProviders } from '../../../../mock';
|
||||
import { createStore, hostsModel, State } from '../../../../store';
|
||||
import { KibanaConfigContext } from '../../../formatted_date';
|
||||
|
||||
|
@ -34,6 +34,7 @@ describe('Load More Table Component', () => {
|
|||
<ReduxStoreProvider store={store}>
|
||||
<KibanaConfigContext.Provider value={mockFrameworks.default_UTC}>
|
||||
<HostsTable
|
||||
indexPattern={mockIndexPattern}
|
||||
loading={false}
|
||||
data={mockData.Hosts.edges}
|
||||
totalCount={mockData.Hosts.totalCount}
|
||||
|
@ -54,6 +55,7 @@ describe('Load More Table Component', () => {
|
|||
<MockedProvider>
|
||||
<TestProviders store={store}>
|
||||
<HostsTable
|
||||
indexPattern={mockIndexPattern}
|
||||
loading={false}
|
||||
data={mockData.Hosts.edges}
|
||||
totalCount={mockData.Hosts.totalCount}
|
||||
|
@ -71,6 +73,7 @@ describe('Load More Table Component', () => {
|
|||
<MockedProvider>
|
||||
<TestProviders store={store}>
|
||||
<HostsTable
|
||||
indexPattern={mockIndexPattern}
|
||||
loading={false}
|
||||
data={mockData.Hosts.edges}
|
||||
totalCount={mockData.Hosts.totalCount}
|
||||
|
|
|
@ -4,24 +4,37 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { EuiIconTip } from '@elastic/eui';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
import { StaticIndexPattern } from 'ui/index_patterns';
|
||||
|
||||
import { Direction, HostsEdges, HostsFields, HostsSortField } from '../../../../graphql/types';
|
||||
import { assertUnreachable } from '../../../../lib/helpers';
|
||||
import {
|
||||
Direction,
|
||||
HostsEdges,
|
||||
HostsFields,
|
||||
HostsSortField,
|
||||
HostItem,
|
||||
} from '../../../../graphql/types';
|
||||
import { assertUnreachable, ValueOf } from '../../../../lib/helpers';
|
||||
import { hostsActions, hostsModel, hostsSelectors, State } from '../../../../store';
|
||||
import { Criteria, ItemsPerRow, LoadMoreTable } from '../../../load_more_table';
|
||||
import { CountBadge } from '../../index';
|
||||
import {
|
||||
Criteria,
|
||||
ItemsPerRow,
|
||||
LoadMoreTable,
|
||||
Columns,
|
||||
SortingBasicTable,
|
||||
} from '../../../load_more_table';
|
||||
|
||||
import { getHostsColumns } from './columns';
|
||||
import * as i18n from './translations';
|
||||
import { TableTitle } from '../../table_title';
|
||||
|
||||
interface OwnProps {
|
||||
data: HostsEdges[];
|
||||
loading: boolean;
|
||||
indexPattern: StaticIndexPattern;
|
||||
hasNextPage: boolean;
|
||||
nextCursor: string;
|
||||
totalCount: number;
|
||||
|
@ -68,54 +81,75 @@ const rowItems: ItemsPerRow[] = [
|
|||
},
|
||||
];
|
||||
|
||||
const Sup = styled.sup`
|
||||
vertical-align: super;
|
||||
padding: 0 5px;
|
||||
`;
|
||||
|
||||
class HostsTableComponent extends React.PureComponent<HostsTableProps> {
|
||||
private memoizedColumns: (
|
||||
type: hostsModel.HostsType,
|
||||
indexPattern: StaticIndexPattern
|
||||
) => Array<Columns<ValueOf<HostItem>>>;
|
||||
private memoizedTitle: (totalCount: number) => JSX.Element;
|
||||
private memoizedSorting: (
|
||||
trigger: string,
|
||||
sortField: HostsFields,
|
||||
direction: Direction
|
||||
) => SortingBasicTable;
|
||||
|
||||
constructor(props: HostsTableProps) {
|
||||
super(props);
|
||||
this.memoizedColumns = memoizeOne(this.getMemoizeHostsColumns);
|
||||
this.memoizedTitle = memoizeOne(this.getTitle);
|
||||
this.memoizedSorting = memoizeOne(this.getSorting);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
data,
|
||||
direction,
|
||||
hasNextPage,
|
||||
indexPattern,
|
||||
limit,
|
||||
loading,
|
||||
loadMore,
|
||||
totalCount,
|
||||
nextCursor,
|
||||
updateLimitPagination,
|
||||
sortField,
|
||||
type,
|
||||
} = this.props;
|
||||
return (
|
||||
<LoadMoreTable
|
||||
columns={getHostsColumns(type)}
|
||||
columns={this.memoizedColumns(type, indexPattern)}
|
||||
loadingTitle={i18n.HOSTS}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
loadMore={() => loadMore(nextCursor)}
|
||||
loadMore={this.wrappedLoadMore}
|
||||
limit={limit}
|
||||
hasNextPage={hasNextPage}
|
||||
itemsPerRow={rowItems}
|
||||
onChange={this.onChange}
|
||||
updateLimitPagination={newLimit =>
|
||||
updateLimitPagination({ limit: newLimit, hostsType: type })
|
||||
}
|
||||
sorting={{ field: getNodeField(sortField), direction }}
|
||||
title={
|
||||
<h3>
|
||||
{i18n.HOSTS}
|
||||
<Sup>
|
||||
<EuiIconTip content={i18n.TOOLTIP} position="right" />
|
||||
</Sup>
|
||||
<CountBadge color="hollow">{totalCount}</CountBadge>
|
||||
</h3>
|
||||
}
|
||||
updateLimitPagination={this.wrappedUpdateLimitPagination}
|
||||
sorting={this.memoizedSorting(`${sortField}-${direction}`, sortField, direction)}
|
||||
title={this.memoizedTitle(totalCount)}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
private getSorting = (
|
||||
trigger: string,
|
||||
sortField: HostsFields,
|
||||
direction: Direction
|
||||
): SortingBasicTable => ({ field: getNodeField(sortField), direction });
|
||||
|
||||
private getTitle = (totalCount: number): JSX.Element => (
|
||||
<TableTitle title={i18n.HOSTS} infoTooltip={i18n.TOOLTIP} totalCount={totalCount} />
|
||||
);
|
||||
|
||||
private wrappedUpdateLimitPagination = (newLimit: number) =>
|
||||
this.props.updateLimitPagination({ limit: newLimit, hostsType: this.props.type });
|
||||
|
||||
private wrappedLoadMore = () => this.props.loadMore(this.props.nextCursor);
|
||||
|
||||
private getMemoizeHostsColumns = (
|
||||
type: hostsModel.HostsType,
|
||||
indexPattern: StaticIndexPattern
|
||||
): Array<Columns<ValueOf<HostItem>>> => getHostsColumns(type, indexPattern);
|
||||
|
||||
private onChange = (criteria: Criteria) => {
|
||||
if (criteria.sort != null) {
|
||||
const sort: HostsSortField = {
|
||||
|
|
|
@ -65,6 +65,91 @@ exports[`Domains Table Component Rendering it renders the default Domains table
|
|||
}
|
||||
flowTarget="source"
|
||||
hasNextPage={false}
|
||||
indexPattern={
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@timestamp",
|
||||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@version",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.ephemeral_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.hostname",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test1",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test2",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test3",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test4",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test5",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test6",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test7",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test8",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
],
|
||||
"title": "filebeat-*,auditbeat-*,packetbeat-*",
|
||||
}
|
||||
}
|
||||
ip="10.10.10.10"
|
||||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
|
|
|
@ -9,6 +9,7 @@ import numeral from '@elastic/numeral';
|
|||
import { getOr, isEmpty } from 'lodash/fp';
|
||||
import moment from 'moment';
|
||||
import React from 'react';
|
||||
import { StaticIndexPattern } from 'ui/index_patterns';
|
||||
|
||||
import {
|
||||
DomainsItem,
|
||||
|
@ -32,6 +33,7 @@ import { AddToKql } from '../../add_to_kql';
|
|||
import * as i18n from './translations';
|
||||
|
||||
export const getDomainsColumns = (
|
||||
indexPattern: StaticIndexPattern,
|
||||
ip: string,
|
||||
flowDirection: FlowDirection,
|
||||
flowTarget: FlowTarget,
|
||||
|
@ -89,6 +91,7 @@ export const getDomainsColumns = (
|
|||
: directions &&
|
||||
directions.map((direction, index) => (
|
||||
<AddToKql
|
||||
indexPattern={indexPattern}
|
||||
key={escapeDataProviderId(
|
||||
`${tableId}-table-${flowTarget}-${flowDirection}-direction-${direction}`
|
||||
)}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { MockedProvider } from 'react-apollo/test-utils';
|
|||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
|
||||
import { FlowTarget } from '../../../../graphql/types';
|
||||
import { mockGlobalState, TestProviders } from '../../../../mock';
|
||||
import { mockIndexPattern, mockGlobalState, TestProviders } from '../../../../mock';
|
||||
import { createStore, networkModel, State } from '../../../../store';
|
||||
|
||||
import { DomainsTable } from '.';
|
||||
|
@ -34,6 +34,7 @@ describe('Domains Table Component', () => {
|
|||
const wrapper = shallow(
|
||||
<ReduxStoreProvider store={store}>
|
||||
<DomainsTable
|
||||
indexPattern={mockIndexPattern}
|
||||
ip={ip}
|
||||
totalCount={1}
|
||||
loading={false}
|
||||
|
@ -57,6 +58,7 @@ describe('Domains Table Component', () => {
|
|||
<MockedProvider>
|
||||
<TestProviders store={store}>
|
||||
<DomainsTable
|
||||
indexPattern={mockIndexPattern}
|
||||
ip={ip}
|
||||
totalCount={1}
|
||||
loading={false}
|
||||
|
|
|
@ -9,6 +9,7 @@ import { isEqual } from 'lodash/fp';
|
|||
import React from 'react';
|
||||
import { connect } from 'react-redux';
|
||||
import { ActionCreator } from 'redux';
|
||||
import { StaticIndexPattern } from 'ui/index_patterns';
|
||||
|
||||
import {
|
||||
Direction,
|
||||
|
@ -31,6 +32,7 @@ interface OwnProps {
|
|||
flowTarget: FlowTarget;
|
||||
loading: boolean;
|
||||
hasNextPage: boolean;
|
||||
indexPattern: StaticIndexPattern;
|
||||
ip: string;
|
||||
nextCursor: string;
|
||||
totalCount: number;
|
||||
|
@ -88,6 +90,7 @@ class DomainsTableComponent extends React.PureComponent<DomainsTableProps> {
|
|||
data,
|
||||
domainsSortField,
|
||||
hasNextPage,
|
||||
indexPattern,
|
||||
ip,
|
||||
limit,
|
||||
loading,
|
||||
|
@ -102,7 +105,14 @@ class DomainsTableComponent extends React.PureComponent<DomainsTableProps> {
|
|||
|
||||
return (
|
||||
<LoadMoreTable
|
||||
columns={getDomainsColumns(ip, flowDirection, flowTarget, type, DomainsTableId)}
|
||||
columns={getDomainsColumns(
|
||||
indexPattern,
|
||||
ip,
|
||||
flowDirection,
|
||||
flowTarget,
|
||||
type,
|
||||
DomainsTableId
|
||||
)}
|
||||
loadingTitle={i18n.DOMAINS}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
|
|
|
@ -52,6 +52,91 @@ exports[`NetworkTopNFlow Table Component rendering it renders the default Networ
|
|||
]
|
||||
}
|
||||
hasNextPage={true}
|
||||
indexPattern={
|
||||
Object {
|
||||
"fields": Array [
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@timestamp",
|
||||
"searchable": true,
|
||||
"type": "date",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "@version",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.ephemeral_id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.hostname",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.id",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test1",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test2",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test3",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test4",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test5",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test6",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test7",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
Object {
|
||||
"aggregatable": true,
|
||||
"name": "agent.test8",
|
||||
"searchable": true,
|
||||
"type": "string",
|
||||
},
|
||||
],
|
||||
"title": "filebeat-*,auditbeat-*,packetbeat-*",
|
||||
}
|
||||
}
|
||||
loadMore={[MockFunction]}
|
||||
loading={false}
|
||||
nextCursor="10"
|
||||
|
|
|
@ -9,6 +9,7 @@ import numeral from '@elastic/numeral';
|
|||
import { get, isEmpty } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { StaticIndexPattern } from 'ui/index_patterns';
|
||||
|
||||
import {
|
||||
FlowDirection,
|
||||
|
@ -31,6 +32,7 @@ import { AddToKql } from '../../add_to_kql';
|
|||
import * as i18n from './translations';
|
||||
|
||||
export const getNetworkTopNFlowColumns = (
|
||||
indexPattern: StaticIndexPattern,
|
||||
flowDirection: FlowDirection,
|
||||
flowTarget: FlowTarget,
|
||||
type: networkModel.NetworkType,
|
||||
|
@ -151,6 +153,7 @@ export const getNetworkTopNFlowColumns = (
|
|||
: directions &&
|
||||
directions.map((direction, index) => (
|
||||
<AddToKql
|
||||
indexPattern={indexPattern}
|
||||
key={escapeDataProviderId(
|
||||
`${tableId}-table-${flowTarget}-${flowDirection}-direction-${direction}`
|
||||
)}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { MockedProvider } from 'react-apollo/test-utils';
|
|||
import { Provider as ReduxStoreProvider } from 'react-redux';
|
||||
|
||||
import { FlowDirection } from '../../../../graphql/types';
|
||||
import { mockGlobalState, TestProviders } from '../../../../mock';
|
||||
import { mockIndexPattern, mockGlobalState, TestProviders } from '../../../../mock';
|
||||
import { createStore, networkModel, State } from '../../../../store';
|
||||
|
||||
import { NetworkTopNFlowTable, NetworkTopNFlowTableId } from '.';
|
||||
|
@ -33,6 +33,7 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
const wrapper = shallow(
|
||||
<ReduxStoreProvider store={store}>
|
||||
<NetworkTopNFlowTable
|
||||
indexPattern={mockIndexPattern}
|
||||
loading={false}
|
||||
data={mockData.NetworkTopNFlow.edges}
|
||||
totalCount={mockData.NetworkTopNFlow.totalCount}
|
||||
|
@ -58,6 +59,7 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
<MockedProvider>
|
||||
<TestProviders store={store}>
|
||||
<NetworkTopNFlowTable
|
||||
indexPattern={mockIndexPattern}
|
||||
loading={false}
|
||||
data={mockData.NetworkTopNFlow.edges}
|
||||
totalCount={mockData.NetworkTopNFlow.totalCount}
|
||||
|
@ -91,6 +93,7 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
<MockedProvider>
|
||||
<TestProviders store={store}>
|
||||
<NetworkTopNFlowTable
|
||||
indexPattern={mockIndexPattern}
|
||||
loading={false}
|
||||
data={mockData.NetworkTopNFlow.edges}
|
||||
totalCount={mockData.NetworkTopNFlow.totalCount}
|
||||
|
@ -131,6 +134,7 @@ describe('NetworkTopNFlow Table Component', () => {
|
|||
<MockedProvider>
|
||||
<TestProviders store={store}>
|
||||
<NetworkTopNFlowTable
|
||||
indexPattern={mockIndexPattern}
|
||||
loading={false}
|
||||
data={mockData.NetworkTopNFlow.edges}
|
||||
totalCount={mockData.NetworkTopNFlow.totalCount}
|
||||
|
|
|
@ -10,6 +10,7 @@ import React from 'react';
|
|||
import { connect } from 'react-redux';
|
||||
import styled from 'styled-components';
|
||||
import { ActionCreator } from 'typescript-fsa';
|
||||
import { StaticIndexPattern } from 'ui/index_patterns';
|
||||
|
||||
import {
|
||||
FlowDirection,
|
||||
|
@ -29,6 +30,7 @@ import * as i18n from './translations';
|
|||
|
||||
interface OwnProps {
|
||||
data: NetworkTopNFlowEdges[];
|
||||
indexPattern: StaticIndexPattern;
|
||||
loading: boolean;
|
||||
hasNextPage: boolean;
|
||||
nextCursor: string;
|
||||
|
@ -92,6 +94,7 @@ class NetworkTopNFlowTableComponent extends React.PureComponent<NetworkTopNFlowT
|
|||
const {
|
||||
data,
|
||||
hasNextPage,
|
||||
indexPattern,
|
||||
limit,
|
||||
loading,
|
||||
loadMore,
|
||||
|
@ -112,7 +115,13 @@ class NetworkTopNFlowTableComponent extends React.PureComponent<NetworkTopNFlowT
|
|||
|
||||
return (
|
||||
<LoadMoreTable
|
||||
columns={getNetworkTopNFlowColumns(flowDirection, flowTarget, type, NetworkTopNFlowTableId)}
|
||||
columns={getNetworkTopNFlowColumns(
|
||||
indexPattern,
|
||||
flowDirection,
|
||||
flowTarget,
|
||||
type,
|
||||
NetworkTopNFlowTableId
|
||||
)}
|
||||
loadingTitle={i18n.TOP_TALKERS}
|
||||
loading={loading}
|
||||
pageOfItems={data}
|
||||
|
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* 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 { EuiIconTip } from '@elastic/eui';
|
||||
import { isEmpty } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { CountBadge } from '../index';
|
||||
|
||||
interface OwnProps {
|
||||
title: string;
|
||||
infoTooltip: string;
|
||||
totalCount: number;
|
||||
}
|
||||
|
||||
export const TableTitle = pure<OwnProps>(({ title, infoTooltip, totalCount }) => (
|
||||
<h3>
|
||||
{title}
|
||||
{!isEmpty(infoTooltip) && (
|
||||
<Sup>
|
||||
<EuiIconTip content={infoTooltip} position="right" />
|
||||
</Sup>
|
||||
)}
|
||||
<CountBadge color="hollow">{totalCount}</CountBadge>
|
||||
</h3>
|
||||
));
|
||||
|
||||
const Sup = styled.sup`
|
||||
vertical-align: super;
|
||||
padding: 0 5px;
|
||||
`;
|
|
@ -4,7 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getOr } from 'lodash/fp';
|
||||
import { get, getOr } from 'lodash/fp';
|
||||
import memoizeOne from 'memoize-one';
|
||||
import React from 'react';
|
||||
import { Query } from 'react-apollo';
|
||||
import { connect } from 'react-redux';
|
||||
|
@ -56,6 +57,16 @@ class HostsComponentQuery extends QueryTemplate<
|
|||
GetHostsTableQuery.Query,
|
||||
GetHostsTableQuery.Variables
|
||||
> {
|
||||
private memoizedHosts: (
|
||||
variables: string,
|
||||
data: GetHostsTableQuery.Source | undefined
|
||||
) => HostsEdges[];
|
||||
|
||||
constructor(props: HostsProps) {
|
||||
super(props);
|
||||
this.memoizedHosts = memoizeOne(this.getHosts);
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {
|
||||
id = 'hostsQuery',
|
||||
|
@ -68,32 +79,32 @@ class HostsComponentQuery extends QueryTemplate<
|
|||
sourceId,
|
||||
sortField,
|
||||
} = this.props;
|
||||
const variables: GetHostsTableQuery.Variables = {
|
||||
sourceId,
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
from: startDate,
|
||||
to: endDate,
|
||||
},
|
||||
sort: {
|
||||
direction,
|
||||
field: sortField,
|
||||
},
|
||||
pagination: {
|
||||
limit,
|
||||
cursor: null,
|
||||
tiebreaker: null,
|
||||
},
|
||||
filterQuery: createFilter(filterQuery),
|
||||
};
|
||||
return (
|
||||
<Query<GetHostsTableQuery.Query, GetHostsTableQuery.Variables>
|
||||
query={HostsTableQuery}
|
||||
fetchPolicy="cache-first"
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{
|
||||
sourceId,
|
||||
timerange: {
|
||||
interval: '12h',
|
||||
from: startDate,
|
||||
to: endDate,
|
||||
},
|
||||
sort: {
|
||||
direction,
|
||||
field: sortField,
|
||||
},
|
||||
pagination: {
|
||||
limit,
|
||||
cursor: null,
|
||||
tiebreaker: null,
|
||||
},
|
||||
filterQuery: createFilter(filterQuery),
|
||||
}}
|
||||
variables={variables}
|
||||
>
|
||||
{({ data, loading, fetchMore, refetch }) => {
|
||||
const hosts = getOr([], 'source.Hosts.edges', data);
|
||||
this.setFetchMore(fetchMore);
|
||||
this.setFetchMoreOptions((newCursor: string) => ({
|
||||
variables: {
|
||||
|
@ -123,7 +134,7 @@ class HostsComponentQuery extends QueryTemplate<
|
|||
refetch,
|
||||
loading,
|
||||
totalCount: getOr(0, 'source.Hosts.totalCount', data),
|
||||
hosts,
|
||||
hosts: this.memoizedHosts(JSON.stringify(variables), get('source', data)),
|
||||
startDate,
|
||||
endDate,
|
||||
pageInfo: getOr({}, 'source.Hosts.pageInfo', data),
|
||||
|
@ -133,6 +144,11 @@ class HostsComponentQuery extends QueryTemplate<
|
|||
</Query>
|
||||
);
|
||||
}
|
||||
|
||||
private getHosts = (
|
||||
variables: string,
|
||||
source: GetHostsTableQuery.Source | undefined
|
||||
): HostsEdges[] => getOr([], 'Hosts.edges', source);
|
||||
}
|
||||
|
||||
const makeMapStateToProps = () => {
|
||||
|
|
|
@ -33,7 +33,7 @@ interface KueryAutocompletionLifecycleState {
|
|||
suggestions: AutocompleteSuggestion[];
|
||||
}
|
||||
|
||||
export class KueryAutocompletion extends React.Component<
|
||||
export class KueryAutocompletion extends React.PureComponent<
|
||||
KueryAutocompletionLifecycleProps,
|
||||
KueryAutocompletionLifecycleState
|
||||
> {
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { isUndefined } from 'lodash';
|
||||
import { get, memoize, pick, set } from 'lodash/fp';
|
||||
import { get, memoize, pick, set, difference } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { Query } from 'react-apollo';
|
||||
import { StaticIndexPattern, StaticIndexPatternField } from 'ui/index_patterns';
|
||||
|
@ -43,8 +43,11 @@ interface WithSourceProps {
|
|||
}
|
||||
|
||||
export class WithSource extends React.PureComponent<WithSourceProps> {
|
||||
private memoizedIndexFields: (title: string, fields: IndexField[]) => StaticIndexPatternField[];
|
||||
private memoizedBrowserFields: (title: string, fields: IndexField[]) => BrowserFields;
|
||||
private memoizedIndexFields: (
|
||||
indexTypes: string[],
|
||||
fields: IndexField[]
|
||||
) => StaticIndexPatternField[];
|
||||
private memoizedBrowserFields: (indexTypes: string[], fields: IndexField[]) => BrowserFields;
|
||||
|
||||
constructor(props: WithSourceProps) {
|
||||
super(props);
|
||||
|
@ -59,7 +62,7 @@ export class WithSource extends React.PureComponent<WithSourceProps> {
|
|||
query={sourceQuery}
|
||||
fetchPolicy="cache-first"
|
||||
notifyOnNetworkStatusChange
|
||||
variables={{ sourceId, indexTypes }}
|
||||
variables={{ sourceId, indexTypes: [IndexType.ANY] }}
|
||||
>
|
||||
{({ data }) => {
|
||||
const logAlias = get('source.configuration.logAlias', data);
|
||||
|
@ -89,20 +92,21 @@ export class WithSource extends React.PureComponent<WithSourceProps> {
|
|||
indexPatternTitle = [...indexPatternTitle, winlogbeatAlias];
|
||||
}
|
||||
}
|
||||
const indexTypesLowerCase = indexTypes.map(i => i.toLocaleLowerCase());
|
||||
return children({
|
||||
auditbeatIndicesExist: get('source.status.auditbeatIndicesExist', data),
|
||||
filebeatIndicesExist: get('source.status.filebeatIndicesExist', data),
|
||||
winlogbeatIndicesExist: get('source.status.winlogbeatIndicesExist', data),
|
||||
browserFields: get('source.status.indexFields', data)
|
||||
? this.memoizedBrowserFields(
|
||||
indexPatternTitle.join(),
|
||||
indexTypesLowerCase,
|
||||
get('source.status.indexFields', data)
|
||||
)
|
||||
: {},
|
||||
indexPattern: {
|
||||
fields: get('source.status.indexFields', data)
|
||||
? this.memoizedIndexFields(
|
||||
indexPatternTitle.join(),
|
||||
indexTypesLowerCase,
|
||||
get('source.status.indexFields', data)
|
||||
)
|
||||
: [],
|
||||
|
@ -114,15 +118,30 @@ export class WithSource extends React.PureComponent<WithSourceProps> {
|
|||
);
|
||||
}
|
||||
|
||||
private getIndexFields = (title: string, fields: IndexField[]): StaticIndexPatternField[] =>
|
||||
fields.map(field => pick(['name', 'searchable', 'type', 'aggregatable'], field));
|
||||
private getIndexFields = (
|
||||
indexTypes: string[],
|
||||
fields: IndexField[]
|
||||
): StaticIndexPatternField[] =>
|
||||
fields
|
||||
.filter(
|
||||
item =>
|
||||
indexTypes.includes('any') ||
|
||||
difference(item.indexes, indexTypes).length !== item.indexes.length
|
||||
)
|
||||
.map(field => pick(['name', 'searchable', 'type', 'aggregatable'], field));
|
||||
|
||||
private getBrowserFields = (title: string, fields: IndexField[]): BrowserFields =>
|
||||
fields.reduce(
|
||||
(accumulator: BrowserFields, field: IndexField) =>
|
||||
set([field.category, 'fields', field.name], field, accumulator),
|
||||
{} as BrowserFields
|
||||
);
|
||||
private getBrowserFields = (indexTypes: string[], fields: IndexField[]): BrowserFields =>
|
||||
fields
|
||||
.filter(
|
||||
item =>
|
||||
indexTypes.includes('any') ||
|
||||
difference(item.indexes, indexTypes).length !== item.indexes.length
|
||||
)
|
||||
.reduce(
|
||||
(accumulator: BrowserFields, field: IndexField) =>
|
||||
set([field.category, 'fields', field.name], field, accumulator),
|
||||
{} as BrowserFields
|
||||
);
|
||||
}
|
||||
|
||||
export const indicesExistOrDataTemporarilyUnavailable = (indicesExist: boolean | undefined) =>
|
||||
|
|
|
@ -2184,44 +2184,6 @@ export namespace GetIpOverviewQuery {
|
|||
};
|
||||
}
|
||||
|
||||
export namespace GetKpiEventsQuery {
|
||||
export type Variables = {
|
||||
sourceId: string;
|
||||
timerange: TimerangeInput;
|
||||
filterQuery?: string | null;
|
||||
pagination: PaginationInput;
|
||||
sortField: SortField;
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
|
||||
source: Source;
|
||||
};
|
||||
|
||||
export type Source = {
|
||||
__typename?: 'Source';
|
||||
|
||||
id: string;
|
||||
|
||||
Events: Events;
|
||||
};
|
||||
|
||||
export type Events = {
|
||||
__typename?: 'EventsData';
|
||||
|
||||
kpiEventType?: KpiEventType[] | null;
|
||||
};
|
||||
|
||||
export type KpiEventType = {
|
||||
__typename?: 'KpiItem';
|
||||
|
||||
value?: string | null;
|
||||
|
||||
count: number;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace GetKpiNetworkQuery {
|
||||
export type Variables = {
|
||||
sourceId: string;
|
||||
|
@ -3440,27 +3402,3 @@ export namespace GetUncommonProcessesQuery {
|
|||
value: string;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace WhoAmIQuery {
|
||||
export type Variables = {
|
||||
sourceId: string;
|
||||
};
|
||||
|
||||
export type Query = {
|
||||
__typename?: 'Query';
|
||||
|
||||
source: Source;
|
||||
};
|
||||
|
||||
export type Source = {
|
||||
__typename?: 'Source';
|
||||
|
||||
whoAmI?: WhoAmI | null;
|
||||
};
|
||||
|
||||
export type WhoAmI = {
|
||||
__typename?: 'SayMyName';
|
||||
|
||||
appName: string;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -61,6 +61,7 @@ const HostsComponent = pure<HostsComponentProps>(({ filterQuery }) => (
|
|||
{({ hosts, totalCount, loading, pageInfo, loadMore, id, refetch }) => (
|
||||
<HostsTableManage
|
||||
id={id}
|
||||
indexPattern={indexPattern}
|
||||
refetch={refetch}
|
||||
setQuery={setQuery}
|
||||
loading={loading}
|
||||
|
|
|
@ -102,6 +102,7 @@ const IPDetailsComponent = pure<IPDetailsComponentProps>(
|
|||
{({ id, domains, totalCount, pageInfo, loading, loadMore, refetch }) => (
|
||||
<DomainsTableManage
|
||||
data={domains}
|
||||
indexPattern={indexPattern}
|
||||
id={id}
|
||||
flowTarget={flowTarget}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
|
|
|
@ -83,6 +83,7 @@ const NetworkComponent = pure<NetworkComponentProps>(({ filterQuery }) => (
|
|||
}) => (
|
||||
<NetworkTopNFlowTableManage
|
||||
data={networkTopNFlow}
|
||||
indexPattern={indexPattern}
|
||||
id={id}
|
||||
hasNextPage={getOr(false, 'hasNextPage', pageInfo)!}
|
||||
loading={loading}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue