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

anomaly detection changes. #123

Merged
merged 5 commits into from
Apr 28, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .lintstagedrc
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"*.{js,jsx,json,css,md}": ["prettier --write", "git add"]
"*.{ts,tsx,js,jsx,json,css,md}": ["prettier --write", "git add"]
ylwu-amzn marked this conversation as resolved.
Show resolved Hide resolved
}

12 changes: 9 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
*/

import { resolve } from 'path';
import { alerts, destinations, elasticsearch, monitors } from './server/routes';
import { alerts, destinations, elasticsearch, monitors, detectors } from './server/routes';
import {
AlertService,
DestinationsService,
ElasticsearchService,
MonitorService,
AnomalyDetectorService,
} from './server/services';
import { createAlertingCluster } from './server/clusters';
import { createAlertingCluster, createAlertingAdCluster } from './server/clusters';
import { PLUGIN_NAME } from './utils/constants';

export default function(kibana) {
Expand All @@ -33,6 +34,7 @@ export default function(kibana) {
title: 'Alerting',
description: 'Kibana Alerting Plugin',
main: `plugins/${PLUGIN_NAME}/app`,
icon: `plugins/${PLUGIN_NAME}/images/alerting_icon.svg`,
},

hacks: [`plugins/${PLUGIN_NAME}/hack`],
Expand All @@ -47,25 +49,29 @@ export default function(kibana) {
init(server, options) {
// Create clusters
createAlertingCluster(server);
createAlertingAdCluster(server);
ylwu-amzn marked this conversation as resolved.
Show resolved Hide resolved

// Initialize services
const esDriver = server.plugins.elasticsearch;
const alertService = new AlertService(esDriver);
const elasticsearchService = new ElasticsearchService(esDriver);
const monitorService = new MonitorService(esDriver);
const destinationsService = new DestinationsService(esDriver);
const anomalyDetectorService = new AnomalyDetectorService(esDriver);
const services = {
alertService,
destinationsService,
elasticsearchService,
monitorService,
anomalyDetectorService,
};

// Add server routes
alerts(server, services);
destinations(server, services);
elasticsearch(server, services);
monitors(server, services);
detectors(server, services);
},
});
}
}
14 changes: 12 additions & 2 deletions public/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,18 @@ import { HashRouter as Router, Route } from 'react-router-dom';
import 'react-vis/dist/style.css';
import 'ui/autoload/styles';
import './less/main.less';

import Main from './pages/Main';
import { AppContext } from './utils/AppContext';

const app = uiModules.get('apps/alerting');
const darkMode = chrome.getUiSettingsClient().get('theme:darkMode') || false;
Copy link
Contributor

Choose a reason for hiding this comment

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

I can't remember but I thought @mihirsoni already added some dark mode logic to Alerting before?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not sure about that, @mihirsoni , can you confirm? Maybe you have reviewed this part before?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think this could work out of the box. This could be needed.


//Load Chart's dark mode CSS
if (darkMode) {
require('@elastic/charts/dist/theme_only_dark.css');
} else {
require('@elastic/charts/dist/theme_only_light.css');
}

app.config($locationProvider => {
$locationProvider.html5Mode({
Expand All @@ -43,7 +51,9 @@ function RootController($scope, $element, $http) {
// render react to DOM
render(
<Router>
<Route render={props => <Main title="Alerting" httpClient={$http} {...props} />} />
<AppContext.Provider value={{ httpClient: $http, darkMode }}>
<Route render={props => <Main title="Alerting" httpClient={$http} {...props} />} />
</AppContext.Provider>
</Router>,
domNode
);
Expand Down
41 changes: 41 additions & 0 deletions public/components/ChartContainer/ChartContainer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
ylwu-amzn marked this conversation as resolved.
Show resolved Hide resolved
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import PropTypes from 'prop-types';
import React from 'react';

const ChartContainer = ({ children, style }) => (
<div
style={{
borderRadius: '5px',
padding: '10px',
border: '1px solid #D9D9D9',
height: '250px',
width: '100%',
...style,
}}
>
{children}
</div>
);
ChartContainer.propTypes = {
style: PropTypes.object,
children: PropTypes.oneOfType([PropTypes.node, PropTypes.arrayOf([PropTypes.node])]).isRequired,
};
ChartContainer.defaultPropTypes = {
style: {},
};

export { ChartContainer };
16 changes: 16 additions & 0 deletions public/images/alerting_icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import React from 'react';
import PropTypes from 'prop-types';
import DelayedLoader from '../../../../../components/DelayedLoader';
ylwu-amzn marked this conversation as resolved.
Show resolved Hide resolved
import { EuiText, EuiSpacer } from '@elastic/eui';
import {
Chart,
getAxisId,
Axis,
getSpecId,
LineSeries,
niceTimeFormatter,
Settings,
Position,
getAnnotationId,
LineAnnotation,
} from '@elastic/charts';
dbbaughe marked this conversation as resolved.
Show resolved Hide resolved
import { ChartContainer } from '../../../../../components/ChartContainer/ChartContainer';

const getAxisTitle = (displayGrade, displayConfidence) => {
ylwu-amzn marked this conversation as resolved.
Show resolved Hide resolved
if (displayGrade && displayConfidence) {
return 'Anomaly grade / confidence';
} else if (displayGrade) {
ylwu-amzn marked this conversation as resolved.
Show resolved Hide resolved
return 'Anomaly grade';
}
return 'Anomaly confidence';
};

const AnomaliesChart = props => {
const timeFormatter = niceTimeFormatter([props.startDateTime, props.endDateTime]);
return (
<DelayedLoader isLoading={props.isLoading}>
{showLoader => (
<React.Fragment>
{props.showTitle ? (
<EuiText size="xs">
<strong>{props.title}</strong>
</EuiText>
) : null}
<EuiSpacer size="s" />
<div>
<ChartContainer
style={{ height: '300px', width: '100%', opacity: showLoader ? 0.2 : 1 }}
>
<Chart>
{props.showSettings ? (
<Settings
showLegend
legendPosition={Position.Bottom}
showLegendDisplayValue={false}
/>
) : null}
<Axis id={getAxisId('bottom')} position="bottom" tickFormat={timeFormatter} />
<Axis
id={getAxisId('left')}
title={getAxisTitle(props.displayGrade, props.displayConfidence)}
position="left"
domain={{ min: 0, max: 1 }}
/>
{props.annotationData ? (
<LineAnnotation
annotationId={getAnnotationId('anomalyAnnotation')}
domainType="yDomain"
dataValues={props.annotationData}
style={{
line: {
strokeWidth: 1.5,
stroke: '#f00',
},
}}
/>
) : null}
{props.displayGrade ? (
<LineSeries
id={getSpecId('Anomaly grade')}
xScaleType="time"
yScaleType="linear"
xAccessor={'plotTime'}
yAccessors={['anomalyGrade']}
data={props.anomalies}
/>
) : null}
{props.displayConfidence ? (
<LineSeries
id={getSpecId('Confidence')}
xScaleType="time"
yScaleType="linear"
xAccessor={'plotTime'}
yAccessors={['confidence']}
data={props.anomalies}
/>
) : null}
</Chart>
</ChartContainer>
</div>
</React.Fragment>
)}
</DelayedLoader>
);
};

AnomaliesChart.propTypes = {
startDateTime: PropTypes.number,
endDateTime: PropTypes.number,
endDate: PropTypes.number,
isLoading: PropTypes.bool,
showTitle: PropTypes.bool,
showSettings: PropTypes.bool,
annotationData: PropTypes.array,
anomalies: PropTypes.array.isRequired,
title: PropTypes.string,
};

AnomaliesChart.defaultProps = {
isLoading: false,
showTitle: true,
showSettings: true,
anomalies: undefined,
title: 'Sample preview for anomaly score',
};

export { AnomaliesChart };
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2019 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

import React from 'react';
import { render } from 'enzyme';
import { AnomaliesChart } from './AnomaliesChart';
ylwu-amzn marked this conversation as resolved.
Show resolved Hide resolved
import moment from 'moment';

const startTime = moment('2018-10-25T09:30:00').valueOf();
const endTime = moment('2018-10-29T09:30:00').valueOf();

describe('AnomaliesChart', () => {
test('renders ', () => {
const sampleData = [
{
anomalyGrade: 0.9983687181753063,
anomalyScore: 0.8381447468893426,
confidence: 0.42865659282252266,
detectorId: 'temp',
endTime: 1569097677667,
plotTime: 1569097377667,
startTime: 1569097077667,
},
];
const component = (
<AnomaliesChart
anomalies={sampleData}
startDateTime={startTime}
endDateTime={endTime}
isLoading={false}
displayGrade
displayConfidence
showTitle
/>
);
expect(render(component)).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`AnomaliesChart renders 1`] = `
Array [
<div
class="euiText euiText--extraSmall"
>
<strong>
Sample preview for anomaly score
</strong>
</div>,
<div
class="euiSpacer euiSpacer--s"
/>,
<div>
<div
style="border-radius:5px;padding:10px;border:1px solid #D9D9D9;height:300px;width:100%;opacity:1"
>
<div
class="echChart"
>
<div
class="echChartStatus"
data-ech-render-complete="false"
data-ech-render-count="0"
/>
<div
class="echChartResizer"
/>
<div
class="echContainer"
>
<div
class="echReactiveChart_unavailable"
>
<p>
No data to display
</p>
</div>
</div>
</div>
</div>
</div>,
]
`;
Loading