mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
* [SIEM] Kpi network enhancement (#36915) * add histogram * clean up with hooks * fix types and inport * update mock data * fix barchart * fix integration test * wrapping hooks with functional component * update snapshot * remove active agents and network events histogram from kpi network * amend chart height * fix integration test * update wording * add readonly type * add references * update translations and add constants * clean up types for kpi host * clean up types for kpinetwork * remove a redundant type * fix tests * replace react memo with recompose pure * add unit test and update mock data * remove redundant type * update translation index
This commit is contained in:
parent
0fdafb9c42
commit
bc70842706
38 changed files with 1368 additions and 817 deletions
|
@ -8,11 +8,11 @@ import { mount, ReactWrapper } from 'enzyme';
|
|||
import * as React from 'react';
|
||||
|
||||
import { AreaChartBaseComponent, AreaChartWithCustomPrompt } from './areachart';
|
||||
import { AreaChartData } from './common';
|
||||
import { ChartConfigsData } from './common';
|
||||
|
||||
describe('AreaChartBaseComponent', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
const mockAreaChartData: AreaChartData[] = [
|
||||
const mockAreaChartData: ChartConfigsData[] = [
|
||||
{
|
||||
key: 'uniqueSourceIpsHistogram',
|
||||
value: [
|
||||
|
@ -99,7 +99,7 @@ describe('AreaChartWithCustomPrompt', () => {
|
|||
],
|
||||
],
|
||||
],
|
||||
])('renders areachart', (data: AreaChartData[] | [] | null | undefined) => {
|
||||
])('renders areachart', (data: ChartConfigsData[] | [] | null | undefined) => {
|
||||
beforeAll(() => {
|
||||
wrapper = mount(<AreaChartWithCustomPrompt height={100} width={120} data={data} />);
|
||||
});
|
||||
|
@ -181,7 +181,7 @@ describe('AreaChartWithCustomPrompt', () => {
|
|||
},
|
||||
],
|
||||
],
|
||||
])('renders prompt', (data: AreaChartData[] | [] | null | undefined) => {
|
||||
])('renders prompt', (data: ChartConfigsData[] | [] | null | undefined) => {
|
||||
beforeAll(() => {
|
||||
wrapper = mount(<AreaChartWithCustomPrompt height={100} width={120} data={data} />);
|
||||
});
|
||||
|
|
|
@ -13,14 +13,16 @@ import {
|
|||
getSpecId,
|
||||
Position,
|
||||
ScaleType,
|
||||
Settings,
|
||||
} from '@elastic/charts';
|
||||
import '@elastic/charts/dist/style.css';
|
||||
import {
|
||||
AreaChartData,
|
||||
ChartConfigsData,
|
||||
ChartHolder,
|
||||
getSeriesStyle,
|
||||
numberFormatter,
|
||||
WrappedByAutoSizer,
|
||||
getTheme,
|
||||
} from './common';
|
||||
import { AutoSizer } from '../auto_sizer';
|
||||
|
||||
|
@ -28,6 +30,7 @@ const dateFormatter = (d: string) => {
|
|||
return d.toLocaleString().split('T')[0];
|
||||
};
|
||||
|
||||
// custom series styles: https://ela.st/areachart-styling
|
||||
const getSeriesLineStyle = (color: string | undefined) => {
|
||||
return color
|
||||
? {
|
||||
|
@ -57,14 +60,16 @@ const getSeriesLineStyle = (color: string | undefined) => {
|
|||
: undefined;
|
||||
};
|
||||
|
||||
// https://ela.st/multi-areaseries
|
||||
export const AreaChartBaseComponent = React.memo<{
|
||||
data: AreaChartData[];
|
||||
data: ChartConfigsData[];
|
||||
width: number | null | undefined;
|
||||
height: number | null | undefined;
|
||||
}>(({ data, ...chartConfigs }) => {
|
||||
return chartConfigs.width && chartConfigs.height ? (
|
||||
<div style={{ height: chartConfigs.height, width: chartConfigs.width, position: 'relative' }}>
|
||||
<Chart>
|
||||
<Settings theme={getTheme()} />
|
||||
{data.map(series => {
|
||||
const seriesKey = series.key;
|
||||
const seriesSpecId = getSpecId(seriesKey);
|
||||
|
@ -103,7 +108,7 @@ export const AreaChartBaseComponent = React.memo<{
|
|||
});
|
||||
|
||||
export const AreaChartWithCustomPrompt = React.memo<{
|
||||
data: AreaChartData[] | null | undefined;
|
||||
data: ChartConfigsData[] | null | undefined;
|
||||
height: number | null | undefined;
|
||||
width: number | null | undefined;
|
||||
}>(({ data, height, width }) => {
|
||||
|
@ -121,7 +126,7 @@ export const AreaChartWithCustomPrompt = React.memo<{
|
|||
);
|
||||
});
|
||||
|
||||
export const AreaChart = React.memo<{ areaChart: AreaChartData[] | null | undefined }>(
|
||||
export const AreaChart = React.memo<{ areaChart: ChartConfigsData[] | null | undefined }>(
|
||||
({ areaChart }) => (
|
||||
<AutoSizer detectAnyWindowResize={false} content>
|
||||
{({ measureRef, content: { height, width } }) => (
|
||||
|
|
|
@ -8,11 +8,11 @@ import { mount, ReactWrapper } from 'enzyme';
|
|||
import * as React from 'react';
|
||||
|
||||
import { BarChartBaseComponent, BarChartWithCustomPrompt } from './barchart';
|
||||
import { BarChartData } from './common';
|
||||
import { ChartConfigsData } from './common';
|
||||
|
||||
describe('BarChartBaseComponent', () => {
|
||||
let wrapper: ReactWrapper;
|
||||
const mockBarChartData: BarChartData[] = [
|
||||
const mockBarChartData: ChartConfigsData[] = [
|
||||
{
|
||||
key: 'uniqueSourceIps',
|
||||
value: [{ y: 1714, x: 'uniqueSourceIps', g: 'uniqueSourceIps' }],
|
||||
|
@ -155,7 +155,7 @@ describe.each([
|
|||
},
|
||||
],
|
||||
],
|
||||
])('renders prompt', (data: BarChartData[] | [] | null | undefined) => {
|
||||
])('renders prompt', (data: ChartConfigsData[] | [] | null | undefined) => {
|
||||
let wrapper: ReactWrapper;
|
||||
beforeAll(() => {
|
||||
wrapper = mount(<BarChartWithCustomPrompt height={100} width={120} data={data} />);
|
||||
|
|
|
@ -6,39 +6,22 @@
|
|||
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Chart,
|
||||
BarSeries,
|
||||
Axis,
|
||||
Position,
|
||||
getSpecId,
|
||||
ScaleType,
|
||||
Settings,
|
||||
mergeWithDefaultTheme,
|
||||
PartialTheme,
|
||||
} from '@elastic/charts';
|
||||
import { Chart, BarSeries, Axis, Position, getSpecId, ScaleType, Settings } from '@elastic/charts';
|
||||
import { getAxisId } from '@elastic/charts';
|
||||
import {
|
||||
BarChartData,
|
||||
ChartConfigsData,
|
||||
WrappedByAutoSizer,
|
||||
ChartHolder,
|
||||
numberFormatter,
|
||||
SeriesType,
|
||||
getSeriesStyle,
|
||||
getTheme,
|
||||
} from './common';
|
||||
import { AutoSizer } from '../auto_sizer';
|
||||
|
||||
const getTheme = () => {
|
||||
const theme: PartialTheme = {
|
||||
scales: {
|
||||
barsPadding: 0.5,
|
||||
},
|
||||
};
|
||||
return mergeWithDefaultTheme(theme);
|
||||
};
|
||||
|
||||
// Bar chart rotation: https://ela.st/chart-rotations
|
||||
export const BarChartBaseComponent = React.memo<{
|
||||
data: BarChartData[];
|
||||
data: ChartConfigsData[];
|
||||
width: number | null | undefined;
|
||||
height: number | null | undefined;
|
||||
}>(({ data, ...chartConfigs }) => {
|
||||
|
@ -82,7 +65,7 @@ export const BarChartBaseComponent = React.memo<{
|
|||
});
|
||||
|
||||
export const BarChartWithCustomPrompt = React.memo<{
|
||||
data: BarChartData[] | null | undefined;
|
||||
data: ChartConfigsData[] | null | undefined;
|
||||
height: number | null | undefined;
|
||||
width: number | null | undefined;
|
||||
}>(({ data, height, width }) => {
|
||||
|
@ -98,7 +81,7 @@ export const BarChartWithCustomPrompt = React.memo<{
|
|||
);
|
||||
});
|
||||
|
||||
export const BarChart = React.memo<{ barChart: BarChartData[] | null | undefined }>(
|
||||
export const BarChart = React.memo<{ barChart: ChartConfigsData[] | null | undefined }>(
|
||||
({ barChart }) => (
|
||||
<AutoSizer detectAnyWindowResize={false} content>
|
||||
{({ measureRef, content: { height, width } }) => (
|
||||
|
|
|
@ -6,9 +6,16 @@
|
|||
import { EuiFlexGroup, EuiText, EuiFlexItem } from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { CustomSeriesColorsMap, DataSeriesColorsValues, getSpecId } from '@elastic/charts';
|
||||
import {
|
||||
CustomSeriesColorsMap,
|
||||
DataSeriesColorsValues,
|
||||
getSpecId,
|
||||
mergeWithDefaultTheme,
|
||||
PartialTheme,
|
||||
} from '@elastic/charts';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
const chartHeight = 74;
|
||||
const FlexGroup = styled(EuiFlexGroup)`
|
||||
height: 100%;
|
||||
`;
|
||||
|
@ -25,12 +32,6 @@ export const ChartHolder = () => (
|
|||
</FlexGroup>
|
||||
);
|
||||
|
||||
export interface AreaChartData {
|
||||
key: string;
|
||||
value: ChartData[] | [] | null;
|
||||
color?: string | undefined;
|
||||
}
|
||||
|
||||
export interface ChartData {
|
||||
x: number | string | null;
|
||||
y: number | string | null;
|
||||
|
@ -38,14 +39,14 @@ export interface ChartData {
|
|||
g?: number | string;
|
||||
}
|
||||
|
||||
export interface BarChartData {
|
||||
export interface ChartConfigsData {
|
||||
key: string;
|
||||
value: [ChartData] | [] | null;
|
||||
value: ChartData[] | [] | null;
|
||||
color?: string | undefined;
|
||||
}
|
||||
|
||||
export const WrappedByAutoSizer = styled.div`
|
||||
height: 100px;
|
||||
height: ${chartHeight}px;
|
||||
position: relative;
|
||||
|
||||
&:hover {
|
||||
|
@ -63,6 +64,7 @@ export enum SeriesType {
|
|||
LINE = 'line',
|
||||
}
|
||||
|
||||
// Customize colors: https://ela.st/custom-colors
|
||||
export const getSeriesStyle = (
|
||||
seriesKey: string,
|
||||
color: string | undefined,
|
||||
|
@ -79,3 +81,26 @@ export const getSeriesStyle = (
|
|||
|
||||
return customSeriesColors;
|
||||
};
|
||||
|
||||
// Apply margins and paddings: https://ela.st/charts-spacing
|
||||
export const getTheme = () => {
|
||||
const theme: PartialTheme = {
|
||||
chartMargins: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
// Apply some paddings to the top to avoid chopping the y tick https://ela.st/chopping-edge
|
||||
top: 4,
|
||||
bottom: 0,
|
||||
},
|
||||
chartPaddings: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0,
|
||||
},
|
||||
scales: {
|
||||
barsPadding: 0.5,
|
||||
},
|
||||
};
|
||||
return mergeWithDefaultTheme(theme);
|
||||
};
|
||||
|
|
|
@ -5,19 +5,20 @@
|
|||
*/
|
||||
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { get, getOr } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { EuiLoadingSpinner } from '@elastic/eui';
|
||||
import { EuiFlexItem } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { KpiHostsData } from '../../../../graphql/types';
|
||||
import { StatItem, StatItems, StatItemsComponent, StatItemsProps } from '../../../stat_items';
|
||||
import {
|
||||
StatItemsComponent,
|
||||
StatItemsProps,
|
||||
useKpiMatrixStatus,
|
||||
StatItems,
|
||||
} from '../../../stat_items';
|
||||
import * as i18n from './translations';
|
||||
import { BarChartData, AreaChartData } from '../../../charts/common';
|
||||
|
||||
interface KpiHostsProps {
|
||||
data: KpiHostsData;
|
||||
loading: boolean;
|
||||
}
|
||||
const kpiWidgetHeight = 247;
|
||||
|
||||
const euiColorVis0 = '#00B3A4';
|
||||
const euiColorVis1 = '#3185FC';
|
||||
|
@ -25,8 +26,14 @@ const euiColorVis2 = '#DB1374';
|
|||
const euiColorVis3 = '#490092';
|
||||
const euiColorVis9 = '#920000';
|
||||
|
||||
const fieldTitleMapping: StatItems[] = [
|
||||
interface KpiHostsProps {
|
||||
data: KpiHostsData;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const fieldTitleMapping: Readonly<StatItems[]> = [
|
||||
{
|
||||
key: 'hosts',
|
||||
fields: [
|
||||
{
|
||||
key: 'hosts',
|
||||
|
@ -40,6 +47,7 @@ const fieldTitleMapping: StatItems[] = [
|
|||
description: i18n.HOSTS,
|
||||
},
|
||||
{
|
||||
key: 'authentication',
|
||||
fields: [
|
||||
{
|
||||
key: 'authSuccess',
|
||||
|
@ -62,6 +70,7 @@ const fieldTitleMapping: StatItems[] = [
|
|||
description: i18n.AUTHENTICATION,
|
||||
},
|
||||
{
|
||||
key: 'uniqueIps',
|
||||
fields: [
|
||||
{
|
||||
key: 'uniqueSourceIps',
|
||||
|
@ -86,75 +95,25 @@ const fieldTitleMapping: StatItems[] = [
|
|||
},
|
||||
];
|
||||
|
||||
export const KpiHostsComponent = React.memo<KpiHostsProps>(({ data, loading }) => {
|
||||
const FlexGroupSpinner = styled(EuiFlexGroup)`
|
||||
{
|
||||
min-height: ${kpiWidgetHeight}px;
|
||||
}
|
||||
`;
|
||||
|
||||
export const KpiHostsComponent = ({ data, loading }: KpiHostsProps) => {
|
||||
const statItemsProps: StatItemsProps[] = useKpiMatrixStatus(fieldTitleMapping, data);
|
||||
return loading ? (
|
||||
<EuiFlexGroup justifyContent="center" alignItems="center" style={{ minHeight: 247 }}>
|
||||
<FlexGroupSpinner justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiLoadingSpinner size="xl" />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
</FlexGroupSpinner>
|
||||
) : (
|
||||
<EuiFlexGroup>
|
||||
{fieldTitleMapping.map(stat => {
|
||||
let statItemProps: StatItemsProps = {
|
||||
...stat,
|
||||
key: `kpi-hosts-summary-${stat.description}`,
|
||||
};
|
||||
|
||||
if (stat.fields != null)
|
||||
statItemProps = {
|
||||
...statItemProps,
|
||||
fields: addValueToFields(stat.fields, data),
|
||||
};
|
||||
|
||||
if (stat.enableAreaChart)
|
||||
statItemProps = {
|
||||
...statItemProps,
|
||||
areaChart: addValueToAreaChart(stat.fields, data),
|
||||
};
|
||||
|
||||
if (stat.enableBarChart != null)
|
||||
statItemProps = {
|
||||
...statItemProps,
|
||||
barChart: addValueToBarChart(stat.fields, data),
|
||||
};
|
||||
|
||||
return <StatItemsComponent {...statItemProps} />;
|
||||
{statItemsProps.map(mappedStatItemProps => {
|
||||
return <StatItemsComponent {...mappedStatItemProps} />;
|
||||
})}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
});
|
||||
|
||||
const addValueToFields = (fields: StatItem[], data: KpiHostsData): StatItem[] =>
|
||||
fields.map(field => ({ ...field, value: get(field.key, data) }));
|
||||
|
||||
const addValueToAreaChart = (fields: StatItem[], data: KpiHostsData): AreaChartData[] =>
|
||||
fields
|
||||
.filter(field => get(`${field.key}Histogram`, data) != null)
|
||||
.map(field => ({
|
||||
...field,
|
||||
value: get(`${field.key}Histogram`, data),
|
||||
key: `${field.key}Histogram`,
|
||||
}));
|
||||
|
||||
const addValueToBarChart = (fields: StatItem[], data: KpiHostsData): BarChartData[] => {
|
||||
if (fields.length === 0) return [];
|
||||
return fields.reduce((acc: BarChartData[], field: StatItem, idx: number) => {
|
||||
const key: string = get('key', field);
|
||||
const y: number | null = getOr(null, key, data);
|
||||
const x: string = get(`${idx}.name`, fields) || getOr('', `${idx}.description`, fields);
|
||||
|
||||
return acc.concat([
|
||||
{
|
||||
...field,
|
||||
value: [
|
||||
{
|
||||
x,
|
||||
y,
|
||||
g: key,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
}, []);
|
||||
};
|
||||
|
|
|
@ -1,16 +1,35 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`KpiNetwork Component rendering it renders loading icons 1`] = `
|
||||
<pure(Component)
|
||||
<Component
|
||||
data={
|
||||
Object {
|
||||
"activeAgents": 60015,
|
||||
"dnsQueries": 278,
|
||||
"networkEvents": 16,
|
||||
"tlsHandshakes": 10000,
|
||||
"uniqueDestinationPrivateIps": 18,
|
||||
"uniqueDestinationPrivateIpsHistogram": Array [
|
||||
Object {
|
||||
"x": "2019-02-09T16:00:00.000Z",
|
||||
"y": 8,
|
||||
},
|
||||
Object {
|
||||
"x": "2019-02-09T19:00:00.000Z",
|
||||
"y": 0,
|
||||
},
|
||||
],
|
||||
"uniqueFlowId": 10277307,
|
||||
"uniqueSourcePrivateIps": 383,
|
||||
"uniqueSourcePrivateIpsHistogram": Array [
|
||||
Object {
|
||||
"x": "2019-02-09T16:00:00.000Z",
|
||||
"y": 8,
|
||||
},
|
||||
Object {
|
||||
"x": "2019-02-09T19:00:00.000Z",
|
||||
"y": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
loading={true}
|
||||
|
@ -18,16 +37,35 @@ exports[`KpiNetwork Component rendering it renders loading icons 1`] = `
|
|||
`;
|
||||
|
||||
exports[`KpiNetwork Component rendering it renders the default widget 1`] = `
|
||||
<pure(Component)
|
||||
<Component
|
||||
data={
|
||||
Object {
|
||||
"activeAgents": 60015,
|
||||
"dnsQueries": 278,
|
||||
"networkEvents": 16,
|
||||
"tlsHandshakes": 10000,
|
||||
"uniqueDestinationPrivateIps": 18,
|
||||
"uniqueDestinationPrivateIpsHistogram": Array [
|
||||
Object {
|
||||
"x": "2019-02-09T16:00:00.000Z",
|
||||
"y": 8,
|
||||
},
|
||||
Object {
|
||||
"x": "2019-02-09T19:00:00.000Z",
|
||||
"y": 0,
|
||||
},
|
||||
],
|
||||
"uniqueFlowId": 10277307,
|
||||
"uniqueSourcePrivateIps": 383,
|
||||
"uniqueSourcePrivateIpsHistogram": Array [
|
||||
Object {
|
||||
"x": "2019-02-09T16:00:00.000Z",
|
||||
"y": 8,
|
||||
},
|
||||
Object {
|
||||
"x": "2019-02-09T19:00:00.000Z",
|
||||
"y": 0,
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
loading={false}
|
||||
|
|
|
@ -5,69 +5,77 @@
|
|||
*/
|
||||
|
||||
import { EuiFlexGroup } from '@elastic/eui';
|
||||
import { get } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import { EuiFlexItem, EuiLoadingSpinner } from '@elastic/eui';
|
||||
import styled from 'styled-components';
|
||||
import { StatItem, StatItems, StatItemsComponent } from '../../../../components/stat_items';
|
||||
import { EuiSpacer } from '@elastic/eui';
|
||||
import { chunk as _chunk } from 'lodash/fp';
|
||||
import {
|
||||
StatItemsComponent,
|
||||
StatItemsProps,
|
||||
useKpiMatrixStatus,
|
||||
StatItems,
|
||||
} from '../../../../components/stat_items';
|
||||
import { KpiNetworkData } from '../../../../graphql/types';
|
||||
|
||||
import * as i18n from './translations';
|
||||
|
||||
const kipsPerRow = 2;
|
||||
const kpiWidgetHeight = 228;
|
||||
|
||||
const euiColorVis1 = '#3185FC';
|
||||
const euiColorVis2 = '#DB1374';
|
||||
const euiColorVis3 = '#490092';
|
||||
|
||||
interface KpiNetworkProps {
|
||||
data: KpiNetworkData;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
const fieldTitleMapping: Readonly<StatItems[]> = [
|
||||
{
|
||||
fields: [
|
||||
{
|
||||
key: 'networkEvents',
|
||||
value: null,
|
||||
},
|
||||
],
|
||||
description: i18n.NETWORK_EVENTS,
|
||||
},
|
||||
{
|
||||
fields: [
|
||||
{
|
||||
key: 'uniqueFlowId',
|
||||
value: null,
|
||||
},
|
||||
],
|
||||
description: i18n.UNIQUE_ID,
|
||||
},
|
||||
{
|
||||
fields: [
|
||||
{
|
||||
key: 'activeAgents',
|
||||
value: null,
|
||||
},
|
||||
],
|
||||
description: i18n.ACTIVE_AGENTS,
|
||||
},
|
||||
export const fieldTitleChartMapping: Readonly<StatItems[]> = [
|
||||
{
|
||||
key: 'UniqueIps',
|
||||
fields: [
|
||||
{
|
||||
key: 'uniqueSourcePrivateIps',
|
||||
value: null,
|
||||
name: i18n.SRC,
|
||||
description: i18n.SOURCE,
|
||||
color: euiColorVis2,
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
],
|
||||
description: i18n.UNIQUE_SOURCE_PRIVATE_IPS,
|
||||
},
|
||||
{
|
||||
fields: [
|
||||
{
|
||||
key: 'uniqueDestinationPrivateIps',
|
||||
value: null,
|
||||
name: i18n.DIST,
|
||||
description: i18n.DESTINATION,
|
||||
color: euiColorVis3,
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
],
|
||||
description: i18n.UNIQUE_DESTINATION_PRIVATE_IPS,
|
||||
description: i18n.UNIQUE_PRIVATE_IPS,
|
||||
enableAreaChart: true,
|
||||
enableBarChart: true,
|
||||
grow: 2,
|
||||
},
|
||||
];
|
||||
|
||||
const fieldTitleMatrixMapping: Readonly<StatItems[]> = [
|
||||
{
|
||||
key: 'networkEvents',
|
||||
fields: [
|
||||
{
|
||||
key: 'networkEvents',
|
||||
value: null,
|
||||
color: euiColorVis1,
|
||||
},
|
||||
],
|
||||
description: i18n.NETWORK_EVENTS,
|
||||
grow: 1,
|
||||
},
|
||||
{
|
||||
key: 'dnsQueries',
|
||||
fields: [
|
||||
{
|
||||
key: 'dnsQueries',
|
||||
|
@ -77,6 +85,17 @@ const fieldTitleMapping: Readonly<StatItems[]> = [
|
|||
description: i18n.DNS_QUERIES,
|
||||
},
|
||||
{
|
||||
key: 'uniqueFlowId',
|
||||
fields: [
|
||||
{
|
||||
key: 'uniqueFlowId',
|
||||
value: null,
|
||||
},
|
||||
],
|
||||
description: i18n.UNIQUE_FLOW_IDS,
|
||||
},
|
||||
{
|
||||
key: 'tlsHandshakes',
|
||||
fields: [
|
||||
{
|
||||
key: 'tlsHandshakes',
|
||||
|
@ -88,10 +107,28 @@ const fieldTitleMapping: Readonly<StatItems[]> = [
|
|||
];
|
||||
|
||||
const FlexGroup = styled(EuiFlexGroup)`
|
||||
margin-height: 86px;
|
||||
min-height: ${kpiWidgetHeight}px;
|
||||
`;
|
||||
|
||||
export const KpiNetworkComponent = pure<KpiNetworkProps>(({ data, loading }) => {
|
||||
export const KpiNetworkBaseComponent = ({
|
||||
fieldsMapping,
|
||||
data,
|
||||
}: {
|
||||
fieldsMapping: Readonly<StatItems[]>;
|
||||
data: KpiNetworkData;
|
||||
}) => {
|
||||
const statItemsProps: StatItemsProps[] = useKpiMatrixStatus(fieldsMapping, data);
|
||||
|
||||
return (
|
||||
<EuiFlexGroup wrap>
|
||||
{statItemsProps.map(mappedStatItemProps => {
|
||||
return <StatItemsComponent {...mappedStatItemProps} />;
|
||||
})}
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
};
|
||||
|
||||
export const KpiNetworkComponent = React.memo<KpiNetworkProps>(({ data, loading }) => {
|
||||
return loading ? (
|
||||
<FlexGroup justifyContent="center" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
|
@ -99,17 +136,18 @@ export const KpiNetworkComponent = pure<KpiNetworkProps>(({ data, loading }) =>
|
|||
</EuiFlexItem>
|
||||
</FlexGroup>
|
||||
) : (
|
||||
<EuiFlexGroup>
|
||||
{fieldTitleMapping.map(stat => (
|
||||
<StatItemsComponent
|
||||
key={`kpi-network-summary-${stat.fields[0].key}`}
|
||||
description={stat.description}
|
||||
fields={addValueToFields(stat.fields, data)}
|
||||
/>
|
||||
))}
|
||||
<EuiFlexGroup wrap>
|
||||
<EuiFlexItem grow={1}>
|
||||
{_chunk(kipsPerRow, fieldTitleMatrixMapping).map((mappingsPerLine, idx) => (
|
||||
<React.Fragment key={`kpi-network-row-${idx}`}>
|
||||
{idx % kipsPerRow === 1 && <EuiSpacer size="l" />}
|
||||
<KpiNetworkBaseComponent data={data} fieldsMapping={mappingsPerLine} />
|
||||
</React.Fragment>
|
||||
))}
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={1}>
|
||||
<KpiNetworkBaseComponent data={data} fieldsMapping={fieldTitleChartMapping} />
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
);
|
||||
});
|
||||
|
||||
const addValueToFields = (fields: StatItem[], data: KpiNetworkData): StatItem[] =>
|
||||
fields.map(field => ({ ...field, value: get(field.key, data) }));
|
||||
|
|
|
@ -5,15 +5,207 @@
|
|||
*/
|
||||
|
||||
import { KpiNetworkData } from '../../../../graphql/types';
|
||||
import { StatItems } from '../../../stat_items';
|
||||
|
||||
export const mockData: { KpiNetwork: KpiNetworkData } = {
|
||||
KpiNetwork: {
|
||||
networkEvents: 16,
|
||||
uniqueFlowId: 10277307,
|
||||
activeAgents: 60015,
|
||||
uniqueSourcePrivateIps: 383,
|
||||
uniqueSourcePrivateIpsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: 8,
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
uniqueDestinationPrivateIps: 18,
|
||||
uniqueDestinationPrivateIpsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: 8,
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
dnsQueries: 278,
|
||||
tlsHandshakes: 10000,
|
||||
},
|
||||
};
|
||||
|
||||
const mockMappingItems: StatItems = {
|
||||
key: 'UniqueIps',
|
||||
fields: [
|
||||
{
|
||||
key: 'uniqueSourcePrivateIps',
|
||||
value: null,
|
||||
name: 'Src.',
|
||||
description: 'Source',
|
||||
color: '#DB1374',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
{
|
||||
key: 'uniqueDestinationPrivateIps',
|
||||
value: null,
|
||||
name: 'Dist.',
|
||||
description: 'Destination',
|
||||
color: '#490092',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
],
|
||||
description: 'Unique Private IPs',
|
||||
enableAreaChart: true,
|
||||
enableBarChart: true,
|
||||
grow: 2,
|
||||
};
|
||||
|
||||
export const mockNoChartMappings: Readonly<StatItems[]> = [
|
||||
{
|
||||
...mockMappingItems,
|
||||
enableAreaChart: false,
|
||||
enableBarChart: false,
|
||||
},
|
||||
];
|
||||
|
||||
export const mockDisableChartsInitialData = {
|
||||
fields: [
|
||||
{
|
||||
key: 'uniqueSourcePrivateIps',
|
||||
value: undefined,
|
||||
name: 'Src.',
|
||||
description: 'Source',
|
||||
color: '#DB1374',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
{
|
||||
key: 'uniqueDestinationPrivateIps',
|
||||
value: undefined,
|
||||
name: 'Dist.',
|
||||
description: 'Destination',
|
||||
color: '#490092',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
],
|
||||
description: 'Unique Private IPs',
|
||||
enableAreaChart: false,
|
||||
enableBarChart: false,
|
||||
grow: 2,
|
||||
areaChart: undefined,
|
||||
barChart: undefined,
|
||||
};
|
||||
|
||||
export const mockEnableChartsInitialData = {
|
||||
fields: [
|
||||
{
|
||||
key: 'uniqueSourcePrivateIps',
|
||||
value: undefined,
|
||||
name: 'Src.',
|
||||
description: 'Source',
|
||||
color: '#DB1374',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
{
|
||||
key: 'uniqueDestinationPrivateIps',
|
||||
value: undefined,
|
||||
name: 'Dist.',
|
||||
description: 'Destination',
|
||||
color: '#490092',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
],
|
||||
description: 'Unique Private IPs',
|
||||
enableAreaChart: true,
|
||||
enableBarChart: true,
|
||||
grow: 2,
|
||||
areaChart: [],
|
||||
barChart: [
|
||||
{
|
||||
color: '#DB1374',
|
||||
key: 'uniqueSourcePrivateIps',
|
||||
value: [
|
||||
{
|
||||
g: 'uniqueSourcePrivateIps',
|
||||
x: 'Src.',
|
||||
y: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
color: '#490092',
|
||||
key: 'uniqueDestinationPrivateIps',
|
||||
value: [
|
||||
{
|
||||
g: 'uniqueDestinationPrivateIps',
|
||||
x: 'Dist.',
|
||||
y: null,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const mockEnableChartsData = {
|
||||
fields: [
|
||||
{
|
||||
key: 'uniqueSourcePrivateIps',
|
||||
value: 383,
|
||||
name: 'Src.',
|
||||
description: 'Source',
|
||||
color: '#DB1374',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
{
|
||||
key: 'uniqueDestinationPrivateIps',
|
||||
value: 18,
|
||||
name: 'Dist.',
|
||||
description: 'Destination',
|
||||
color: '#490092',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
],
|
||||
description: 'Unique Private IPs',
|
||||
enableAreaChart: true,
|
||||
enableBarChart: true,
|
||||
grow: 2,
|
||||
areaChart: [
|
||||
{
|
||||
key: 'uniqueSourcePrivateIpsHistogram',
|
||||
value: [
|
||||
{ x: '2019-02-09T16:00:00.000Z', y: 8 },
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
name: 'Src.',
|
||||
description: 'Source',
|
||||
color: '#DB1374',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
{
|
||||
key: 'uniqueDestinationPrivateIpsHistogram',
|
||||
value: [{ x: '2019-02-09T16:00:00.000Z', y: 8 }, { x: '2019-02-09T19:00:00.000Z', y: 0 }],
|
||||
name: 'Dist.',
|
||||
description: 'Destination',
|
||||
color: '#490092',
|
||||
icon: 'visMapCoordinate',
|
||||
},
|
||||
],
|
||||
barChart: [
|
||||
{
|
||||
key: 'uniqueSourcePrivateIps',
|
||||
color: '#DB1374',
|
||||
value: [{ x: 'Src.', y: 383, g: 'uniqueSourcePrivateIps' }],
|
||||
},
|
||||
{
|
||||
key: 'uniqueDestinationPrivateIps',
|
||||
color: '#490092',
|
||||
value: [{ x: 'Dist.', y: 18, g: 'uniqueDestinationPrivateIps' }],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -10,27 +10,36 @@ export const NETWORK_EVENTS = i18n.translate('xpack.siem.kpiNetwork.source.netwo
|
|||
defaultMessage: 'Network Events',
|
||||
});
|
||||
|
||||
export const UNIQUE_ID = i18n.translate('xpack.siem.kpiNetwork.source.uniquiIdTitle', {
|
||||
defaultMessage: 'Unique Flow ID',
|
||||
export const UNIQUE_FLOW_IDS = i18n.translate('xpack.siem.kpiNetwork.source.uniqueFlowIdsTitle', {
|
||||
defaultMessage: 'Unique Flow IDs',
|
||||
});
|
||||
|
||||
export const ACTIVE_AGENTS = i18n.translate('xpack.siem.kpiNetwork.source.activeAgentsTitle', {
|
||||
defaultMessage: 'Active Agents',
|
||||
});
|
||||
|
||||
export const UNIQUE_SOURCE_PRIVATE_IPS = i18n.translate(
|
||||
'xpack.siem.kpiNetwork.source.uniqueSourcePrivateIpsTitle',
|
||||
export const UNIQUE_PRIVATE_IPS = i18n.translate(
|
||||
'xpack.siem.kpiNetwork.source.uniquePrivateIpsTitle',
|
||||
{
|
||||
defaultMessage: 'Unique Source IPs',
|
||||
defaultMessage: 'Unique Private IPs',
|
||||
}
|
||||
);
|
||||
|
||||
export const UNIQUE_DESTINATION_PRIVATE_IPS = i18n.translate(
|
||||
'xpack.siem.kpiNetwork.source.uniqueDestinationPrivateIpsTitle',
|
||||
{
|
||||
defaultMessage: 'Unique Destination IPs',
|
||||
}
|
||||
);
|
||||
export const SRC = i18n.translate('xpack.siem.kpiNetwork.source.srcTitle', {
|
||||
defaultMessage: 'Src.',
|
||||
});
|
||||
|
||||
export const SOURCE = i18n.translate('xpack.siem.kpiNetwork.source.sourceTitle', {
|
||||
defaultMessage: 'Source',
|
||||
});
|
||||
|
||||
export const DESTINATION = i18n.translate('xpack.siem.kpiNetwork.source.destinationTitle', {
|
||||
defaultMessage: 'Destination',
|
||||
});
|
||||
|
||||
export const DIST = i18n.translate('xpack.siem.kpiNetwork.source.distTitle', {
|
||||
defaultMessage: 'Dist.',
|
||||
});
|
||||
|
||||
export const LOADING = i18n.translate('xpack.siem.kpiNetwork.source.loadingDescription', {
|
||||
defaultMessage: 'Loading',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Stat Items disable charts it renders the default widget 1`] = `
|
||||
exports[`Stat Items Component disable charts it renders the default widget 1`] = `
|
||||
<Component
|
||||
description="HOSTS"
|
||||
fields={
|
||||
|
@ -102,7 +102,7 @@ exports[`Stat Items disable charts it renders the default widget 1`] = `
|
|||
</Component>
|
||||
`;
|
||||
|
||||
exports[`Stat Items disable charts it renders the default widget 2`] = `
|
||||
exports[`Stat Items Component disable charts it renders the default widget 2`] = `
|
||||
<Component
|
||||
areaChart={Array []}
|
||||
barChart={Array []}
|
||||
|
@ -207,7 +207,7 @@ exports[`Stat Items disable charts it renders the default widget 2`] = `
|
|||
</Component>
|
||||
`;
|
||||
|
||||
exports[`Stat Items rendering kpis with charts it renders the default widget 1`] = `
|
||||
exports[`Stat Items Component rendering kpis with charts it renders the default widget 1`] = `
|
||||
<Component
|
||||
areaChart={
|
||||
Array [
|
||||
|
@ -256,8 +256,8 @@ exports[`Stat Items rendering kpis with charts it renders the default widget 1`]
|
|||
"key": "uniqueSourceIps",
|
||||
"value": Array [
|
||||
Object {
|
||||
"x": 1714,
|
||||
"y": "uniqueSourceIps",
|
||||
"x": "uniqueSourceIps",
|
||||
"y": "1714",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -266,8 +266,8 @@ exports[`Stat Items rendering kpis with charts it renders the default widget 1`]
|
|||
"key": "uniqueDestinationIps",
|
||||
"value": Array [
|
||||
Object {
|
||||
"x": 2354,
|
||||
"y": "uniqueDestinationIps",
|
||||
"x": "uniqueDestinationIps",
|
||||
"y": 2354,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -531,8 +531,8 @@ exports[`Stat Items rendering kpis with charts it renders the default widget 1`]
|
|||
"key": "uniqueSourceIps",
|
||||
"value": Array [
|
||||
Object {
|
||||
"x": 1714,
|
||||
"y": "uniqueSourceIps",
|
||||
"x": "uniqueSourceIps",
|
||||
"y": "1714",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -541,8 +541,8 @@ exports[`Stat Items rendering kpis with charts it renders the default widget 1`]
|
|||
"key": "uniqueDestinationIps",
|
||||
"value": Array [
|
||||
Object {
|
||||
"x": 2354,
|
||||
"y": "uniqueDestinationIps",
|
||||
"x": "uniqueDestinationIps",
|
||||
"y": 2354,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -557,7 +557,7 @@ exports[`Stat Items rendering kpis with charts it renders the default widget 1`]
|
|||
innerRef={[Function]}
|
||||
>
|
||||
<div
|
||||
className="sc-bwzfXH gluXfG"
|
||||
className="sc-bwzfXH ffMqh"
|
||||
>
|
||||
<Component
|
||||
data={
|
||||
|
@ -567,8 +567,8 @@ exports[`Stat Items rendering kpis with charts it renders the default widget 1`]
|
|||
"key": "uniqueSourceIps",
|
||||
"value": Array [
|
||||
Object {
|
||||
"x": 1714,
|
||||
"y": "uniqueSourceIps",
|
||||
"x": "uniqueSourceIps",
|
||||
"y": "1714",
|
||||
},
|
||||
],
|
||||
},
|
||||
|
@ -577,67 +577,40 @@ exports[`Stat Items rendering kpis with charts it renders the default widget 1`]
|
|||
"key": "uniqueDestinationIps",
|
||||
"value": Array [
|
||||
Object {
|
||||
"x": 2354,
|
||||
"y": "uniqueDestinationIps",
|
||||
"x": "uniqueDestinationIps",
|
||||
"y": 2354,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
>
|
||||
<ChartHolder>
|
||||
<Styled(EuiFlexGroup)
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
>
|
||||
<EuiFlexGroup
|
||||
alignItems="center"
|
||||
className="sc-bdVaJa dGIfxd"
|
||||
justifyContent="center"
|
||||
>
|
||||
<div
|
||||
className="euiFlexGroup euiFlexGroup--gutterLarge euiFlexGroup--alignItemsCenter euiFlexGroup--justifyContentCenter euiFlexGroup--directionRow euiFlexGroup--responsive sc-bdVaJa dGIfxd"
|
||||
>
|
||||
<EuiFlexItem
|
||||
grow={false}
|
||||
>
|
||||
<div
|
||||
className="euiFlexItem euiFlexItem--flexGrowZero"
|
||||
>
|
||||
<EuiText
|
||||
color="subdued"
|
||||
size="s"
|
||||
textAlign="center"
|
||||
>
|
||||
<div
|
||||
className="euiText euiText--small"
|
||||
>
|
||||
<EuiTextAlign
|
||||
textAlign="center"
|
||||
>
|
||||
<div
|
||||
className="euiTextAlign euiTextAlign--center"
|
||||
>
|
||||
<EuiTextColor
|
||||
color="subdued"
|
||||
component="div"
|
||||
>
|
||||
<div
|
||||
className="euiTextColor euiTextColor--subdued"
|
||||
>
|
||||
Chart Data Not Available
|
||||
</div>
|
||||
</EuiTextColor>
|
||||
</div>
|
||||
</EuiTextAlign>
|
||||
</div>
|
||||
</EuiText>
|
||||
</div>
|
||||
</EuiFlexItem>
|
||||
</div>
|
||||
</EuiFlexGroup>
|
||||
</Styled(EuiFlexGroup)>
|
||||
</ChartHolder>
|
||||
<Component
|
||||
data={
|
||||
Array [
|
||||
Object {
|
||||
"color": "#DB1374",
|
||||
"key": "uniqueSourceIps",
|
||||
"value": Array [
|
||||
Object {
|
||||
"x": "uniqueSourceIps",
|
||||
"y": "1714",
|
||||
},
|
||||
],
|
||||
},
|
||||
Object {
|
||||
"color": "#490092",
|
||||
"key": "uniqueDestinationIps",
|
||||
"value": Array [
|
||||
Object {
|
||||
"x": "uniqueDestinationIps",
|
||||
"y": 2354,
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
}
|
||||
/>
|
||||
</Component>
|
||||
</div>
|
||||
</styled.div>
|
||||
|
@ -703,7 +676,7 @@ exports[`Stat Items rendering kpis with charts it renders the default widget 1`]
|
|||
innerRef={[Function]}
|
||||
>
|
||||
<div
|
||||
className="sc-bwzfXH gluXfG"
|
||||
className="sc-bwzfXH ffMqh"
|
||||
>
|
||||
<Component
|
||||
data={
|
||||
|
|
|
@ -8,12 +8,26 @@ import { mount, ReactWrapper } from 'enzyme';
|
|||
import toJson from 'enzyme-to-json';
|
||||
import * as React from 'react';
|
||||
|
||||
import { StatItemsComponent, StatItemsProps } from '.';
|
||||
import {
|
||||
StatItemsComponent,
|
||||
StatItemsProps,
|
||||
addValueToFields,
|
||||
addValueToAreaChart,
|
||||
addValueToBarChart,
|
||||
} from '.';
|
||||
import { BarChart } from '../charts/barchart';
|
||||
import { AreaChart } from '../charts/areachart';
|
||||
import { EuiHorizontalRule } from '@elastic/eui';
|
||||
import { fieldTitleChartMapping, KpiNetworkBaseComponent } from '../page/network/kpi_network';
|
||||
import {
|
||||
mockData,
|
||||
mockNoChartMappings,
|
||||
mockDisableChartsInitialData,
|
||||
mockEnableChartsData,
|
||||
mockEnableChartsInitialData,
|
||||
} from '../page/network/kpi_network/mock';
|
||||
|
||||
describe('Stat Items', () => {
|
||||
describe('Stat Items Component', () => {
|
||||
describe.each([
|
||||
[
|
||||
mount(
|
||||
|
@ -102,10 +116,10 @@ describe('Stat Items', () => {
|
|||
},
|
||||
],
|
||||
barChart: [
|
||||
{ key: 'uniqueSourceIps', value: [{ x: 1714, y: 'uniqueSourceIps' }], color: '#DB1374' },
|
||||
{ key: 'uniqueSourceIps', value: [{ x: 'uniqueSourceIps', y: '1714' }], color: '#DB1374' },
|
||||
{
|
||||
key: 'uniqueDestinationIps',
|
||||
value: [{ x: 2354, y: 'uniqueDestinationIps' }],
|
||||
value: [{ x: 'uniqueDestinationIps', y: 2354 }],
|
||||
color: '#490092',
|
||||
},
|
||||
],
|
||||
|
@ -141,3 +155,72 @@ describe('Stat Items', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('addValueToFields', () => {
|
||||
const mockNetworkMappings = fieldTitleChartMapping[0];
|
||||
const mockKpiNetworkData = mockData.KpiNetwork;
|
||||
test('should update value from data', () => {
|
||||
const result = addValueToFields(mockNetworkMappings.fields, mockKpiNetworkData);
|
||||
expect(result).toEqual(mockEnableChartsData.fields);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addValueToAreaChart', () => {
|
||||
const mockNetworkMappings = fieldTitleChartMapping[0];
|
||||
const mockKpiNetworkData = mockData.KpiNetwork;
|
||||
test('should add areaChart from data', () => {
|
||||
const result = addValueToAreaChart(mockNetworkMappings.fields, mockKpiNetworkData);
|
||||
expect(result).toEqual(mockEnableChartsData.areaChart);
|
||||
});
|
||||
});
|
||||
|
||||
describe('addValueToBarChart', () => {
|
||||
const mockNetworkMappings = fieldTitleChartMapping[0];
|
||||
const mockKpiNetworkData = mockData.KpiNetwork;
|
||||
test('should add areaChart from data', () => {
|
||||
const result = addValueToBarChart(mockNetworkMappings.fields, mockKpiNetworkData);
|
||||
expect(result).toEqual(mockEnableChartsData.barChart);
|
||||
});
|
||||
});
|
||||
|
||||
describe('useKpiMatrixStatus', () => {
|
||||
const mockNetworkMappings = fieldTitleChartMapping;
|
||||
const mockKpiNetworkData = mockData.KpiNetwork;
|
||||
|
||||
test('it updates status correctly', () => {
|
||||
const wrapper = mount(
|
||||
<KpiNetworkBaseComponent fieldsMapping={mockNetworkMappings} data={{}} />
|
||||
);
|
||||
expect(wrapper.find(StatItemsComponent).get(0).props).toEqual(mockEnableChartsInitialData);
|
||||
wrapper.setProps({ data: mockKpiNetworkData });
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find(StatItemsComponent).get(0).props).toEqual(mockEnableChartsData);
|
||||
});
|
||||
|
||||
test('it should not append areaChart if enableAreaChart is off', () => {
|
||||
const mockNetworkMappingsNoAreaChart = mockNoChartMappings;
|
||||
|
||||
const wrapper = mount(
|
||||
<KpiNetworkBaseComponent fieldsMapping={mockNetworkMappingsNoAreaChart} data={{}} />
|
||||
);
|
||||
expect(wrapper.find(StatItemsComponent).get(0).props).toEqual(mockDisableChartsInitialData);
|
||||
wrapper.setProps({ data: mockKpiNetworkData });
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find(StatItemsComponent).get(0).props.areaChart).toBeUndefined();
|
||||
});
|
||||
|
||||
test('it should not append barChart if enableBarChart is off', () => {
|
||||
const mockNetworkMappingsNoAreaChart = mockNoChartMappings;
|
||||
|
||||
const wrapper = mount(
|
||||
<KpiNetworkBaseComponent fieldsMapping={mockNetworkMappingsNoAreaChart} data={{}} />
|
||||
);
|
||||
expect(wrapper.find(StatItemsComponent).get(0).props).toEqual(mockDisableChartsInitialData);
|
||||
wrapper.setProps({ data: mockKpiNetworkData });
|
||||
wrapper.update();
|
||||
|
||||
expect(wrapper.find(StatItemsComponent).get(0).props.barChart).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,13 +13,15 @@ import {
|
|||
EuiTitle,
|
||||
IconType,
|
||||
} from '@elastic/eui';
|
||||
import React from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import styled from 'styled-components';
|
||||
|
||||
import { get, getOr } from 'lodash/fp';
|
||||
import { BarChart } from '../charts/barchart';
|
||||
import { AreaChart } from '../charts/areachart';
|
||||
import { getEmptyTagValue } from '../empty_value';
|
||||
import { AreaChartData, BarChartData } from '../charts/common';
|
||||
import { ChartConfigsData, ChartData } from '../charts/common';
|
||||
import { KpiHostsData, KpiNetworkData } from '../../graphql/types';
|
||||
|
||||
const FlexItem = styled(EuiFlexItem)`
|
||||
min-width: 0;
|
||||
|
@ -31,7 +33,7 @@ const StatValue = styled(EuiTitle)`
|
|||
white-space: nowrap;
|
||||
`;
|
||||
|
||||
export interface StatItem {
|
||||
interface StatItem {
|
||||
key: string;
|
||||
description?: string;
|
||||
value: number | undefined | null;
|
||||
|
@ -41,6 +43,7 @@ export interface StatItem {
|
|||
}
|
||||
|
||||
export interface StatItems {
|
||||
key: string;
|
||||
fields: StatItem[];
|
||||
description?: string;
|
||||
enableAreaChart?: boolean;
|
||||
|
@ -49,11 +52,81 @@ export interface StatItems {
|
|||
}
|
||||
|
||||
export interface StatItemsProps extends StatItems {
|
||||
key: string;
|
||||
areaChart?: AreaChartData[];
|
||||
barChart?: BarChartData[];
|
||||
areaChart?: ChartConfigsData[];
|
||||
barChart?: ChartConfigsData[];
|
||||
}
|
||||
|
||||
export const addValueToFields = (
|
||||
fields: StatItem[],
|
||||
data: KpiHostsData | KpiNetworkData
|
||||
): StatItem[] => fields.map(field => ({ ...field, value: get(field.key, data) }));
|
||||
|
||||
export const addValueToAreaChart = (
|
||||
fields: StatItem[],
|
||||
data: KpiHostsData | KpiNetworkData
|
||||
): ChartConfigsData[] =>
|
||||
fields
|
||||
.filter(field => get(`${field.key}Histogram`, data) != null)
|
||||
.map(field => ({
|
||||
...field,
|
||||
value: get(`${field.key}Histogram`, data),
|
||||
key: `${field.key}Histogram`,
|
||||
}));
|
||||
|
||||
export const addValueToBarChart = (
|
||||
fields: StatItem[],
|
||||
data: KpiHostsData | KpiNetworkData
|
||||
): ChartConfigsData[] => {
|
||||
if (fields.length === 0) return [];
|
||||
return fields.reduce((acc: ChartConfigsData[], field: StatItem, idx: number) => {
|
||||
const { key, color } = field;
|
||||
const y: number | null = getOr(null, key, data);
|
||||
const x: string = get(`${idx}.name`, fields) || getOr('', `${idx}.description`, fields);
|
||||
const value: [ChartData] = [
|
||||
{
|
||||
x,
|
||||
y,
|
||||
g: key,
|
||||
},
|
||||
];
|
||||
|
||||
return [
|
||||
...acc,
|
||||
{
|
||||
key,
|
||||
color,
|
||||
value,
|
||||
},
|
||||
];
|
||||
}, []);
|
||||
};
|
||||
|
||||
export const useKpiMatrixStatus = (
|
||||
mappings: Readonly<StatItems[]>,
|
||||
data: KpiHostsData | KpiNetworkData
|
||||
): StatItemsProps[] => {
|
||||
const [statItemsProps, setStatItemsProps] = useState(mappings as StatItemsProps[]);
|
||||
|
||||
useEffect(
|
||||
() => {
|
||||
setStatItemsProps(
|
||||
mappings.map(stat => {
|
||||
return {
|
||||
...stat,
|
||||
key: `kpi-summary-${stat.key}`,
|
||||
fields: addValueToFields(stat.fields, data),
|
||||
areaChart: stat.enableAreaChart ? addValueToAreaChart(stat.fields, data) : undefined,
|
||||
barChart: stat.enableBarChart ? addValueToBarChart(stat.fields, data) : undefined,
|
||||
};
|
||||
})
|
||||
);
|
||||
},
|
||||
[data]
|
||||
);
|
||||
|
||||
return statItemsProps;
|
||||
};
|
||||
|
||||
export const StatItemsComponent = React.memo<StatItemsProps>(
|
||||
({ fields, description, grow, barChart, areaChart, enableAreaChart, enableBarChart }) => {
|
||||
const isBarChartDataAbailable =
|
||||
|
|
|
@ -7,12 +7,9 @@
|
|||
import gql from 'graphql-tag';
|
||||
|
||||
export const kpiHostsQuery = gql`
|
||||
fragment ChartFields on KpiHostHistogramData {
|
||||
x: key_as_string
|
||||
y: count {
|
||||
value
|
||||
doc_count
|
||||
}
|
||||
fragment KpiHostChartFields on KpiHostHistogramData {
|
||||
x
|
||||
y
|
||||
}
|
||||
|
||||
query GetKpiHostsQuery(
|
||||
|
@ -26,23 +23,23 @@ export const kpiHostsQuery = gql`
|
|||
KpiHosts(timerange: $timerange, filterQuery: $filterQuery, defaultIndex: $defaultIndex) {
|
||||
hosts
|
||||
hostsHistogram {
|
||||
...ChartFields
|
||||
...KpiHostChartFields
|
||||
}
|
||||
authSuccess
|
||||
authSuccessHistogram {
|
||||
...ChartFields
|
||||
...KpiHostChartFields
|
||||
}
|
||||
authFailure
|
||||
authFailureHistogram {
|
||||
...ChartFields
|
||||
...KpiHostChartFields
|
||||
}
|
||||
uniqueSourceIps
|
||||
uniqueSourceIpsHistogram {
|
||||
...ChartFields
|
||||
...KpiHostChartFields
|
||||
}
|
||||
uniqueDestinationIps
|
||||
uniqueDestinationIpsHistogram {
|
||||
...ChartFields
|
||||
...KpiHostChartFields
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { getOr, get } from 'lodash/fp';
|
||||
import { getOr } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { Query } from 'react-apollo';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { DEFAULT_INDEX_KEY } from '../../../common/constants';
|
||||
|
@ -17,7 +16,6 @@ import { createFilter } from '../helpers';
|
|||
import { QueryTemplateProps } from '../query_template';
|
||||
|
||||
import { kpiHostsQuery } from './index.gql_query';
|
||||
import { ChartData } from '../../components/charts/common';
|
||||
|
||||
export interface KpiHostsArgs {
|
||||
id: string;
|
||||
|
@ -30,21 +28,7 @@ export interface KpiHostsProps extends QueryTemplateProps {
|
|||
children: (args: KpiHostsArgs) => React.ReactNode;
|
||||
}
|
||||
|
||||
const formatHistogramData = (
|
||||
data: Array<{
|
||||
x: number;
|
||||
y: { value: number; doc_count: number };
|
||||
}>
|
||||
): ChartData[] => {
|
||||
return data.length > 0
|
||||
? data.map(({ x, y }) => ({
|
||||
x,
|
||||
y: y.value || y.doc_count,
|
||||
}))
|
||||
: [];
|
||||
};
|
||||
|
||||
export const KpiHostsQuery = pure<KpiHostsProps>(
|
||||
export const KpiHostsQuery = React.memo<KpiHostsProps>(
|
||||
({ id = 'kpiHostsQuery', children, filterQuery, sourceId, startDate, endDate }) => (
|
||||
<Query<GetKpiHostsQuery.Query, GetKpiHostsQuery.Variables>
|
||||
query={kpiHostsQuery}
|
||||
|
@ -63,29 +47,9 @@ export const KpiHostsQuery = pure<KpiHostsProps>(
|
|||
>
|
||||
{({ data, loading, refetch }) => {
|
||||
const kpiHosts = getOr({}, `source.KpiHosts`, data);
|
||||
const hostsHistogram = get(`hostsHistogram`, kpiHosts);
|
||||
const authFailureHistogram = get(`authFailureHistogram`, kpiHosts);
|
||||
const authSuccessHistogram = get(`authSuccessHistogram`, kpiHosts);
|
||||
const uniqueSourceIpsHistogram = get(`uniqueSourceIpsHistogram`, kpiHosts);
|
||||
const uniqueDestinationIpsHistogram = get(`uniqueDestinationIpsHistogram`, kpiHosts);
|
||||
return children({
|
||||
id,
|
||||
kpiHosts: {
|
||||
...kpiHosts,
|
||||
hostsHistogram: hostsHistogram ? formatHistogramData(hostsHistogram) : [],
|
||||
authFailureHistogram: authFailureHistogram
|
||||
? formatHistogramData(authFailureHistogram)
|
||||
: [],
|
||||
authSuccessHistogram: authSuccessHistogram
|
||||
? formatHistogramData(authSuccessHistogram)
|
||||
: [],
|
||||
uniqueSourceIpsHistogram: uniqueSourceIpsHistogram
|
||||
? formatHistogramData(uniqueSourceIpsHistogram)
|
||||
: [],
|
||||
uniqueDestinationIpsHistogram: uniqueDestinationIpsHistogram
|
||||
? formatHistogramData(uniqueDestinationIpsHistogram)
|
||||
: [],
|
||||
},
|
||||
kpiHosts,
|
||||
loading,
|
||||
refetch,
|
||||
});
|
||||
|
|
|
@ -7,6 +7,11 @@
|
|||
import gql from 'graphql-tag';
|
||||
|
||||
export const kpiNetworkQuery = gql`
|
||||
fragment KpiNetworkChartFields on KpiNetworkHistogramData {
|
||||
x
|
||||
y
|
||||
}
|
||||
|
||||
query GetKpiNetworkQuery(
|
||||
$sourceId: ID!
|
||||
$timerange: TimerangeInput!
|
||||
|
@ -18,9 +23,14 @@ export const kpiNetworkQuery = gql`
|
|||
KpiNetwork(timerange: $timerange, filterQuery: $filterQuery, defaultIndex: $defaultIndex) {
|
||||
networkEvents
|
||||
uniqueFlowId
|
||||
activeAgents
|
||||
uniqueSourcePrivateIps
|
||||
uniqueSourcePrivateIpsHistogram {
|
||||
...KpiNetworkChartFields
|
||||
}
|
||||
uniqueDestinationPrivateIps
|
||||
uniqueDestinationPrivateIpsHistogram {
|
||||
...KpiNetworkChartFields
|
||||
}
|
||||
dnsQueries
|
||||
tlsHandshakes
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import { getOr } from 'lodash/fp';
|
||||
import React from 'react';
|
||||
import { Query } from 'react-apollo';
|
||||
import { pure } from 'recompose';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { DEFAULT_INDEX_KEY } from '../../../common/constants';
|
||||
|
@ -29,7 +28,7 @@ export interface KpiNetworkProps extends QueryTemplateProps {
|
|||
children: (args: KpiNetworkArgs) => React.ReactNode;
|
||||
}
|
||||
|
||||
export const KpiNetworkQuery = pure<KpiNetworkProps>(
|
||||
export const KpiNetworkQuery = React.memo<KpiNetworkProps>(
|
||||
({ id = 'kpiNetworkQuery', children, filterQuery, sourceId, startDate, endDate }) => (
|
||||
<Query<GetKpiNetworkQuery.Query, GetKpiNetworkQuery.Variables>
|
||||
query={kpiNetworkQuery}
|
||||
|
|
|
@ -6853,14 +6853,6 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "activeAgents",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "uniqueSourcePrivateIps",
|
||||
"description": "",
|
||||
|
@ -6869,6 +6861,22 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "uniqueSourcePrivateIpsHistogram",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "OBJECT", "name": "KpiNetworkHistogramData", "ofType": null }
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "uniqueDestinationPrivateIps",
|
||||
"description": "",
|
||||
|
@ -6877,6 +6885,22 @@
|
|||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "uniqueDestinationPrivateIpsHistogram",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": {
|
||||
"kind": "LIST",
|
||||
"name": null,
|
||||
"ofType": {
|
||||
"kind": "NON_NULL",
|
||||
"name": null,
|
||||
"ofType": { "kind": "OBJECT", "name": "KpiNetworkHistogramData", "ofType": null }
|
||||
}
|
||||
},
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "dnsQueries",
|
||||
"description": "",
|
||||
|
@ -6899,6 +6923,33 @@
|
|||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "KpiNetworkHistogramData",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "x",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "y",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "KpiHostsData",
|
||||
|
@ -7036,15 +7087,7 @@
|
|||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "key",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "key_as_string",
|
||||
"name": "x",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "String", "ofType": null },
|
||||
|
@ -7052,34 +7095,7 @@
|
|||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "count",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "OBJECT", "name": "Count", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
}
|
||||
],
|
||||
"inputFields": null,
|
||||
"interfaces": [],
|
||||
"enumValues": null,
|
||||
"possibleTypes": null
|
||||
},
|
||||
{
|
||||
"kind": "OBJECT",
|
||||
"name": "Count",
|
||||
"description": "",
|
||||
"fields": [
|
||||
{
|
||||
"name": "value",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
"isDeprecated": false,
|
||||
"deprecationReason": null
|
||||
},
|
||||
{
|
||||
"name": "doc_count",
|
||||
"name": "y",
|
||||
"description": "",
|
||||
"args": [],
|
||||
"type": { "kind": "SCALAR", "name": "Float", "ofType": null },
|
||||
|
|
|
@ -1054,17 +1054,25 @@ export interface KpiNetworkData {
|
|||
|
||||
uniqueFlowId?: number | null;
|
||||
|
||||
activeAgents?: number | null;
|
||||
|
||||
uniqueSourcePrivateIps?: number | null;
|
||||
|
||||
uniqueSourcePrivateIpsHistogram?: KpiNetworkHistogramData[] | null;
|
||||
|
||||
uniqueDestinationPrivateIps?: number | null;
|
||||
|
||||
uniqueDestinationPrivateIpsHistogram?: KpiNetworkHistogramData[] | null;
|
||||
|
||||
dnsQueries?: number | null;
|
||||
|
||||
tlsHandshakes?: number | null;
|
||||
}
|
||||
|
||||
export interface KpiNetworkHistogramData {
|
||||
x?: string | null;
|
||||
|
||||
y?: number | null;
|
||||
}
|
||||
|
||||
export interface KpiHostsData {
|
||||
hosts?: number | null;
|
||||
|
||||
|
@ -1088,17 +1096,9 @@ export interface KpiHostsData {
|
|||
}
|
||||
|
||||
export interface KpiHostHistogramData {
|
||||
key?: number | null;
|
||||
x?: string | null;
|
||||
|
||||
key_as_string?: string | null;
|
||||
|
||||
count?: Count | null;
|
||||
}
|
||||
|
||||
export interface Count {
|
||||
value?: number | null;
|
||||
|
||||
doc_count?: number | null;
|
||||
y?: number | null;
|
||||
}
|
||||
|
||||
export interface NetworkTopNFlowData {
|
||||
|
@ -2956,15 +2956,15 @@ export namespace GetKpiHostsQuery {
|
|||
uniqueDestinationIpsHistogram?: UniqueDestinationIpsHistogram[] | null;
|
||||
};
|
||||
|
||||
export type HostsHistogram = ChartFields.Fragment;
|
||||
export type HostsHistogram = KpiHostChartFields.Fragment;
|
||||
|
||||
export type AuthSuccessHistogram = ChartFields.Fragment;
|
||||
export type AuthSuccessHistogram = KpiHostChartFields.Fragment;
|
||||
|
||||
export type AuthFailureHistogram = ChartFields.Fragment;
|
||||
export type AuthFailureHistogram = KpiHostChartFields.Fragment;
|
||||
|
||||
export type UniqueSourceIpsHistogram = ChartFields.Fragment;
|
||||
export type UniqueSourceIpsHistogram = KpiHostChartFields.Fragment;
|
||||
|
||||
export type UniqueDestinationIpsHistogram = ChartFields.Fragment;
|
||||
export type UniqueDestinationIpsHistogram = KpiHostChartFields.Fragment;
|
||||
}
|
||||
|
||||
export namespace GetKpiNetworkQuery {
|
||||
|
@ -2996,16 +2996,22 @@ export namespace GetKpiNetworkQuery {
|
|||
|
||||
uniqueFlowId?: number | null;
|
||||
|
||||
activeAgents?: number | null;
|
||||
|
||||
uniqueSourcePrivateIps?: number | null;
|
||||
|
||||
uniqueSourcePrivateIpsHistogram?: UniqueSourcePrivateIpsHistogram[] | null;
|
||||
|
||||
uniqueDestinationPrivateIps?: number | null;
|
||||
|
||||
uniqueDestinationPrivateIpsHistogram?: UniqueDestinationPrivateIpsHistogram[] | null;
|
||||
|
||||
dnsQueries?: number | null;
|
||||
|
||||
tlsHandshakes?: number | null;
|
||||
};
|
||||
|
||||
export type UniqueSourcePrivateIpsHistogram = KpiNetworkChartFields.Fragment;
|
||||
|
||||
export type UniqueDestinationPrivateIpsHistogram = KpiNetworkChartFields.Fragment;
|
||||
}
|
||||
|
||||
export namespace GetNetworkDnsQuery {
|
||||
|
@ -5031,20 +5037,22 @@ export namespace GetUsersQuery {
|
|||
};
|
||||
}
|
||||
|
||||
export namespace ChartFields {
|
||||
export namespace KpiHostChartFields {
|
||||
export type Fragment = {
|
||||
__typename?: 'KpiHostHistogramData';
|
||||
|
||||
x?: string | null;
|
||||
|
||||
y?: Y | null;
|
||||
};
|
||||
|
||||
export type Y = {
|
||||
__typename?: 'Count';
|
||||
|
||||
value?: number | null;
|
||||
|
||||
doc_count?: number | null;
|
||||
y?: number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export namespace KpiNetworkChartFields {
|
||||
export type Fragment = {
|
||||
__typename?: 'KpiNetworkHistogramData';
|
||||
|
||||
x?: string | null;
|
||||
|
||||
y?: number | null;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,15 +7,9 @@
|
|||
import gql from 'graphql-tag';
|
||||
|
||||
export const kpiHostsSchema = gql`
|
||||
type Count {
|
||||
value: Float
|
||||
doc_count: Float
|
||||
}
|
||||
|
||||
type KpiHostHistogramData {
|
||||
key: Float
|
||||
key_as_string: String
|
||||
count: Count
|
||||
x: String
|
||||
y: Float
|
||||
}
|
||||
|
||||
type KpiHostsData {
|
||||
|
|
|
@ -7,12 +7,18 @@
|
|||
import gql from 'graphql-tag';
|
||||
|
||||
export const kpiNetworkSchema = gql`
|
||||
type KpiNetworkHistogramData {
|
||||
x: String
|
||||
y: Float
|
||||
}
|
||||
|
||||
type KpiNetworkData {
|
||||
networkEvents: Float
|
||||
uniqueFlowId: Float
|
||||
activeAgents: Float
|
||||
uniqueSourcePrivateIps: Float
|
||||
uniqueSourcePrivateIpsHistogram: [KpiNetworkHistogramData!]
|
||||
uniqueDestinationPrivateIps: Float
|
||||
uniqueDestinationPrivateIpsHistogram: [KpiNetworkHistogramData!]
|
||||
dnsQueries: Float
|
||||
tlsHandshakes: Float
|
||||
}
|
||||
|
|
|
@ -1083,17 +1083,25 @@ export interface KpiNetworkData {
|
|||
|
||||
uniqueFlowId?: number | null;
|
||||
|
||||
activeAgents?: number | null;
|
||||
|
||||
uniqueSourcePrivateIps?: number | null;
|
||||
|
||||
uniqueSourcePrivateIpsHistogram?: KpiNetworkHistogramData[] | null;
|
||||
|
||||
uniqueDestinationPrivateIps?: number | null;
|
||||
|
||||
uniqueDestinationPrivateIpsHistogram?: KpiNetworkHistogramData[] | null;
|
||||
|
||||
dnsQueries?: number | null;
|
||||
|
||||
tlsHandshakes?: number | null;
|
||||
}
|
||||
|
||||
export interface KpiNetworkHistogramData {
|
||||
x?: string | null;
|
||||
|
||||
y?: number | null;
|
||||
}
|
||||
|
||||
export interface KpiHostsData {
|
||||
hosts?: number | null;
|
||||
|
||||
|
@ -1117,17 +1125,9 @@ export interface KpiHostsData {
|
|||
}
|
||||
|
||||
export interface KpiHostHistogramData {
|
||||
key?: number | null;
|
||||
x?: string | null;
|
||||
|
||||
key_as_string?: string | null;
|
||||
|
||||
count?: Count | null;
|
||||
}
|
||||
|
||||
export interface Count {
|
||||
value?: number | null;
|
||||
|
||||
doc_count?: number | null;
|
||||
y?: number | null;
|
||||
}
|
||||
|
||||
export interface NetworkTopNFlowData {
|
||||
|
@ -5805,16 +5805,26 @@ export namespace KpiNetworkDataResolvers {
|
|||
|
||||
uniqueFlowId?: UniqueFlowIdResolver<number | null, TypeParent, Context>;
|
||||
|
||||
activeAgents?: ActiveAgentsResolver<number | null, TypeParent, Context>;
|
||||
|
||||
uniqueSourcePrivateIps?: UniqueSourcePrivateIpsResolver<number | null, TypeParent, Context>;
|
||||
|
||||
uniqueSourcePrivateIpsHistogram?: UniqueSourcePrivateIpsHistogramResolver<
|
||||
KpiNetworkHistogramData[] | null,
|
||||
TypeParent,
|
||||
Context
|
||||
>;
|
||||
|
||||
uniqueDestinationPrivateIps?: UniqueDestinationPrivateIpsResolver<
|
||||
number | null,
|
||||
TypeParent,
|
||||
Context
|
||||
>;
|
||||
|
||||
uniqueDestinationPrivateIpsHistogram?: UniqueDestinationPrivateIpsHistogramResolver<
|
||||
KpiNetworkHistogramData[] | null,
|
||||
TypeParent,
|
||||
Context
|
||||
>;
|
||||
|
||||
dnsQueries?: DnsQueriesResolver<number | null, TypeParent, Context>;
|
||||
|
||||
tlsHandshakes?: TlsHandshakesResolver<number | null, TypeParent, Context>;
|
||||
|
@ -5830,21 +5840,26 @@ export namespace KpiNetworkDataResolvers {
|
|||
Parent = KpiNetworkData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type ActiveAgentsResolver<
|
||||
R = number | null,
|
||||
Parent = KpiNetworkData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type UniqueSourcePrivateIpsResolver<
|
||||
R = number | null,
|
||||
Parent = KpiNetworkData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type UniqueSourcePrivateIpsHistogramResolver<
|
||||
R = KpiNetworkHistogramData[] | null,
|
||||
Parent = KpiNetworkData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type UniqueDestinationPrivateIpsResolver<
|
||||
R = number | null,
|
||||
Parent = KpiNetworkData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type UniqueDestinationPrivateIpsHistogramResolver<
|
||||
R = KpiNetworkHistogramData[] | null,
|
||||
Parent = KpiNetworkData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type DnsQueriesResolver<
|
||||
R = number | null,
|
||||
Parent = KpiNetworkData,
|
||||
|
@ -5857,6 +5872,25 @@ export namespace KpiNetworkDataResolvers {
|
|||
> = Resolver<R, Parent, Context>;
|
||||
}
|
||||
|
||||
export namespace KpiNetworkHistogramDataResolvers {
|
||||
export interface Resolvers<Context = SiemContext, TypeParent = KpiNetworkHistogramData> {
|
||||
x?: XResolver<string | null, TypeParent, Context>;
|
||||
|
||||
y?: YResolver<number | null, TypeParent, Context>;
|
||||
}
|
||||
|
||||
export type XResolver<
|
||||
R = string | null,
|
||||
Parent = KpiNetworkHistogramData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type YResolver<
|
||||
R = number | null,
|
||||
Parent = KpiNetworkHistogramData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
}
|
||||
|
||||
export namespace KpiHostsDataResolvers {
|
||||
export interface Resolvers<Context = SiemContext, TypeParent = KpiHostsData> {
|
||||
hosts?: HostsResolver<number | null, TypeParent, Context>;
|
||||
|
@ -5950,49 +5984,23 @@ export namespace KpiHostsDataResolvers {
|
|||
|
||||
export namespace KpiHostHistogramDataResolvers {
|
||||
export interface Resolvers<Context = SiemContext, TypeParent = KpiHostHistogramData> {
|
||||
key?: KeyResolver<number | null, TypeParent, Context>;
|
||||
x?: XResolver<string | null, TypeParent, Context>;
|
||||
|
||||
key_as_string?: KeyAsStringResolver<string | null, TypeParent, Context>;
|
||||
|
||||
count?: CountResolver<Count | null, TypeParent, Context>;
|
||||
y?: YResolver<number | null, TypeParent, Context>;
|
||||
}
|
||||
|
||||
export type KeyResolver<
|
||||
R = number | null,
|
||||
Parent = KpiHostHistogramData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type KeyAsStringResolver<
|
||||
export type XResolver<
|
||||
R = string | null,
|
||||
Parent = KpiHostHistogramData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
export type CountResolver<
|
||||
R = Count | null,
|
||||
export type YResolver<
|
||||
R = number | null,
|
||||
Parent = KpiHostHistogramData,
|
||||
Context = SiemContext
|
||||
> = Resolver<R, Parent, Context>;
|
||||
}
|
||||
|
||||
export namespace CountResolvers {
|
||||
export interface Resolvers<Context = SiemContext, TypeParent = Count> {
|
||||
value?: ValueResolver<number | null, TypeParent, Context>;
|
||||
|
||||
doc_count?: DocCountResolver<number | null, TypeParent, Context>;
|
||||
}
|
||||
|
||||
export type ValueResolver<R = number | null, Parent = Count, Context = SiemContext> = Resolver<
|
||||
R,
|
||||
Parent,
|
||||
Context
|
||||
>;
|
||||
export type DocCountResolver<R = number | null, Parent = Count, Context = SiemContext> = Resolver<
|
||||
R,
|
||||
Parent,
|
||||
Context
|
||||
>;
|
||||
}
|
||||
|
||||
export namespace NetworkTopNFlowDataResolvers {
|
||||
export interface Resolvers<Context = SiemContext, TypeParent = NetworkTopNFlowData> {
|
||||
edges?: EdgesResolver<NetworkTopNFlowEdges[], TypeParent, Context>;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { KpiHostsData } from '../../graphql/types';
|
||||
import { FrameworkAdapter, FrameworkRequest } from '../framework';
|
||||
|
||||
import { ElasticsearchKpiHostsAdapter } from './elasticsearch_adapter';
|
||||
|
@ -19,6 +18,7 @@ import {
|
|||
} from './mock';
|
||||
import * as authQueryDsl from './query_authentication.dsl';
|
||||
import * as generalQueryDsl from './query_general.dsl';
|
||||
import { KpiHostsMappedData } from './types';
|
||||
|
||||
describe('Hosts Kpi elasticsearch_adapter', () => {
|
||||
const mockCallWithRequest = jest.fn();
|
||||
|
@ -33,7 +33,7 @@ describe('Hosts Kpi elasticsearch_adapter', () => {
|
|||
let mockBuildQuery: jest.SpyInstance;
|
||||
let mockBuildAuthQuery: jest.SpyInstance;
|
||||
let EsKpiHosts: ElasticsearchKpiHostsAdapter;
|
||||
let data: KpiHostsData;
|
||||
let data: KpiHostsMappedData;
|
||||
|
||||
describe('getKpiHosts - call stack', () => {
|
||||
beforeAll(async () => {
|
||||
|
|
|
@ -6,7 +6,6 @@
|
|||
|
||||
import { getOr } from 'lodash/fp';
|
||||
|
||||
import { KpiHostsData } from '../../graphql/types';
|
||||
import { FrameworkAdapter, FrameworkRequest, RequestBasicOptions } from '../framework';
|
||||
import { TermAggregation } from '../types';
|
||||
|
||||
|
@ -17,7 +16,34 @@ import {
|
|||
KpiHostsESMSearchBody,
|
||||
KpiHostsGeneralHit,
|
||||
KpiHostsAuthHit,
|
||||
KpiHostsMappedData,
|
||||
KpiHostHistogram,
|
||||
KpiHostGeneralHistogramCount,
|
||||
KpiHostAuthHistogramCount,
|
||||
} from './types';
|
||||
import { KpiHostHistogramData } from '../../graphql/types';
|
||||
|
||||
const formatGeneralHistogramData = (
|
||||
data: Array<KpiHostHistogram<KpiHostGeneralHistogramCount>>
|
||||
): KpiHostHistogramData[] | null => {
|
||||
return data && data.length > 0
|
||||
? data.map<KpiHostHistogramData>(({ key_as_string, count }) => ({
|
||||
x: key_as_string,
|
||||
y: count.value,
|
||||
}))
|
||||
: null;
|
||||
};
|
||||
|
||||
const formatAuthHistogramData = (
|
||||
data: Array<KpiHostHistogram<KpiHostAuthHistogramCount>>
|
||||
): KpiHostHistogramData[] | null => {
|
||||
return data && data.length > 0
|
||||
? data.map<KpiHostHistogramData>(({ key_as_string, count }) => ({
|
||||
x: key_as_string,
|
||||
y: count.doc_count,
|
||||
}))
|
||||
: null;
|
||||
};
|
||||
|
||||
export class ElasticsearchKpiHostsAdapter implements KpiHostsAdapter {
|
||||
constructor(private readonly framework: FrameworkAdapter) {}
|
||||
|
@ -25,7 +51,7 @@ export class ElasticsearchKpiHostsAdapter implements KpiHostsAdapter {
|
|||
public async getKpiHosts(
|
||||
request: FrameworkRequest,
|
||||
options: RequestBasicOptions
|
||||
): Promise<KpiHostsData> {
|
||||
): Promise<KpiHostsMappedData> {
|
||||
const generalQuery: KpiHostsESMSearchBody[] = buildGeneralQuery(options);
|
||||
const authQuery: KpiHostsESMSearchBody[] = buildAuthQuery(options);
|
||||
const response = await this.framework.callWithRequest<
|
||||
|
@ -34,45 +60,55 @@ export class ElasticsearchKpiHostsAdapter implements KpiHostsAdapter {
|
|||
>(request, 'msearch', {
|
||||
body: [...generalQuery, ...authQuery],
|
||||
});
|
||||
|
||||
const hostsHistogram = getOr(
|
||||
null,
|
||||
'responses.0.aggregations.hosts_histogram.buckets',
|
||||
response
|
||||
);
|
||||
const authSuccessHistogram = getOr(
|
||||
null,
|
||||
'responses.1.aggregations.authentication_success_histogram.buckets',
|
||||
response
|
||||
);
|
||||
const authFailureHistogram = getOr(
|
||||
null,
|
||||
'responses.1.aggregations.authentication_failure_histogram.buckets',
|
||||
response
|
||||
);
|
||||
const uniqueSourceIpsHistogram = getOr(
|
||||
null,
|
||||
'responses.0.aggregations.unique_source_ips_histogram.buckets',
|
||||
response
|
||||
);
|
||||
const uniqueDestinationIpsHistogram = getOr(
|
||||
null,
|
||||
'responses.0.aggregations.unique_destination_ips_histogram.buckets',
|
||||
response
|
||||
);
|
||||
return {
|
||||
hosts: getOr(null, 'responses.0.aggregations.hosts.value', response),
|
||||
hostsHistogram: getOr(null, 'responses.0.aggregations.hosts_histogram.buckets', response),
|
||||
hostsHistogram: formatGeneralHistogramData(hostsHistogram),
|
||||
authSuccess: getOr(
|
||||
null,
|
||||
'responses.1.aggregations.authentication_success.doc_count',
|
||||
response
|
||||
),
|
||||
authSuccessHistogram: getOr(
|
||||
null,
|
||||
'responses.1.aggregations.authentication_success_histogram.buckets',
|
||||
response
|
||||
),
|
||||
authSuccessHistogram: formatAuthHistogramData(authSuccessHistogram),
|
||||
authFailure: getOr(
|
||||
null,
|
||||
'responses.1.aggregations.authentication_failure.doc_count',
|
||||
response
|
||||
),
|
||||
authFailureHistogram: getOr(
|
||||
null,
|
||||
'responses.1.aggregations.authentication_failure_histogram.buckets',
|
||||
response
|
||||
),
|
||||
authFailureHistogram: formatAuthHistogramData(authFailureHistogram),
|
||||
uniqueSourceIps: getOr(null, 'responses.0.aggregations.unique_source_ips.value', response),
|
||||
uniqueSourceIpsHistogram: getOr(
|
||||
null,
|
||||
'responses.0.aggregations.unique_source_ips_histogram.buckets',
|
||||
response
|
||||
),
|
||||
uniqueSourceIpsHistogram: formatGeneralHistogramData(uniqueSourceIpsHistogram),
|
||||
uniqueDestinationIps: getOr(
|
||||
null,
|
||||
'responses.0.aggregations.unique_destination_ips.value',
|
||||
response
|
||||
),
|
||||
uniqueDestinationIpsHistogram: getOr(
|
||||
null,
|
||||
'responses.0.aggregations.unique_destination_ips_histogram.buckets',
|
||||
response
|
||||
),
|
||||
uniqueDestinationIpsHistogram: formatGeneralHistogramData(uniqueDestinationIpsHistogram),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,10 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { KpiHostsData } from '../../graphql/types';
|
||||
import { FrameworkRequest, RequestBasicOptions } from '../framework';
|
||||
|
||||
import { KpiHostsAdapter } from './types';
|
||||
import { KpiHostsAdapter, KpiHostsMappedData } from './types';
|
||||
|
||||
export class KpiHosts {
|
||||
constructor(private readonly adapter: KpiHostsAdapter) {}
|
||||
|
@ -15,7 +14,7 @@ export class KpiHosts {
|
|||
public async getKpiHosts(
|
||||
req: FrameworkRequest,
|
||||
options: RequestBasicOptions
|
||||
): Promise<KpiHostsData> {
|
||||
): Promise<KpiHostsMappedData> {
|
||||
return await this.adapter.getKpiHosts(req, options);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -241,136 +241,76 @@ export const mockResult = {
|
|||
hosts: 986,
|
||||
hostsHistogram: [
|
||||
{
|
||||
key_as_string: '2019-05-03T13:00:00.000Z',
|
||||
key: 1556888400000,
|
||||
doc_count: 3158515,
|
||||
count: {
|
||||
value: 919,
|
||||
},
|
||||
x: '2019-05-03T13:00:00.000Z',
|
||||
y: 919,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T01:00:00.000Z',
|
||||
key: 1556931600000,
|
||||
doc_count: 703032,
|
||||
count: {
|
||||
value: 82,
|
||||
},
|
||||
x: '2019-05-04T01:00:00.000Z',
|
||||
y: 82,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T13:00:00.000Z',
|
||||
key: 1556974800000,
|
||||
doc_count: 1780,
|
||||
count: {
|
||||
value: 4,
|
||||
},
|
||||
x: '2019-05-04T13:00:00.000Z',
|
||||
y: 4,
|
||||
},
|
||||
],
|
||||
authSuccess: 61,
|
||||
authSuccessHistogram: [
|
||||
{
|
||||
key_as_string: '2019-05-03T13:00:00.000Z',
|
||||
key: 1556888400000,
|
||||
doc_count: 11739,
|
||||
count: {
|
||||
doc_count: 8,
|
||||
},
|
||||
x: '2019-05-03T13:00:00.000Z',
|
||||
y: 8,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T01:00:00.000Z',
|
||||
key: 1556931600000,
|
||||
doc_count: 4031,
|
||||
count: {
|
||||
doc_count: 52,
|
||||
},
|
||||
x: '2019-05-04T01:00:00.000Z',
|
||||
y: 52,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T13:00:00.000Z',
|
||||
key: 1556974800000,
|
||||
doc_count: 13,
|
||||
count: {
|
||||
doc_count: 1,
|
||||
},
|
||||
x: '2019-05-04T13:00:00.000Z',
|
||||
y: 1,
|
||||
},
|
||||
],
|
||||
authFailure: 15722,
|
||||
authFailureHistogram: [
|
||||
{
|
||||
key_as_string: '2019-05-03T13:00:00.000Z',
|
||||
key: 1556888400000,
|
||||
doc_count: 11739,
|
||||
count: {
|
||||
doc_count: 11731,
|
||||
},
|
||||
x: '2019-05-03T13:00:00.000Z',
|
||||
y: 11731,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T01:00:00.000Z',
|
||||
key: 1556931600000,
|
||||
doc_count: 4031,
|
||||
count: {
|
||||
doc_count: 3979,
|
||||
},
|
||||
x: '2019-05-04T01:00:00.000Z',
|
||||
y: 3979,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T13:00:00.000Z',
|
||||
key: 1556974800000,
|
||||
doc_count: 13,
|
||||
count: {
|
||||
doc_count: 12,
|
||||
},
|
||||
x: '2019-05-04T13:00:00.000Z',
|
||||
y: 12,
|
||||
},
|
||||
],
|
||||
uniqueSourceIps: 1407,
|
||||
uniqueSourceIpsHistogram: [
|
||||
{
|
||||
key_as_string: '2019-05-03T13:00:00.000Z',
|
||||
key: 1556888400000,
|
||||
doc_count: 3158515,
|
||||
count: {
|
||||
value: 1182,
|
||||
},
|
||||
x: '2019-05-03T13:00:00.000Z',
|
||||
y: 1182,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T01:00:00.000Z',
|
||||
key: 1556931600000,
|
||||
doc_count: 703032,
|
||||
count: {
|
||||
value: 364,
|
||||
},
|
||||
x: '2019-05-04T01:00:00.000Z',
|
||||
y: 364,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T13:00:00.000Z',
|
||||
key: 1556974800000,
|
||||
doc_count: 1780,
|
||||
count: {
|
||||
value: 63,
|
||||
},
|
||||
x: '2019-05-04T13:00:00.000Z',
|
||||
y: 63,
|
||||
},
|
||||
],
|
||||
uniqueDestinationIps: 1954,
|
||||
uniqueDestinationIpsHistogram: [
|
||||
{
|
||||
key_as_string: '2019-05-03T13:00:00.000Z',
|
||||
key: 1556888400000,
|
||||
doc_count: 3158515,
|
||||
count: {
|
||||
value: 1809,
|
||||
},
|
||||
x: '2019-05-03T13:00:00.000Z',
|
||||
y: 1809,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T01:00:00.000Z',
|
||||
key: 1556931600000,
|
||||
doc_count: 703032,
|
||||
count: {
|
||||
value: 407,
|
||||
},
|
||||
x: '2019-05-04T01:00:00.000Z',
|
||||
y: 407,
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-04T13:00:00.000Z',
|
||||
key: 1556974800000,
|
||||
doc_count: 1780,
|
||||
count: {
|
||||
value: 64,
|
||||
},
|
||||
x: '2019-05-04T13:00:00.000Z',
|
||||
y: 64,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -3,12 +3,40 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { KpiHostsData } from '../../graphql/types';
|
||||
import { FrameworkRequest, RequestBasicOptions } from '../framework';
|
||||
import { MSearchHeader, SearchHit } from '../types';
|
||||
import { KpiHostHistogramData } from '../../graphql/types';
|
||||
|
||||
export interface KpiHostsMappedData {
|
||||
hosts?: number | null;
|
||||
hostsHistogram?: KpiHostHistogramData[] | null;
|
||||
authSuccess?: number | null;
|
||||
authSuccessHistogram?: KpiHostHistogramData[] | null;
|
||||
authFailure?: number | null;
|
||||
authFailureHistogram?: KpiHostHistogramData[] | null;
|
||||
uniqueSourceIps?: number | null;
|
||||
uniqueSourceIpsHistogram?: KpiHostHistogramData[] | null;
|
||||
uniqueDestinationIps?: number | null;
|
||||
uniqueDestinationIpsHistogram?: KpiHostHistogramData[] | null;
|
||||
}
|
||||
|
||||
export interface KpiHostsAdapter {
|
||||
getKpiHosts(request: FrameworkRequest, options: RequestBasicOptions): Promise<KpiHostsData>;
|
||||
getKpiHosts(request: FrameworkRequest, options: RequestBasicOptions): Promise<KpiHostsMappedData>;
|
||||
}
|
||||
|
||||
export interface KpiHostHistogram<T> {
|
||||
key_as_string: string;
|
||||
key: number;
|
||||
doc_count: number;
|
||||
count: T;
|
||||
}
|
||||
|
||||
export interface KpiHostGeneralHistogramCount {
|
||||
value: number;
|
||||
}
|
||||
|
||||
export interface KpiHostAuthHistogramCount {
|
||||
doc_count: number;
|
||||
}
|
||||
|
||||
export interface KpiHostsGeneralHit extends SearchHit {
|
||||
|
@ -17,46 +45,19 @@ export interface KpiHostsGeneralHit extends SearchHit {
|
|||
value: number;
|
||||
};
|
||||
hosts_histogram: {
|
||||
buckets: [
|
||||
{
|
||||
key_as_string: string;
|
||||
key: number;
|
||||
doc_count: number;
|
||||
count: {
|
||||
value: number;
|
||||
};
|
||||
}
|
||||
];
|
||||
buckets: Array<KpiHostHistogram<KpiHostGeneralHistogramCount>>;
|
||||
};
|
||||
unique_source_ips: {
|
||||
value: number;
|
||||
};
|
||||
unique_source_ips_histogram: {
|
||||
buckets: [
|
||||
{
|
||||
key_as_string: string;
|
||||
key: number;
|
||||
doc_count: number;
|
||||
count: {
|
||||
value: number;
|
||||
};
|
||||
}
|
||||
];
|
||||
buckets: Array<KpiHostHistogram<KpiHostGeneralHistogramCount>>;
|
||||
};
|
||||
unique_destination_ips: {
|
||||
value: number;
|
||||
};
|
||||
unique_destination_ips_histogram: {
|
||||
buckets: [
|
||||
{
|
||||
key_as_string: string;
|
||||
key: number;
|
||||
doc_count: number;
|
||||
count: {
|
||||
value: number;
|
||||
};
|
||||
}
|
||||
];
|
||||
buckets: Array<KpiHostHistogram<KpiHostGeneralHistogramCount>>;
|
||||
};
|
||||
};
|
||||
_shards: {
|
||||
|
@ -79,31 +80,13 @@ export interface KpiHostsAuthHit extends SearchHit {
|
|||
doc_count: number;
|
||||
};
|
||||
authentication_success_histogram: {
|
||||
buckets: [
|
||||
{
|
||||
key_as_string: string;
|
||||
key: number;
|
||||
doc_count: number;
|
||||
count: {
|
||||
doc_count: number;
|
||||
};
|
||||
}
|
||||
];
|
||||
buckets: Array<KpiHostHistogram<KpiHostAuthHistogramCount>>;
|
||||
};
|
||||
authentication_failure: {
|
||||
doc_count: number;
|
||||
};
|
||||
authentication_failure_histogram: {
|
||||
buckets: [
|
||||
{
|
||||
key_as_string: string;
|
||||
key: number;
|
||||
doc_count: number;
|
||||
count: {
|
||||
doc_count: number;
|
||||
};
|
||||
}
|
||||
];
|
||||
buckets: Array<KpiHostHistogram<KpiHostAuthHistogramCount>>;
|
||||
};
|
||||
};
|
||||
_shards: {
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { KpiNetworkData } from '../../graphql/types';
|
||||
import { FrameworkAdapter, FrameworkRequest } from '../framework';
|
||||
|
||||
import { ElasticsearchKpiNetworkAdapter } from './elasticsearch_adapter';
|
||||
|
@ -13,6 +12,7 @@ import * as dnsQueryDsl from './query_dns.dsl';
|
|||
import * as generalQueryDsl from './query_general.dsl';
|
||||
import * as tlsHandshakesQueryDsl from './query_tls_handshakes.dsl';
|
||||
import * as uniquePrvateIpQueryDsl from './query_unique_private_ips.dsl';
|
||||
import { KpiNetworkData } from '../../graphql/types';
|
||||
|
||||
describe('Network Kpi elasticsearch_adapter', () => {
|
||||
const mockCallWithRequest = jest.fn();
|
||||
|
@ -118,9 +118,10 @@ describe('Network Kpi elasticsearch_adapter', () => {
|
|||
expect(data).toEqual({
|
||||
networkEvents: null,
|
||||
uniqueFlowId: null,
|
||||
activeAgents: null,
|
||||
uniqueSourcePrivateIps: null,
|
||||
uniqueSourcePrivateIpsHistogram: null,
|
||||
uniqueDestinationPrivateIps: null,
|
||||
uniqueDestinationPrivateIpsHistogram: null,
|
||||
dnsQueries: null,
|
||||
tlsHandshakes: null,
|
||||
});
|
||||
|
|
|
@ -6,15 +6,34 @@
|
|||
|
||||
import { getOr } from 'lodash/fp';
|
||||
|
||||
import { KpiNetworkData } from '../../graphql/types';
|
||||
import { FrameworkAdapter, FrameworkRequest, RequestBasicOptions } from '../framework';
|
||||
import { TermAggregation } from '../types';
|
||||
|
||||
import { buildDnsQuery } from './query_dns.dsl';
|
||||
import { buildGeneralQuery } from './query_general.dsl';
|
||||
import { buildTlsHandshakeQuery } from './query_tls_handshakes.dsl';
|
||||
import { buildUniquePrvateIpQuery } from './query_unique_private_ips.dsl';
|
||||
import { KpiNetworkAdapter, KpiNetworkESMSearchBody, KpiNetworkHit } from './types';
|
||||
import {
|
||||
KpiNetworkHit,
|
||||
KpiNetworkAdapter,
|
||||
KpiNetworkESMSearchBody,
|
||||
KpiNetworkGeneralHit,
|
||||
KpiNetworkUniquePrivateIpsHit,
|
||||
} from './types';
|
||||
import { TermAggregation } from '../types';
|
||||
import { KpiNetworkHistogramData, KpiNetworkData } from '../../graphql/types';
|
||||
|
||||
const formatHistogramData = (
|
||||
data: Array<{ key_as_string: string; count: { value: number } }>
|
||||
): KpiNetworkHistogramData[] | null => {
|
||||
return data && data.length > 0
|
||||
? data.map<KpiNetworkHistogramData>(({ key_as_string, count }) => {
|
||||
return {
|
||||
x: key_as_string,
|
||||
y: getOr(null, 'value', count),
|
||||
};
|
||||
})
|
||||
: null;
|
||||
};
|
||||
|
||||
export class ElasticsearchKpiNetworkAdapter implements KpiNetworkAdapter {
|
||||
constructor(private readonly framework: FrameworkAdapter) {}
|
||||
|
@ -34,34 +53,46 @@ export class ElasticsearchKpiNetworkAdapter implements KpiNetworkAdapter {
|
|||
);
|
||||
const dnsQuery: KpiNetworkESMSearchBody[] = buildDnsQuery(options);
|
||||
const tlsHandshakesQuery: KpiNetworkESMSearchBody[] = buildTlsHandshakeQuery(options);
|
||||
const response = await this.framework.callWithRequest<KpiNetworkHit, TermAggregation>(
|
||||
request,
|
||||
'msearch',
|
||||
{
|
||||
body: [
|
||||
...generalQuery,
|
||||
...uniqueSourcePrivateIpsQuery,
|
||||
...uniqueDestinationPrivateIpsQuery,
|
||||
...dnsQuery,
|
||||
...tlsHandshakesQuery,
|
||||
],
|
||||
}
|
||||
const response = await this.framework.callWithRequest<
|
||||
KpiNetworkGeneralHit | KpiNetworkHit | KpiNetworkUniquePrivateIpsHit,
|
||||
TermAggregation
|
||||
>(request, 'msearch', {
|
||||
body: [
|
||||
...generalQuery,
|
||||
...uniqueSourcePrivateIpsQuery,
|
||||
...uniqueDestinationPrivateIpsQuery,
|
||||
...dnsQuery,
|
||||
...tlsHandshakesQuery,
|
||||
],
|
||||
});
|
||||
const uniqueSourcePrivateIpsHistogram = getOr(
|
||||
null,
|
||||
'responses.1.aggregations.histogram.buckets',
|
||||
response
|
||||
);
|
||||
const uniqueDestinationPrivateIpsHistogram = getOr(
|
||||
null,
|
||||
'responses.2.aggregations.histogram.buckets',
|
||||
response
|
||||
);
|
||||
|
||||
return {
|
||||
networkEvents: getOr(null, 'responses.0.hits.total.value', response),
|
||||
uniqueFlowId: getOr(null, 'responses.0.aggregations.unique_flow_id.value', response),
|
||||
activeAgents: getOr(null, 'responses.0.aggregations.active_agents.value', response),
|
||||
uniqueSourcePrivateIps: getOr(
|
||||
null,
|
||||
'responses.1.aggregations.unique_private_ips.value',
|
||||
response
|
||||
),
|
||||
uniqueSourcePrivateIpsHistogram: formatHistogramData(uniqueSourcePrivateIpsHistogram),
|
||||
uniqueDestinationPrivateIps: getOr(
|
||||
null,
|
||||
'responses.2.aggregations.unique_private_ips.value',
|
||||
response
|
||||
),
|
||||
uniqueDestinationPrivateIpsHistogram: formatHistogramData(
|
||||
uniqueDestinationPrivateIpsHistogram
|
||||
),
|
||||
dnsQueries: getOr(null, 'responses.3.hits.total.value', response),
|
||||
tlsHandshakes: getOr(null, 'responses.4.hits.total.value', response),
|
||||
};
|
||||
|
|
|
@ -4,10 +4,10 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { KpiNetworkData } from '../../graphql/types';
|
||||
import { FrameworkRequest, RequestBasicOptions } from '../framework';
|
||||
|
||||
import { KpiNetworkAdapter } from './types';
|
||||
import { KpiNetworkData } from '../../graphql/types';
|
||||
|
||||
export class KpiNetwork {
|
||||
constructor(private readonly adapter: KpiNetworkAdapter) {}
|
||||
|
|
|
@ -32,11 +32,11 @@ export const mockRequest = {
|
|||
operationName: 'GetKpiNetworkQuery',
|
||||
variables: {
|
||||
sourceId: 'default',
|
||||
timerange: { interval: '12h', from: 1549765830772, to: 1549852230772 },
|
||||
timerange: { interval: '12h', from: 1557445721842, to: 1557532121842 },
|
||||
filterQuery: '',
|
||||
},
|
||||
query:
|
||||
'query GetKpiNetworkQuery($sourceId: ID!, $timerange: TimerangeInput!, $filterQuery: String) {\n source(id: $sourceId) {\n id\n KpiNetwork(timerange: $timerange, filterQuery: $filterQuery) {\n networkEvents\n uniqueFlowId\n activeAgents\n uniqueSourcePrivateIps\n uniqueDestinationPrivateIps\n dnsQueries\n tlsHandshakes\n __typename\n }\n __typename\n }\n}\n',
|
||||
'fragment ChartFields on KpiNetworkHistogramData {\n key_as_string\n doc_count\n count {\n value\n __typename\n }\n __typename\n}\n\nquery GetKpiNetworkQuery($sourceId: ID!, $timerange: TimerangeInput!, $filterQuery: String) {\n source(id: $sourceId) {\n id\n KpiNetwork(timerange: $timerange, filterQuery: $filterQuery) {\n networkEvents\n networkEventsHistogram {\n ...ChartFields\n __typename\n }\n uniqueFlowId\n activeAgents\n uniqueSourcePrivateIps\n uniqueSourcePrivateIpsHistogram {\n ...ChartFields\n __typename\n }\n uniqueDestinationPrivateIps\n uniqueDestinationPrivateIpsHistogram {\n ...ChartFields\n __typename\n }\n dnsQueries\n tlsHandshakes\n __typename\n }\n __typename\n }\n}\n',
|
||||
},
|
||||
query: {},
|
||||
};
|
||||
|
@ -44,52 +44,209 @@ export const mockRequest = {
|
|||
export const mockResponse = {
|
||||
responses: [
|
||||
{
|
||||
took: 258,
|
||||
took: 384,
|
||||
timed_out: false,
|
||||
_shards: { total: 26, successful: 26, skipped: 0, failed: 0 },
|
||||
hits: { total: { value: 950867, relation: 'eq' }, max_score: null, hits: [] },
|
||||
aggregations: { unique_flow_id: { value: 50243 }, active_agents: { value: 15 } },
|
||||
_shards: {
|
||||
total: 10,
|
||||
successful: 10,
|
||||
skipped: 0,
|
||||
failed: 0,
|
||||
},
|
||||
hits: {
|
||||
total: {
|
||||
value: 733106,
|
||||
relation: 'eq',
|
||||
},
|
||||
max_score: null,
|
||||
hits: [],
|
||||
},
|
||||
aggregations: {
|
||||
unique_flow_id: {
|
||||
value: 195415,
|
||||
},
|
||||
},
|
||||
status: 200,
|
||||
},
|
||||
{
|
||||
took: 323,
|
||||
took: 224,
|
||||
timed_out: false,
|
||||
_shards: { total: 26, successful: 26, skipped: 0, failed: 0 },
|
||||
hits: { total: { value: 406839, relation: 'eq' }, max_score: null, hits: [] },
|
||||
aggregations: { unique_private_ips: { value: 383 } },
|
||||
_shards: {
|
||||
total: 10,
|
||||
successful: 10,
|
||||
skipped: 0,
|
||||
failed: 0,
|
||||
},
|
||||
hits: {
|
||||
total: {
|
||||
value: 480755,
|
||||
relation: 'eq',
|
||||
},
|
||||
max_score: null,
|
||||
hits: [],
|
||||
},
|
||||
aggregations: {
|
||||
histogram: {
|
||||
buckets: [
|
||||
{
|
||||
key_as_string: '2019-05-09T23:00:00.000Z',
|
||||
key: 1557442800000,
|
||||
doc_count: 42109,
|
||||
count: {
|
||||
value: 14,
|
||||
},
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-10T11:00:00.000Z',
|
||||
key: 1557486000000,
|
||||
doc_count: 437160,
|
||||
count: {
|
||||
value: 385,
|
||||
},
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-10T23:00:00.000Z',
|
||||
key: 1557529200000,
|
||||
doc_count: 1486,
|
||||
count: {
|
||||
value: 7,
|
||||
},
|
||||
},
|
||||
],
|
||||
interval: '12h',
|
||||
},
|
||||
unique_private_ips: {
|
||||
value: 387,
|
||||
},
|
||||
},
|
||||
status: 200,
|
||||
},
|
||||
{
|
||||
took: 323,
|
||||
took: 184,
|
||||
timed_out: false,
|
||||
_shards: { total: 26, successful: 26, skipped: 0, failed: 0 },
|
||||
hits: { total: { value: 406839, relation: 'eq' }, max_score: null, hits: [] },
|
||||
aggregations: { unique_private_ips: { value: 18 } },
|
||||
_shards: {
|
||||
total: 10,
|
||||
successful: 10,
|
||||
skipped: 0,
|
||||
failed: 0,
|
||||
},
|
||||
hits: {
|
||||
total: {
|
||||
value: 459283,
|
||||
relation: 'eq',
|
||||
},
|
||||
max_score: null,
|
||||
hits: [],
|
||||
},
|
||||
aggregations: {
|
||||
histogram: {
|
||||
buckets: [
|
||||
{
|
||||
key_as_string: '2019-05-09T23:00:00.000Z',
|
||||
key: 1557442800000,
|
||||
doc_count: 36253,
|
||||
count: {
|
||||
value: 11,
|
||||
},
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-10T11:00:00.000Z',
|
||||
key: 1557486000000,
|
||||
doc_count: 421719,
|
||||
count: {
|
||||
value: 877,
|
||||
},
|
||||
},
|
||||
{
|
||||
key_as_string: '2019-05-10T23:00:00.000Z',
|
||||
key: 1557529200000,
|
||||
doc_count: 1311,
|
||||
count: {
|
||||
value: 7,
|
||||
},
|
||||
},
|
||||
],
|
||||
interval: '12h',
|
||||
},
|
||||
unique_private_ips: {
|
||||
value: 878,
|
||||
},
|
||||
},
|
||||
status: 200,
|
||||
},
|
||||
{
|
||||
took: 9,
|
||||
took: 64,
|
||||
timed_out: false,
|
||||
_shards: { total: 38, successful: 38, skipped: 36, failed: 0 },
|
||||
hits: { total: { value: 278, relation: 'eq' }, max_score: null, hits: [] },
|
||||
_shards: {
|
||||
total: 10,
|
||||
successful: 10,
|
||||
skipped: 0,
|
||||
failed: 0,
|
||||
},
|
||||
hits: {
|
||||
total: {
|
||||
value: 10942,
|
||||
relation: 'eq',
|
||||
},
|
||||
max_score: null,
|
||||
hits: [],
|
||||
},
|
||||
status: 200,
|
||||
},
|
||||
{
|
||||
took: 60,
|
||||
took: 57,
|
||||
timed_out: false,
|
||||
_shards: { total: 38, successful: 38, skipped: 36, failed: 0 },
|
||||
hits: { total: { value: 10000, relation: 'gte' }, max_score: null, hits: [] },
|
||||
_shards: {
|
||||
total: 10,
|
||||
successful: 10,
|
||||
skipped: 0,
|
||||
failed: 0,
|
||||
},
|
||||
hits: {
|
||||
total: {
|
||||
value: 54482,
|
||||
relation: 'eq',
|
||||
},
|
||||
max_score: null,
|
||||
hits: [],
|
||||
},
|
||||
status: 200,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export const mockResult = {
|
||||
networkEvents: 950867,
|
||||
uniqueFlowId: 50243,
|
||||
activeAgents: 15,
|
||||
uniqueSourcePrivateIps: 383,
|
||||
uniqueDestinationPrivateIps: 18,
|
||||
dnsQueries: 278,
|
||||
tlsHandshakes: 10000,
|
||||
dnsQueries: 10942,
|
||||
networkEvents: 733106,
|
||||
tlsHandshakes: 54482,
|
||||
uniqueDestinationPrivateIps: 878,
|
||||
uniqueDestinationPrivateIpsHistogram: [
|
||||
{
|
||||
x: '2019-05-09T23:00:00.000Z',
|
||||
y: 11,
|
||||
},
|
||||
{
|
||||
x: '2019-05-10T11:00:00.000Z',
|
||||
y: 877,
|
||||
},
|
||||
{
|
||||
x: '2019-05-10T23:00:00.000Z',
|
||||
y: 7,
|
||||
},
|
||||
],
|
||||
uniqueFlowId: 195415,
|
||||
uniqueSourcePrivateIps: 387,
|
||||
uniqueSourcePrivateIpsHistogram: [
|
||||
{
|
||||
x: '2019-05-09T23:00:00.000Z',
|
||||
y: 14,
|
||||
},
|
||||
{
|
||||
x: '2019-05-10T11:00:00.000Z',
|
||||
y: 385,
|
||||
},
|
||||
{
|
||||
x: '2019-05-10T23:00:00.000Z',
|
||||
y: 7,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -75,11 +75,6 @@ export const buildGeneralQuery = ({
|
|||
field: 'network.community_id',
|
||||
},
|
||||
},
|
||||
active_agents: {
|
||||
cardinality: {
|
||||
field: 'agent.id',
|
||||
},
|
||||
},
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
|
|
|
@ -117,6 +117,19 @@ export const buildUniquePrvateIpQuery = (
|
|||
field: `${attrQuery}.ip`,
|
||||
},
|
||||
},
|
||||
histogram: {
|
||||
auto_date_histogram: {
|
||||
field: '@timestamp',
|
||||
buckets: '6',
|
||||
},
|
||||
aggs: {
|
||||
count: {
|
||||
cardinality: {
|
||||
field: `${attrQuery}.ip`,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
query: {
|
||||
bool: {
|
||||
|
|
|
@ -3,21 +3,37 @@
|
|||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
import { KpiNetworkData } from '../../graphql/types';
|
||||
import { FrameworkRequest, RequestBasicOptions } from '../framework';
|
||||
import { MSearchHeader, SearchHit } from '../types';
|
||||
import { KpiNetworkHistogramData, KpiNetworkData } from '../../graphql/types';
|
||||
|
||||
export interface KpiNetworkAdapter {
|
||||
getKpiNetwork(request: FrameworkRequest, options: RequestBasicOptions): Promise<KpiNetworkData>;
|
||||
}
|
||||
|
||||
export interface KpiNetworkHit extends SearchHit {
|
||||
export interface KpiNetworkHit {
|
||||
hits: {
|
||||
total: {
|
||||
value: number;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export interface KpiNetworkGeneralHit extends SearchHit, KpiNetworkHit {
|
||||
aggregations: {
|
||||
unique_flow_id: {
|
||||
value: number;
|
||||
};
|
||||
active_agents: {
|
||||
value: number | null;
|
||||
};
|
||||
}
|
||||
|
||||
export interface KpiNetworkUniquePrivateIpsHit extends SearchHit {
|
||||
aggregations: {
|
||||
unique_private_ips: {
|
||||
value: number;
|
||||
};
|
||||
histogram: {
|
||||
buckets: [KpiNetworkHistogramData];
|
||||
};
|
||||
};
|
||||
}
|
||||
|
@ -32,3 +48,8 @@ export interface KpiNetworkBody {
|
|||
export type KpiNetworkESMSearchBody = KpiNetworkBody | MSearchHeader;
|
||||
|
||||
export type UniquePrivateAttributeQuery = 'source' | 'destination';
|
||||
|
||||
// export interface KpiNetworkHistogram {
|
||||
// x: string | null | undefined;
|
||||
// y: number | null | undefined;
|
||||
// }
|
||||
|
|
|
@ -9135,9 +9135,8 @@
|
|||
"xpack.siem.kpiNetwork.source.loadingDescription": "読み込み中",
|
||||
"xpack.siem.kpiNetwork.source.networkEventsTitle": "ネットワークイベント",
|
||||
"xpack.siem.kpiNetwork.source.tlsHandshakesTitle": "TLS ハンドシェイク",
|
||||
"xpack.siem.kpiNetwork.source.uniqueDestinationPrivateIpsTitle": "固有の送信先 IP",
|
||||
"xpack.siem.kpiNetwork.source.uniqueSourcePrivateIpsTitle": "固有の送信元 IP",
|
||||
"xpack.siem.kpiNetwork.source.uniquiIdTitle": "固有のフロー ID",
|
||||
"xpack.siem.kpiNetwork.source.uniquePrivateIpsTitle": "固有の送信元 IP",
|
||||
"xpack.siem.kpiNetwork.source.uniqueFlowIdsTitle": "固有のフロー ID",
|
||||
"xpack.siem.loadingMoreTable.loadingDescription": "読み込み中",
|
||||
"xpack.siem.loadingMoreTable.loadMoreDescription": "他を読み込む",
|
||||
"xpack.siem.loadingMoreTable.showing": "表示中",
|
||||
|
|
|
@ -9145,9 +9145,8 @@
|
|||
"xpack.siem.kpiNetwork.source.loadingDescription": "正在加载",
|
||||
"xpack.siem.kpiNetwork.source.networkEventsTitle": "网络事件",
|
||||
"xpack.siem.kpiNetwork.source.tlsHandshakesTitle": "TLS 握手",
|
||||
"xpack.siem.kpiNetwork.source.uniqueDestinationPrivateIpsTitle": "唯一目标 IP",
|
||||
"xpack.siem.kpiNetwork.source.uniqueSourcePrivateIpsTitle": "唯一源 IP",
|
||||
"xpack.siem.kpiNetwork.source.uniquiIdTitle": "唯一流 ID",
|
||||
"xpack.siem.kpiNetwork.source.uniquePrivateIpsTitle": "唯一目标 IP",
|
||||
"xpack.siem.kpiNetwork.source.uniqueFlowIdsTitle": "唯一源 IP",
|
||||
"xpack.siem.loadingMoreTable.loadingDescription": "正在加载",
|
||||
"xpack.siem.loadingMoreTable.loadMoreDescription": "加载更多",
|
||||
"xpack.siem.loadingMoreTable.showing": "显示",
|
||||
|
|
|
@ -25,81 +25,49 @@ const kpiHostsTests: KbnTestProvider = ({ getService }) => {
|
|||
hostsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 1,
|
||||
},
|
||||
y: 1,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 0,
|
||||
},
|
||||
y: 0,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 1,
|
||||
},
|
||||
y: 1,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 1,
|
||||
},
|
||||
y: 1,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
],
|
||||
authSuccess: 0,
|
||||
authSuccessHistogram: [],
|
||||
authSuccessHistogram: null,
|
||||
authFailure: 0,
|
||||
authFailureHistogram: [],
|
||||
authFailureHistogram: null,
|
||||
uniqueSourceIps: 121,
|
||||
uniqueSourceIpsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 52,
|
||||
},
|
||||
y: 52,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 0,
|
||||
},
|
||||
y: 0,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 31,
|
||||
},
|
||||
y: 31,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 88,
|
||||
},
|
||||
y: 88,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
],
|
||||
|
@ -107,38 +75,22 @@ const kpiHostsTests: KbnTestProvider = ({ getService }) => {
|
|||
uniqueDestinationIpsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 61,
|
||||
},
|
||||
y: 61,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 0,
|
||||
},
|
||||
y: 0,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 45,
|
||||
},
|
||||
y: 45,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 114,
|
||||
},
|
||||
y: 114,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
],
|
||||
|
@ -177,81 +129,49 @@ const kpiHostsTests: KbnTestProvider = ({ getService }) => {
|
|||
hostsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 1,
|
||||
},
|
||||
y: 1,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 0,
|
||||
},
|
||||
y: 0,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 1,
|
||||
},
|
||||
y: 1,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 1,
|
||||
},
|
||||
y: 1,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
],
|
||||
authSuccess: 0,
|
||||
authSuccessHistogram: [],
|
||||
authSuccessHistogram: null,
|
||||
authFailure: 0,
|
||||
authFailureHistogram: [],
|
||||
authFailureHistogram: null,
|
||||
uniqueSourceIps: 121,
|
||||
uniqueSourceIpsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 52,
|
||||
},
|
||||
y: 52,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 0,
|
||||
},
|
||||
y: 0,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 31,
|
||||
},
|
||||
y: 31,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 88,
|
||||
},
|
||||
y: 88,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
],
|
||||
|
@ -259,38 +179,22 @@ const kpiHostsTests: KbnTestProvider = ({ getService }) => {
|
|||
uniqueDestinationIpsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 61,
|
||||
},
|
||||
y: 61,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 0,
|
||||
},
|
||||
y: 0,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 45,
|
||||
},
|
||||
y: 45,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: {
|
||||
__typename: 'Count',
|
||||
doc_count: null,
|
||||
value: 114,
|
||||
},
|
||||
y: 114,
|
||||
__typename: 'KpiHostHistogramData',
|
||||
},
|
||||
],
|
||||
|
|
|
@ -19,6 +19,59 @@ const kpiNetworkTests: KbnTestProvider = ({ getService }) => {
|
|||
|
||||
const FROM = new Date('2000-01-01T00:00:00.000Z').valueOf();
|
||||
const TO = new Date('3000-01-01T00:00:00.000Z').valueOf();
|
||||
const expectedResult = {
|
||||
__typename: 'KpiNetworkData',
|
||||
networkEvents: 6157,
|
||||
uniqueFlowId: 712,
|
||||
uniqueSourcePrivateIps: 8,
|
||||
uniqueSourcePrivateIpsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: 8,
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: 0,
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: 8,
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: 7,
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
},
|
||||
],
|
||||
uniqueDestinationPrivateIps: 9,
|
||||
uniqueDestinationPrivateIpsHistogram: [
|
||||
{
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: 8,
|
||||
},
|
||||
{
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: 8,
|
||||
},
|
||||
{
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: 8,
|
||||
},
|
||||
],
|
||||
dnsQueries: 169,
|
||||
tlsHandshakes: 62,
|
||||
};
|
||||
|
||||
it('Make sure that we get KpiNetwork data', () => {
|
||||
return client
|
||||
|
@ -36,13 +89,7 @@ const kpiNetworkTests: KbnTestProvider = ({ getService }) => {
|
|||
})
|
||||
.then(resp => {
|
||||
const kpiNetwork = resp.data.source.KpiNetwork;
|
||||
expect(kpiNetwork!.networkEvents).to.be(6157);
|
||||
expect(kpiNetwork!.uniqueFlowId).to.be(712);
|
||||
expect(kpiNetwork!.activeAgents).to.equal(1);
|
||||
expect(kpiNetwork!.uniqueSourcePrivateIps).to.equal(8);
|
||||
expect(kpiNetwork!.uniqueDestinationPrivateIps).to.equal(9);
|
||||
expect(kpiNetwork!.dnsQueries).to.equal(169);
|
||||
expect(kpiNetwork!.tlsHandshakes).to.equal(62);
|
||||
expect(kpiNetwork).to.eql(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -53,7 +100,59 @@ const kpiNetworkTests: KbnTestProvider = ({ getService }) => {
|
|||
|
||||
const FROM = new Date('2000-01-01T00:00:00.000Z').valueOf();
|
||||
const TO = new Date('3000-01-01T00:00:00.000Z').valueOf();
|
||||
|
||||
const expectedResult = {
|
||||
__typename: 'KpiNetworkData',
|
||||
networkEvents: 6157,
|
||||
uniqueFlowId: 712,
|
||||
uniqueSourcePrivateIps: 8,
|
||||
uniqueSourcePrivateIpsHistogram: [
|
||||
{
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: 8,
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: 0,
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: 8,
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
},
|
||||
{
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: 7,
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
},
|
||||
],
|
||||
uniqueDestinationPrivateIps: 9,
|
||||
uniqueDestinationPrivateIpsHistogram: [
|
||||
{
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
x: '2019-02-09T16:00:00.000Z',
|
||||
y: 8,
|
||||
},
|
||||
{
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
x: '2019-02-09T19:00:00.000Z',
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
x: '2019-02-09T22:00:00.000Z',
|
||||
y: 8,
|
||||
},
|
||||
{
|
||||
__typename: 'KpiNetworkHistogramData',
|
||||
x: '2019-02-10T01:00:00.000Z',
|
||||
y: 8,
|
||||
},
|
||||
],
|
||||
dnsQueries: 169,
|
||||
tlsHandshakes: 62,
|
||||
};
|
||||
it('Make sure that we get KpiNetwork data', () => {
|
||||
return client
|
||||
.query<GetKpiNetworkQuery.Query>({
|
||||
|
@ -70,13 +169,7 @@ const kpiNetworkTests: KbnTestProvider = ({ getService }) => {
|
|||
})
|
||||
.then(resp => {
|
||||
const kpiNetwork = resp.data.source.KpiNetwork;
|
||||
expect(kpiNetwork!.networkEvents).to.be(6157);
|
||||
expect(kpiNetwork!.uniqueFlowId).to.be(712);
|
||||
expect(kpiNetwork!.activeAgents).to.equal(1);
|
||||
expect(kpiNetwork!.uniqueSourcePrivateIps).to.equal(8);
|
||||
expect(kpiNetwork!.uniqueDestinationPrivateIps).to.equal(9);
|
||||
expect(kpiNetwork!.dnsQueries).to.equal(169);
|
||||
expect(kpiNetwork!.tlsHandshakes).to.equal(62);
|
||||
expect(kpiNetwork).to.eql(expectedResult);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue