Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion .github/workflows/remote-integ-tests-workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ env:
CI: 1
# avoid warnings like "tput: No value for $TERM and no -T specified"
TERM: xterm
OPENSEARCH_DASHBOARDS_VERSION: 'main'
OPENSEARCH_DASHBOARDS_VERSION: '3.2'
OPENSEARCH_VERSION: '3.2.0'
OPENSEARCH_PLUGIN_VERSION: '3.2.0.0'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ import {
} from '../../../../../../../../src/plugins/vis_augmenter/public';
import { stateToColorMap } from '../../../../../pages/utils/constants';
import {
BASE_DOCS_LINK,
AD_DOCS_LINK,
PLUGIN_NAME,
} from '../../../../../../public/utils/constants';
import { renderTime } from '../../../../../../public/pages/DetectorsList/utils/tableUtils';
Expand Down Expand Up @@ -211,7 +211,7 @@ export function AssociateExisting(
View existing anomaly detectors across your system and add the
detector(s) to a dashboard and visualization.{' '}
<a
href={`${BASE_DOCS_LINK}/ad`}
href={`${AD_DOCS_LINK}`}
target="_blank"
style={{ display: 'inline-block' }}
>
Expand Down
4 changes: 3 additions & 1 deletion public/models/interfaces.ts
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,9 @@ export type Detector = {
flattenCustomResultIndex?: boolean;
filterQuery: { [key: string]: any };
featureAttributes: FeatureAttributes[];
windowDelay: { period: Schedule };
windowDelay?: { period: Schedule };
frequency?: { period: Schedule };
history?: number;
detectionInterval: { period: Schedule };
shingleSize: number;
uiMetadata: UiMetaData;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import React from 'react';
import { EuiIcon } from '@elastic/eui';
import { Monitor } from '../../../../models/interfaces';
import { AlertsButton } from '../AlertsButton/AlertsButton';
import { BASE_DOCS_LINK } from '../../../..//utils/constants';
import { ALERTING_DOCS_LINK } from '../../../..//utils/constants';

type AlertsFlyoutProps = {
detectorId: string;
Expand Down Expand Up @@ -84,7 +84,7 @@ export const AlertsFlyout = (props: AlertsFlyoutProps) => {
<EuiText>
<p className="alerts_flyout_p">
Anomaly detector alerts are powered by the
<EuiLink href={`${BASE_DOCS_LINK}/alerting`}>
<EuiLink href={`${ALERTING_DOCS_LINK}`}>
{' '}
Alerting plugin
</EuiLink>
Expand All @@ -103,7 +103,7 @@ export const AlertsFlyout = (props: AlertsFlyoutProps) => {
<EuiFlexGroup alignItems="center" justifyContent="flexEnd">
<EuiFlexItem grow={true}>
<EuiSmallButton
href={`${BASE_DOCS_LINK}/alerting`}
href={`${ALERTING_DOCS_LINK}`}
target="_blank"
data-test-subj="setUpAlerts"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import { Field, FieldProps, FormikProps, useFormikContext } from 'formik';
import React, { useEffect, useState } from 'react';
import ContentPanel from '../../../../components/ContentPanel/ContentPanel';
import { CUSTOM_FORECASTER_RESULT_INDEX_PREFIX } from '../../../../../server/utils/constants';
import { BASE_DOCS_LINK, FORECASTER_DOCS_LINK } from '../../../../utils/constants';
import { FORECASTER_DOCS_LINK } from '../../../../utils/constants';
import {
isInvalid,
getError,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import {
import { Field, FieldProps, FieldArray } from 'formik';
import React, { useEffect, useState } from 'react';
import ContentPanel from '../../../../components/ContentPanel/ContentPanel';
import { BASE_DOCS_LINK } from '../../../../utils/constants';
import { AD_DOCS_LINK } from '../../../../utils/constants';
import {
isInvalid,
getError,
Expand Down Expand Up @@ -87,7 +87,7 @@ export function AdvancedSettings(props: AdvancedSettingsProps) {
increase recall but also false positives. Larger values
might be useful for ignoring noise in a signal.`,
]}
hintLink={`${BASE_DOCS_LINK}/ad`}
hintLink={`${AD_DOCS_LINK}`}
isInvalid={isInvalid(field.name, form)}
error={getError(field.name, form)}
>
Expand Down Expand Up @@ -133,7 +133,7 @@ export function AdvancedSettings(props: AdvancedSettingsProps) {
<FormattedFormRow
title="Sparse data handling"
hint={[`Choose how to handle missing data points.`]}
hintLink={`${BASE_DOCS_LINK}/ad`}
hintLink={`${AD_DOCS_LINK}`}
isInvalid={isInvalid(field.name, form)}
error={getError(field.name, form)}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import {
} from '@elastic/eui';
import { Field, FieldProps, FormikProps } from 'formik';
import { get, isEmpty } from 'lodash';
import { BASE_DOCS_LINK } from '../../../../utils/constants';
import { AD_DOCS_LINK } from '../../../../utils/constants';
import React, { useState, useEffect } from 'react';
import ContentPanel from '../../../../components/ContentPanel/ContentPanel';
import {
Expand Down Expand Up @@ -75,7 +75,7 @@ export function CategoryField(props: CategoryFieldProps) {
>
Split a single time series into multiple time series based on
categorical fields. You can select up to 2.{' '}
<EuiLink href={`${BASE_DOCS_LINK}/ad`} target="_blank">
<EuiLink href={`${AD_DOCS_LINK}`} target="_blank">
Learn more
</EuiLink>
</EuiText>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ exports[`<CategoryField /> spec renders the component when disabled 1`] = `

<a
class="euiLink euiLink--primary"
href="https://opensearch.org/docs/monitoring-plugins/ad"
href="https://opensearch.org/docs/latest/observing-your-data/ad/index/"
rel="noopener noreferrer"
target="_blank"
>
Expand Down Expand Up @@ -174,7 +174,7 @@ exports[`<CategoryField /> spec renders the component when enabled 1`] = `

<a
class="euiLink euiLink--primary"
href="https://opensearch.org/docs/monitoring-plugins/ad"
href="https://opensearch.org/docs/latest/observing-your-data/ad/index/"
rel="noopener noreferrer"
target="_blank"
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import React, { Fragment, useEffect } from 'react';
import ContentPanel from '../../../../components/ContentPanel/ContentPanel';
import { Detector } from '../../../../models/interfaces';
import { initialFeatureValue } from '../../utils/helpers';
import { MAX_FEATURE_NUM, BASE_DOCS_LINK } from '../../../../utils/constants';
import { MAX_FEATURE_NUM, AD_DOCS_LINK } from '../../../../utils/constants';
import { FeatureAccordion } from '../FeatureAccordion';

interface FeaturesProps {
Expand Down Expand Up @@ -54,7 +54,7 @@ export function Features(props: FeaturesProps) {
>
A feature is the field in your index that you use to check for
anomalies. You can add up to 5 features.{' '}
<EuiLink href={`${BASE_DOCS_LINK}/ad`} target="_blank">
<EuiLink href={`${AD_DOCS_LINK}`} target="_blank">
Learn more
</EuiLink>
</EuiText>
Expand Down
215 changes: 215 additions & 0 deletions public/pages/ConfigureModel/components/Settings/Settings.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
/*
* SPDX-License-Identifier: Apache-2.0
*
* The OpenSearch Contributors require contributions made to
* this file be licensed under the Apache-2.0 license or a
* compatible open source license.
*
* Modifications Copyright OpenSearch Contributors. See
* GitHub history for details.
*/

import {
EuiCompressedFieldNumber,
EuiFlexGroup,
EuiFlexItem,
EuiText,
} from '@elastic/eui';
import { Field, FieldProps, useFormikContext } from 'formik';
import React, { Fragment } from 'react';
import {
isInvalid,
getError,
validatePositiveInteger,
validateMultipleOf,
validateEmptyOrPositiveInteger,
validateEmptyOrNonNegativeInteger,
} from '../../../../utils/utils';
import { AD_DOCS_LINK } from '../../../../utils/constants';
import { FormattedFormRow } from '../../../../components/FormattedFormRow/FormattedFormRow';

export const Settings = () => {
const formik = useFormikContext();
const numberFieldWidth = 140;

// Custom validation function that has access to form values
const validateFrequency = (value: any) => {
return validateMultipleOf(value, formik.values.interval);
};

return (
<Fragment>
<Field name="interval" validate={validatePositiveInteger}>
{({ field, form }: FieldProps) => {
// Keep frequency in sync when the interval changes (only when they started equal).
// This reduces UX friction for users who don't care to tweak frequency while editing interval.
const handleIntervalChange = (
event: React.ChangeEvent<HTMLInputElement>
) => {
const raw = event.target.value;
const nextInterval = raw === '' ? '' : Number(raw);

// remember the values before we mutate anything
const prevInterval = field.value;
const prevFrequency = form.values.frequency;

// push the new interval into Formik
form.setFieldValue(field.name, nextInterval);

// if frequency was just mirroring interval, keep it in lockstep
const prevIntervalNumber = Number(prevInterval);
const prevFrequencyNumber = Number(prevFrequency);
const wasSynced =
!Number.isNaN(prevIntervalNumber) &&
prevIntervalNumber === prevFrequencyNumber;

if (wasSynced) {
form.setFieldValue('frequency', nextInterval);
}
};

return (
<EuiFlexGroup>
<EuiFlexItem style={{ maxWidth: '70%' }}>
<FormattedFormRow
fullWidth
title="Interval"
hint={[
`Interval sets the time window for summarizing and modeling data (e.g., 5 min to 1 hr), where too small creates noise, higher cost, and overreaction to fluctuations, while too large smooths out anomalies and delays detection.`,
]}
hintLink={`${AD_DOCS_LINK}`}
isInvalid={isInvalid(field.name, form)}
error={getError(field.name, form)}
>
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiCompressedFieldNumber
name="detectionInterval"
id="detectionInterval"
placeholder="Interval"
data-test-subj="detectionInterval"
min={1}
style={{ width: numberFieldWidth }}
{...field}
onChange={handleIntervalChange}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiText>
<p className="minutes">minutes</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</FormattedFormRow>
</EuiFlexItem>
</EuiFlexGroup>
);
}}
</Field>

<Field name="frequency" validate={validateFrequency}>
{({ field, form }: FieldProps) => (
<EuiFlexGroup style={{ marginTop: '16px' }}>
<EuiFlexItem style={{ maxWidth: '70%' }}>
<FormattedFormRow
fullWidth
title="Frequency"
hint={[
`Frequency sets how often the detector queries and scores data—i.e., how often alerts may fire. It must be a multiple of the interval and defaults to the same value. Choosing a longer frequency than the interval can improve efficiency by batching short intervals when ultra-fast alerting isn’t required (e.g., monitoring daily counts of anomalies across detectors) and is also useful for infrequent or batch log ingestion where data arrives irregularly or in bulk (e.g., a single daily ingestion spanning the entire day).`,
]}
hintLink={`${AD_DOCS_LINK}`}
isInvalid={isInvalid(field.name, form)}
error={getError(field.name, form)}
>
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiCompressedFieldNumber
name="frequency"
id="frequency"
placeholder="Frequency"
data-test-subj="frequency"
min={1}
style={{ width: numberFieldWidth }}
{...field}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiText>
<p className="minutes">minutes</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</FormattedFormRow>
</EuiFlexItem>
</EuiFlexGroup>
)}
</Field>

<Field name="windowDelay" validate={validateEmptyOrNonNegativeInteger}>
{({ field, form }: FieldProps) => (
<FormattedFormRow
fullWidth
title="Window delay"
hint="Specify a window of delay for a detector to fetch data, if you need to account for extra processing time."
hintLink={`${AD_DOCS_LINK}`}
isInvalid={isInvalid(field.name, form)}
error={getError(field.name, form)}
style={{ marginTop: '16px' }}
>
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiCompressedFieldNumber
name="windowDelay"
id="windowDelay"
placeholder="Window delay"
data-test-subj="windowDelay"
style={{ width: numberFieldWidth }}
{...field}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiText>
<p className="minutes">minutes</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</FormattedFormRow>
)}
</Field>

<Field name="history" validate={validateEmptyOrPositiveInteger}>
{({ field, form }: FieldProps) => (
<FormattedFormRow
fullWidth
title="History"
hint="How far back the model looks for training data. This determines the amount of historical data used to train the anomaly detection model. Minimum history is 40 intervals, maximum is 10,000 intervals, and the default is 40."
hintLink={`${AD_DOCS_LINK}`}
isInvalid={isInvalid(field.name, form)}
error={getError(field.name, form)}
style={{ marginTop: '16px' }}
>
<EuiFlexGroup gutterSize="s" alignItems="center">
<EuiFlexItem grow={false}>
<EuiCompressedFieldNumber
name="history"
id="history"
placeholder="History"
data-test-subj="history"
min={40}
max={10000}
style={{ width: numberFieldWidth }}
{...field}
/>
</EuiFlexItem>
<EuiFlexItem>
<EuiText>
<p className="minutes">intervals</p>
</EuiText>
</EuiFlexItem>
</EuiFlexGroup>
</FormattedFormRow>
)}
</Field>
</Fragment>
);
};
Loading
Loading