From f3266b8bacb0502af3ebc880c688df480b6032e0 Mon Sep 17 00:00:00 2001 From: Jackie Han Date: Thu, 8 Jun 2023 09:48:25 -0700 Subject: [PATCH] Fix various bugs (#504) * fix bug bash bugs Signed-off-by: Jackie Han * bug fix Signed-off-by: Jackie Han * yarn prettier Signed-off-by: Jackie Han * bug fix Signed-off-by: Jackie Han * bug fixes Signed-off-by: Jackie Han * clean up code Signed-off-by: Jackie Han * removed unused snapshot Signed-off-by: Jackie Han --------- Signed-off-by: Jackie Han Signed-off-by: Jackie Han --- .../AddAnomalyDetector.tsx | 75 +++++++++++++++---- .../CreateAnomalyDetector/helpers.tsx | 57 ++++++++++---- public/expressions/constants.ts | 6 ++ .../FeatureAccordion/FeatureAccordion.tsx | 15 +++- .../components/FeatureAccordion/styles.scss | 3 + public/utils/contextMenu/getActions.tsx | 4 +- 6 files changed, 128 insertions(+), 32 deletions(-) create mode 100644 public/pages/ConfigureModel/components/FeatureAccordion/styles.scss diff --git a/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/AddAnomalyDetector.tsx b/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/AddAnomalyDetector.tsx index 9e490801..766cee4e 100644 --- a/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/AddAnomalyDetector.tsx +++ b/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/AddAnomalyDetector.tsx @@ -70,6 +70,7 @@ import { import { focusOnFirstWrongFeature, initialFeatureValue, + validateFeatures, } from '../../../../public/pages/ConfigureModel/utils/helpers'; import { getIndices, @@ -95,6 +96,8 @@ import { ORIGIN_PLUGIN_VIS_LAYER, OVERLAY_ANOMALIES, VIS_LAYER_PLUGIN_TYPE, + PLUGIN_AUGMENTATION_ENABLE_SETTING, + PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING, } from '../../../../public/expressions/constants'; import { formikToDetectorName, visFeatureListToFormik } from './helpers'; import { AssociateExisting } from './AssociateExisting'; @@ -157,11 +160,55 @@ function AddAnomalyDetector({ const notifications = getNotifications(); const handleValidationAndSubmit = (formikProps) => { - if (!isEmpty(formikProps.errors)) { - focusOnFirstWrongFeature(formikProps.errors, formikProps.setFieldTouched); - notifications.toasts.addDanger('One or more input fields is invalid'); + if (formikProps.values.featureList.length !== 0) { + formikProps.setFieldTouched('featureList', true); + formikProps.validateForm().then(async (errors) => { + if (!isEmpty(errors)) { + focusOnFirstWrongFeature(errors, formikProps.setFieldTouched); + notifications.toasts.addDanger( + 'One or more input fields is invalid.' + ); + } else { + const isAugmentationEnabled = uiSettings.get( + PLUGIN_AUGMENTATION_ENABLE_SETTING + ); + if (!isAugmentationEnabled) { + notifications.toasts.addDanger( + 'Visualization augmentation is disabled, please enable visualization:enablePluginAugmentation.' + ); + } else { + const maxAssociatedCount = uiSettings.get( + PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING + ); + await savedObjectLoader.findAll().then(async (resp) => { + if (resp !== undefined) { + const savedAugmentObjects = get(resp, 'hits', []); + // gets all the saved object for this visualization + const savedObjectsForThisVisualization = + savedAugmentObjects.filter( + (savedObj) => + get(savedObj, 'visId', '') === embeddable.vis.id + ); + if ( + maxAssociatedCount <= savedObjectsForThisVisualization.length + ) { + notifications.toasts.addDanger( + `Cannot create the detector and associate it to the visualization due to the limit of the max + amount of associated plugin resources (${maxAssociatedCount}) with + ${savedObjectsForThisVisualization.length} associated to the visualization` + ); + } else { + handleSubmit(formikProps); + } + } + }); + } + } + }); } else { - handleSubmit(formikProps); + notifications.toasts.addDanger( + 'One or more features are required.' + ); } }; @@ -203,7 +250,7 @@ function AddAnomalyDetector({ formikProps.setSubmitting(true); try { const detectorToCreate = formikToDetector(formikProps.values); - dispatch(createDetector(detectorToCreate)) + await dispatch(createDetector(detectorToCreate)) .then(async (response) => { dispatch(startDetector(response.response.id)) .then((startDetectorResponse) => {}) @@ -222,7 +269,7 @@ function AddAnomalyDetector({ const augmentVisSavedObjectToCreate: ISavedAugmentVis = getAugmentVisSavedObject(detectorId); - createAugmentVisSavedObject( + await createAugmentVisSavedObject( augmentVisSavedObjectToCreate, savedObjectLoader, uiSettings @@ -408,7 +455,7 @@ function AddAnomalyDetector({ windowDelay: delayValue, shingleSize: 8, filterQuery: { match_all: {} }, - description: '', + description: 'Created based on ' + embeddable.vis.title, resultIndex: undefined, filters: [], featureList: visFeatureListToFormik( @@ -426,6 +473,7 @@ function AddAnomalyDetector({ initialValues={initialDetectorValue} onSubmit={handleSubmit} validateOnChange={true} + validate={validateFeatures} > {(formikProps) => ( <> @@ -532,8 +580,8 @@ function AddAnomalyDetector({ subTitle={

- Detector interval: {intervalValue} minutes; Window - delay: {delayValue} minutes + Detector interval: {intervalValue} minute(s); Window + delay: {delayValue} minute(s)

} @@ -584,7 +632,7 @@ function AddAnomalyDetector({ -

minutes

+

minute(s)

@@ -618,7 +666,7 @@ function AddAnomalyDetector({ -

minutes

+

minute(s)

@@ -788,8 +836,6 @@ function AddAnomalyDetector({ isOpen={accordionsOpen.modelFeatures} onToggle={() => onAccordionToggle('modelFeatures')} > - - {({ push, @@ -811,6 +857,8 @@ function AddAnomalyDetector({ /> ) )} + + + )} diff --git a/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/helpers.tsx b/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/helpers.tsx index 5c69015c..4a990b67 100644 --- a/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/helpers.tsx +++ b/public/components/FeatureAnywhereContextMenu/CreateAnomalyDetector/helpers.tsx @@ -1,9 +1,12 @@ -import { dispatch } from 'd3'; -import { matchDetector } from 'public/redux/reducers/ad'; -import { validateDetectorName } from 'public/utils/utils'; +/* + * Copyright OpenSearch Contributors + * SPDX-License-Identifier: Apache-2.0 + */ + import { FEATURE_TYPE } from '../../../../public/models/interfaces'; import { FeaturesFormikValues } from '../../../../public/pages/ConfigureModel/models/interfaces'; -import { find, get, isEmpty, snakeCase } from 'lodash'; +import { find, snakeCase } from 'lodash'; +import { AGGREGATION_TYPES } from '../../../../public/pages/ConfigureModel/utils/constants'; export function visFeatureListToFormik( featureList, @@ -17,7 +20,7 @@ export function visFeatureListToFormik( featureType: FEATURE_TYPE.SIMPLE, importance: 1, newFeature: false, - aggregationBy: 'sum', + aggregationBy: visAggregationTypeToFormik(feature), aggregationOf: visAggregationToFormik(feature), aggregationQuery: JSON.stringify( visAggregationQueryToFormik(feature, seriesParams) @@ -44,18 +47,40 @@ const getFeatureNameFromVisParams = (id, seriesParams) => { }; function visAggregationToFormik(value) { - return [ - { - label: value.params.field.name, - type: 'number', - }, - ]; + if (Object.values(value.params).length !== 0) { + return [ + { + label: value.params?.field?.name, + type: value.type, + }, + ]; + } + // for count type of vis, there's no field name in the embeddable-vis schema + return []; } function visAggregationQueryToFormik(value, seriesParams) { - return { - [snakeCase(getFeatureNameFromVisParams(value.id, seriesParams))]: { - sum: { field: value.params.field.name }, - }, - }; + if (Object.values(value.params).length !== 0) { + return { + [snakeCase(getFeatureNameFromVisParams(value.id, seriesParams))]: { + [visAggregationTypeToFormik(value)]: { + field: value.params?.field?.name, + }, + }, + }; + } + // for count type of vis, there's no field name in the embeddable-vis schema + // return '' as the custom expression query + return ''; +} + +function visAggregationTypeToFormik(feature) { + const aggType = feature.__type.name; + if (AGGREGATION_TYPES.some((type) => type.value === aggType)) { + return aggType; + } + if (aggType === 'count') { + return 'value_count'; + } + return 'sum'; } diff --git a/public/expressions/constants.ts b/public/expressions/constants.ts index 066795c0..71d696bc 100644 --- a/public/expressions/constants.ts +++ b/public/expressions/constants.ts @@ -14,6 +14,12 @@ export const OVERLAY_ANOMALIES = 'overlay_anomalies'; export const PLUGIN_EVENT_TYPE = 'Anomalies'; +export const PLUGIN_AUGMENTATION_ENABLE_SETTING = + 'visualization:enablePluginAugmentation'; + +export const PLUGIN_AUGMENTATION_MAX_OBJECTS_SETTING = + 'visualization:enablePluginAugmentation.maxPluginObjects'; + export const DETECTOR_HAS_BEEN_DELETED = 'detector has been deleted'; export const START_OR_END_TIME_INVALID_ERROR = 'start or end time invalid'; diff --git a/public/pages/ConfigureModel/components/FeatureAccordion/FeatureAccordion.tsx b/public/pages/ConfigureModel/components/FeatureAccordion/FeatureAccordion.tsx index f1805858..a819ed8f 100644 --- a/public/pages/ConfigureModel/components/FeatureAccordion/FeatureAccordion.tsx +++ b/public/pages/ConfigureModel/components/FeatureAccordion/FeatureAccordion.tsx @@ -22,6 +22,7 @@ import { EuiCheckbox, EuiButtonIcon, } from '@elastic/eui'; +import './styles.scss'; import { Field, FieldProps } from 'formik'; import { required, @@ -80,6 +81,18 @@ export const FeatureAccordion = (props: FeatureAccordionProps) => { }; const featureButtonContent = (feature: any, index: number) => { + if (props.displayMode === 'flyout') { + return ( +
+ +
+ {feature.featureName ? feature.featureName : 'Add feature'} +
+
+ {showSubtitle ? showFeatureDescription(feature) : null} +
+ ); + } return (
@@ -125,7 +138,7 @@ export const FeatureAccordion = (props: FeatureAccordionProps) => { buttonClassName={ props.index === 0 ? 'euiAccordionForm__noTopPaddingButton' - : 'euiAccordionForm__button' + : 'euiFormAccordion_button' } className="euiAccordion__noTopBorder" paddingSize="l" diff --git a/public/pages/ConfigureModel/components/FeatureAccordion/styles.scss b/public/pages/ConfigureModel/components/FeatureAccordion/styles.scss new file mode 100644 index 00000000..5d819b8a --- /dev/null +++ b/public/pages/ConfigureModel/components/FeatureAccordion/styles.scss @@ -0,0 +1,3 @@ +.euiFormAccordion_button { + padding: 20px 16px 0 0; +} diff --git a/public/utils/contextMenu/getActions.tsx b/public/utils/contextMenu/getActions.tsx index e10e61b2..26bf8f06 100644 --- a/public/utils/contextMenu/getActions.tsx +++ b/public/utils/contextMenu/getActions.tsx @@ -21,7 +21,7 @@ import { FLYOUT_MODES } from '../../../public/components/FeatureAnywhereContextM const grouping: Action['grouping'] = [ { id: 'ad-dashboard-context-menu', - getDisplayName: () => 'Anomaly Detector', + getDisplayName: () => 'Anomaly Detection', getIconType: () => APM_TRACE, }, ]; @@ -54,7 +54,7 @@ export const getActions = () => { title: i18n.translate( 'dashboard.actions.adMenuItem.createAnomalyDetector.displayName', { - defaultMessage: 'Create anomaly detector', + defaultMessage: 'Add anomaly detector', } ), icon: 'plusInCircle' as EuiIconType,