Skip to content
This repository was archived by the owner on Aug 2, 2022. It is now read-only.

Commit b35f110

Browse files
ylwu-amznmihirsoni
andauthored
anomaly detection changes. (#123)
* anomaly detection changes. Co-authored-by: mihirsoni <[email protected]> * remove anomaly detector option when create monitor if AD plugin not installed * address comments: copyright, remove unused code,etc * remove extra showLoader Co-authored-by: mihirsoni <[email protected]>
1 parent 385854f commit b35f110

File tree

70 files changed

+3623
-145
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

70 files changed

+3623
-145
lines changed

.lintstagedrc

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
22
"*.{js,jsx,json,css,md}": ["prettier --write", "git add"]
33
}
4+

index.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
*/
1515

1616
import { resolve } from 'path';
17-
import { alerts, destinations, elasticsearch, monitors } from './server/routes';
17+
import { alerts, destinations, elasticsearch, monitors, detectors } from './server/routes';
1818
import {
1919
AlertService,
2020
DestinationsService,
2121
ElasticsearchService,
2222
MonitorService,
23+
AnomalyDetectorService,
2324
} from './server/services';
24-
import { createAlertingCluster } from './server/clusters';
25+
import { createAlertingCluster, createAlertingADCluster } from './server/clusters';
2526
import { PLUGIN_NAME } from './utils/constants';
2627

2728
export default function(kibana) {
@@ -33,6 +34,7 @@ export default function(kibana) {
3334
title: 'Alerting',
3435
description: 'Kibana Alerting Plugin',
3536
main: `plugins/${PLUGIN_NAME}/app`,
37+
icon: `plugins/${PLUGIN_NAME}/images/alerting_icon.svg`,
3638
},
3739

3840
hacks: [`plugins/${PLUGIN_NAME}/hack`],
@@ -47,25 +49,29 @@ export default function(kibana) {
4749
init(server, options) {
4850
// Create clusters
4951
createAlertingCluster(server);
52+
createAlertingADCluster(server);
5053

5154
// Initialize services
5255
const esDriver = server.plugins.elasticsearch;
5356
const alertService = new AlertService(esDriver);
5457
const elasticsearchService = new ElasticsearchService(esDriver);
5558
const monitorService = new MonitorService(esDriver);
5659
const destinationsService = new DestinationsService(esDriver);
60+
const anomalyDetectorService = new AnomalyDetectorService(esDriver);
5761
const services = {
5862
alertService,
5963
destinationsService,
6064
elasticsearchService,
6165
monitorService,
66+
anomalyDetectorService,
6267
};
6368

6469
// Add server routes
6570
alerts(server, services);
6671
destinations(server, services);
6772
elasticsearch(server, services);
6873
monitors(server, services);
74+
detectors(server, services);
6975
},
7076
});
71-
}
77+
}

public/app.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,18 @@ import { HashRouter as Router, Route } from 'react-router-dom';
2222
import 'react-vis/dist/style.css';
2323
import 'ui/autoload/styles';
2424
import './less/main.less';
25-
2625
import Main from './pages/Main';
26+
import { AppContext } from './utils/AppContext';
2727

2828
const app = uiModules.get('apps/alerting');
29+
const darkMode = chrome.getUiSettingsClient().get('theme:darkMode') || false;
30+
31+
//Load Chart's dark mode CSS
32+
if (darkMode) {
33+
require('@elastic/charts/dist/theme_only_dark.css');
34+
} else {
35+
require('@elastic/charts/dist/theme_only_light.css');
36+
}
2937

3038
app.config($locationProvider => {
3139
$locationProvider.html5Mode({
@@ -43,7 +51,9 @@ function RootController($scope, $element, $http) {
4351
// render react to DOM
4452
render(
4553
<Router>
46-
<Route render={props => <Main title="Alerting" httpClient={$http} {...props} />} />
54+
<AppContext.Provider value={{ httpClient: $http, darkMode }}>
55+
<Route render={props => <Main title="Alerting" httpClient={$http} {...props} />} />
56+
</AppContext.Provider>
4757
</Router>,
4858
domNode
4959
);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
/*
2+
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
import PropTypes from 'prop-types';
17+
import React from 'react';
18+
19+
const ChartContainer = ({ children, style }) => (
20+
<div
21+
style={{
22+
borderRadius: '5px',
23+
padding: '10px',
24+
border: '1px solid #D9D9D9',
25+
height: '250px',
26+
width: '100%',
27+
...style,
28+
}}
29+
>
30+
{children}
31+
</div>
32+
);
33+
ChartContainer.propTypes = {
34+
style: PropTypes.object,
35+
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf([PropTypes.node])]).isRequired,
36+
};
37+
ChartContainer.defaultPropTypes = {
38+
style: {},
39+
};
40+
41+
export { ChartContainer };

public/images/alerting_icon.svg

+16
Loading
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/*
2+
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
import React from 'react';
17+
import PropTypes from 'prop-types';
18+
import { EuiText, EuiSpacer } from '@elastic/eui';
19+
import {
20+
Chart,
21+
getAxisId,
22+
Axis,
23+
getSpecId,
24+
LineSeries,
25+
niceTimeFormatter,
26+
Settings,
27+
Position,
28+
getAnnotationId,
29+
LineAnnotation,
30+
} from '@elastic/charts';
31+
import { ChartContainer } from '../../../../../components/ChartContainer/ChartContainer';
32+
import DelayedLoader from '../../../../../components/DelayedLoader';
33+
34+
const getAxisTitle = (displayGrade, displayConfidence) => {
35+
if (displayGrade && displayConfidence) {
36+
return 'Anomaly grade / confidence';
37+
}
38+
return displayGrade ? 'Anomaly grade' : 'Anomaly confidence';
39+
};
40+
41+
const AnomaliesChart = props => {
42+
const timeFormatter = niceTimeFormatter([props.startDateTime, props.endDateTime]);
43+
return (
44+
<DelayedLoader isLoading={props.isLoading}>
45+
{showLoader => (
46+
<React.Fragment>
47+
{props.showTitle ? (
48+
<EuiText size="xs">
49+
<strong>{props.title}</strong>
50+
</EuiText>
51+
) : null}
52+
<EuiSpacer size="s" />
53+
<div>
54+
<ChartContainer
55+
style={{ height: '300px', width: '100%', opacity: showLoader ? 0.2 : 1 }}
56+
>
57+
<Chart>
58+
{props.showSettings ? (
59+
<Settings
60+
showLegend
61+
legendPosition={Position.Bottom}
62+
showLegendDisplayValue={false}
63+
/>
64+
) : null}
65+
<Axis id={getAxisId('bottom')} position="bottom" tickFormat={timeFormatter} />
66+
<Axis
67+
id={getAxisId('left')}
68+
title={getAxisTitle(props.displayGrade, props.displayConfidence)}
69+
position="left"
70+
domain={{ min: 0, max: 1 }}
71+
/>
72+
{props.annotationData ? (
73+
<LineAnnotation
74+
annotationId={getAnnotationId('anomalyAnnotation')}
75+
domainType="yDomain"
76+
dataValues={props.annotationData}
77+
style={{
78+
line: {
79+
strokeWidth: 1.5,
80+
stroke: '#f00',
81+
},
82+
}}
83+
/>
84+
) : null}
85+
{props.displayGrade ? (
86+
<LineSeries
87+
id={getSpecId('Anomaly grade')}
88+
xScaleType="time"
89+
yScaleType="linear"
90+
xAccessor={'plotTime'}
91+
yAccessors={['anomalyGrade']}
92+
data={props.anomalies}
93+
/>
94+
) : null}
95+
{props.displayConfidence ? (
96+
<LineSeries
97+
id={getSpecId('Confidence')}
98+
xScaleType="time"
99+
yScaleType="linear"
100+
xAccessor={'plotTime'}
101+
yAccessors={['confidence']}
102+
data={props.anomalies}
103+
/>
104+
) : null}
105+
</Chart>
106+
</ChartContainer>
107+
</div>
108+
</React.Fragment>
109+
)}
110+
</DelayedLoader>
111+
);
112+
};
113+
114+
AnomaliesChart.propTypes = {
115+
startDateTime: PropTypes.number,
116+
endDateTime: PropTypes.number,
117+
endDate: PropTypes.number,
118+
isLoading: PropTypes.bool,
119+
showTitle: PropTypes.bool,
120+
showSettings: PropTypes.bool,
121+
annotationData: PropTypes.array,
122+
anomalies: PropTypes.array.isRequired,
123+
title: PropTypes.string,
124+
};
125+
126+
AnomaliesChart.defaultProps = {
127+
isLoading: false,
128+
showTitle: true,
129+
showSettings: true,
130+
anomalies: undefined,
131+
title: 'Sample preview for anomaly score',
132+
};
133+
134+
export { AnomaliesChart };
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
import React from 'react';
17+
import { render } from 'enzyme';
18+
import moment from 'moment';
19+
import { AnomaliesChart } from './AnomaliesChart';
20+
21+
const startTime = moment('2018-10-25T09:30:00').valueOf();
22+
const endTime = moment('2018-10-29T09:30:00').valueOf();
23+
24+
describe('AnomaliesChart', () => {
25+
test('renders ', () => {
26+
const sampleData = [
27+
{
28+
anomalyGrade: 0.9983687181753063,
29+
anomalyScore: 0.8381447468893426,
30+
confidence: 0.42865659282252266,
31+
detectorId: 'temp',
32+
endTime: 1569097677667,
33+
plotTime: 1569097377667,
34+
startTime: 1569097077667,
35+
},
36+
];
37+
const component = (
38+
<AnomaliesChart
39+
anomalies={sampleData}
40+
startDateTime={startTime}
41+
endDateTime={endTime}
42+
isLoading={false}
43+
displayGrade
44+
displayConfidence
45+
showTitle
46+
/>
47+
);
48+
expect(render(component)).toMatchSnapshot();
49+
});
50+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Jest Snapshot v1, https://goo.gl/fbAQLP
2+
3+
exports[`AnomaliesChart renders 1`] = `
4+
Array [
5+
<div
6+
class="euiText euiText--extraSmall"
7+
>
8+
<strong>
9+
Sample preview for anomaly score
10+
</strong>
11+
</div>,
12+
<div
13+
class="euiSpacer euiSpacer--s"
14+
/>,
15+
<div>
16+
<div
17+
style="border-radius:5px;padding:10px;border:1px solid #D9D9D9;height:300px;width:100%;opacity:1"
18+
>
19+
<div
20+
class="echChart"
21+
>
22+
<div
23+
class="echChartStatus"
24+
data-ech-render-complete="false"
25+
data-ech-render-count="0"
26+
/>
27+
<div
28+
class="echChartResizer"
29+
/>
30+
<div
31+
class="echContainer"
32+
>
33+
<div
34+
class="echReactiveChart_unavailable"
35+
>
36+
<p>
37+
No data to display
38+
</p>
39+
</div>
40+
</div>
41+
</div>
42+
</div>
43+
</div>,
44+
]
45+
`;

0 commit comments

Comments
 (0)