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

Adding flyout to create detector from existing monitor through Monitor Details page #180

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

amitgalitz
Copy link

Description of changes:

These are the front end changes for the AD Everywhere project. The main changes revolve adding the create detector button to the monitor details page which converts the existing monitor configurations to Anomaly detector configurations. The configurations are then validated against the validation API to pre fill the fly out. This button is only useble on visual graph type monitors.

Internal Changes:

  • DetectorFailure.js and CreateDetector.js flyouts, conversion and button added to MonitorDetails.js.
  • Added server code to call the AD validation API and to start AD.

External Changes:
Added button:
Screen Shot 2020-08-31 at 2 30 46 PM

Example of pre filled flyout:
image

Example of flyout if query filter is wrong and needs changing:
image

Example of Detector Failure flyout when there is no way detector can be made due to no historical data:
image

After Create detector has been clicked on the flyout:
image

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.

) {
calloutType = 'valid';
}
for (let [key, value] of Object.entries(context.suggestedChanges)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

it seems that you're looping through all of the suggested changes, and would overwrite calloutType if there was multiple suggested changes (one for maxInterval, one for filterQueryTooSparse). Can you confirm if UX has any thoughts on showing multiple callouts if this is the case?

Comment on lines +243 to +248
if (
Object.keys(context.failures).length == 0 &&
Object.keys(context.suggestedChanges).length == 1
) {
calloutType = 'valid';
}
Copy link
Contributor

Choose a reason for hiding this comment

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

This should be ran outside of the for loop, and if true, never execute the for loop. Also, why would there be any suggested changes if the callout is valid?

Copy link
Author

Choose a reason for hiding this comment

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

This is used for the case where the suggested change is a new detector interval (a specific number and not the message that the max was reached) so at that case I just update the configurations and the callout is hence valid. I'll try to make this logic cleaner in my code

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it, yeah. Sounds like a specific case that could be outside of this loop

Comment on lines +394 to +412
let message;
for (let [key, value] of Object.entries(failures)) {
if (key === 'duplicates' && field === 'name') {
message = 'Detector name is a duplicate';
} else if (key === 'missing' && value[0] === field) {
message = 'This field is required';
} else if ((key === 'regex' && field === 'name') || (key === 'format' && field === 'name')) {
message = 'Valid characters are a-z, A-Z, 0-9, -(hyphen) and _(underscore)';
}
}
for (let [key, value] of Object.entries(suggestedChanges)) {
if (key === 'window_delay' && field === 'window_delay') {
message = 'Window delay should be at least ' + value[0] + ' minutes';
} else if (key === 'filter_query' && field === 'filter_query') {
message = value[0];
} else if (key === 'detectionIntervalMax' && field === 'detection_interval') {
message = value;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Looks like message could get overwritten here if there was multiple failures and/or suggested changes. How do we propagate all to the user? This may also need to be discussed with UX, where a callout may not be the right solution here if there's several (2+) things the user should be notified of.

return {
flyoutProps: {
'aria-labelledby': 'createDetectorFlyout',
maxWidth: 900,
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a reason for specifying the maxWidth here? Seems that the size prop should be sufficient here

const detectorFailure = (context) => ({
flyoutProps: {
'aria-labelledby': 'createDetectorFlyout',
maxWidth: 700,
Copy link
Contributor

Choose a reason for hiding this comment

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

again, I don't think maxWidth should be necessary here

Comment on lines +225 to +227
if (aggregationType == 'count') {
aggregationType = 'value_count';
}
Copy link
Contributor

Choose a reason for hiding this comment

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

what is the reason for this? Is there a discrepancy between naming conventions for the count aggregation between alerting and AD, but the rest of the aggregations match up?

Comment on lines +260 to +265
let message = '';
for (let [key, value] of Object.entries(validationResponse.failures)) {
if (key === 'others') {
message = value;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Can there be multiple failures with a key of others? Also, confused on what others means here, could you explain?

query: searchQuery,
};
const response = await httpClient.post('../api/alerting/_search', options);
let maxStamp = _.get(response, 'data.resp.aggregations.max_timefield.value');
Copy link
Contributor

Choose a reason for hiding this comment

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

What will the maxStamp be if no timestamp is found (empty data)? Will this throw an exception, or just return some default value like 0?

Copy link
Author

@amitgalitz amitgalitz Sep 2, 2020

Choose a reason for hiding this comment

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

Currently the visual graph type monitors require a timefield to be created so there will always be one while converting from a visual graph monitor. I could query the index mapping for time field for extra protection and especially when other monitor types are converted.

Copy link
Contributor

@ohltyler ohltyler Sep 2, 2020

Choose a reason for hiding this comment

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

Got it, I'm just wondering if no timestamp is found (even if the time field exists) what the query will return. Not sure if ES returns 0 or null on max aggs that are empty. Maybe there will already be issues w/ monitor creation if this is the case.

Comment on lines +488 to +498
<EuiButton
isLoading={updating}
onClick={() => this.convertToADConfigs(this.state.monitor)}
disabled={
monitor.ui_metadata.search.searchType !== 'graph' ||
monitor.ui_metadata.search.fieldName === '' ||
monitor.ui_metadata.schedule.timezone
}
>
Create Detector
</EuiButton>
Copy link
Contributor

@ohltyler ohltyler Sep 1, 2020

Choose a reason for hiding this comment

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

What will the button show if a detector is already created for this monitor? Wondering if it should be changed to a link to the corresponding detector, similar to how AD handles created corresponding monitors.

Copy link
Author

Choose a reason for hiding this comment

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

At the moment sense the monitor itself isn't directly linked with the newly made detector so the button will still show and users will have the ability to create multiple detectors from the monitor, will discuss this more with UX

Copy link
Contributor

Choose a reason for hiding this comment

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

Got it, yeah this may require some backend changes to store some information on a detector created from a monitor.

import { formikToWhereClause } from '../../../pages/CreateMonitor/containers/CreateMonitor/utils/formikToMonitor';
import { MAX_INTERVAl_LENGTH_MINUTES } from '../../../pages/MonitorDetails/containers/utils/helpers';

function toString(obj) {
Copy link
Contributor

@ylwu-amzn ylwu-amzn Sep 3, 2020

Choose a reason for hiding this comment

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

This function does multiple work: 1. return period filed of input obj 2.parse input obj to specific time format if it's number type. 3. return '-' for undefined input

Suggest to split this function into two (getPeriod and formatTime, put formatTime as some common utils) and make the function name reveal what exactly it does. Suggest use lodash get function to simplify code.

super(props);
}
render() {
const featureAttributes = this.props.featureAttributes;
Copy link
Contributor

@ylwu-amzn ylwu-amzn Sep 3, 2020

Choose a reason for hiding this comment

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

Is featureAttributes just one feature, not feature array, right? If yes, how about we make it singular, rename it as featureAttribute or feature

return (
<div>
<p>
{' '}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why add this space ?

Copy link
Author

Choose a reason for hiding this comment

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

removed, originally it helped with styling it but I changed things sense

<EuiButton
flush="right"
disabled={Object.keys(context.failures).length != 0}
onClick={() => createAndStartDetector(context)}
Copy link
Contributor

Choose a reason for hiding this comment

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

If user click "Create Detector" button , will create detector first, and start to run this detector immediately ?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, I decided since the main goal behind this feature is to help users adopt AD and ease the set up process, it would be nice to automate the start step after creation from an already existing monitor.

const detectorId = _id;
if (ok) {
try {
const response = await httpClient.post(`../api/alerting/detectors/${detectorId}/_start`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we ask user to confirm should start detector or not after creation ?

Copy link
Author

Choose a reason for hiding this comment

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

The general idea is to make the creation process as user friendly as possible so giving an option not to start the detector might confuse unexperienced users. Overall this follows the "1-click" design better and especially assuming the data is already being used for a monitor it is likely they would just want to start this. However I see the point in the added confirmation and wait for the actual start, I'll mention it with UX and add to the handoff docs.

}
for (let [key, value] of Object.entries(context.suggestedChanges)) {
if (key === 'detection_interval') {
let intervalMinutes = Math.ceil(value[0] / 60000) + 1;
Copy link
Contributor

Choose a reason for hiding this comment

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

Why add 1 more minutes as already ceil the value?

Copy link
Author

Choose a reason for hiding this comment

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

This was done to account for the time that user might take in changing configurations, and confirming everything so I felt as it was safe to add 1 more minute, I could change this however or be be more clear in my reasoning in adding this through the code

Copy link
Contributor

Choose a reason for hiding this comment

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

How to calculate the detection interval suggested change ? Detection interval should be some stable value from historical data analysis. Here the suggested value is the minimal interval which contains at least one raw data point ? If yes, we should not add 1 more minute. For example, the index has data for every minute, then the minimal suggested interval should be 1 minute, but with this implementation, we suggest user to use 2 minutes.
Should be reasonable to add 1 more minute for window delay.

</p>
<p>
{' '}
<b>Aggregation method:</b> {featureAttributes.aggregationType || ''}
Copy link
Contributor

Choose a reason for hiding this comment

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

Why don't we show "Field" and "Aggregation method" as separate columns, like "Feature name" and "State"?

Copy link
Author

Choose a reason for hiding this comment

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

This was to keep the same style displayed in the configurations page on the anomaly detection plugin since its the same format that the AD plugin follows. I will discuss this further with UX.

}
return '-';
}

Copy link
Contributor

Choose a reason for hiding this comment

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

General suggestion, this file contains too many things, suggest to refactor it to make the code structure clearer.

@tlfeng tlfeng force-pushed the master branch 3 times, most recently from ce430a5 to 09f93ce Compare February 6, 2021 08:44
Base automatically changed from master to main February 10, 2021 00:14
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants