-
Notifications
You must be signed in to change notification settings - Fork 0
feat(aci): Automatically generate names for new metric monitors #100
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,14 @@ | ||||||||||||||||||
| import type FormModel from 'sentry/components/forms/model'; | ||||||||||||||||||
| import type {FieldValue} from 'sentry/components/forms/types'; | ||||||||||||||||||
|
|
||||||||||||||||||
| /** | ||||||||||||||||||
| * The form values returned by form.getValue are not typed, so this is a | ||||||||||||||||||
| * convenient helper to do a type assertion while getting the value. | ||||||||||||||||||
| */ | ||||||||||||||||||
| export function getFormFieldValue<T extends FieldValue = FieldValue>( | ||||||||||||||||||
| form: FormModel, | ||||||||||||||||||
| field: string | ||||||||||||||||||
| ): T { | ||||||||||||||||||
| // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion | ||||||||||||||||||
| return form.getValue(field) as FieldValue; | ||||||||||||||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [DataTypeCheck] Type mismatch in function return type vs implementation: export function getFormFieldValue<T extends FieldValue = FieldValue>(
form: FormModel,
field: string
): T {
return form.getValue(field) as FieldValue; // ❌ Returns FieldValue, not T
}This function promises to return type Suggested Change
Suggested change
⚡ Committable suggestion Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Context for Agents |
||||||||||||||||||
| } | ||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,114 @@ | ||
| import type FormModel from 'sentry/components/forms/model'; | ||
| import {getFormFieldValue} from 'sentry/components/workflowEngine/form/getFormFieldValue'; | ||
| import {t} from 'sentry/locale'; | ||
| import {DataConditionType} from 'sentry/types/workflowEngine/dataConditions'; | ||
| import getDuration from 'sentry/utils/duration/getDuration'; | ||
| import {unreachable} from 'sentry/utils/unreachable'; | ||
| import {useSetAutomaticName} from 'sentry/views/detectors/components/forms/common/useSetAutomaticName'; | ||
| import { | ||
| METRIC_DETECTOR_FORM_FIELDS, | ||
| type MetricDetectorFormData, | ||
| } from 'sentry/views/detectors/components/forms/metric/metricFormData'; | ||
| import {getDatasetConfig} from 'sentry/views/detectors/datasetConfig/getDatasetConfig'; | ||
| import {getStaticDetectorThresholdSuffix} from 'sentry/views/detectors/utils/metricDetectorSuffix'; | ||
|
|
||
| /** | ||
| * Automatically generates a name for the metric detector form based on the values: | ||
| * - Dataset | ||
| * - Aggregate function | ||
| * - Detection type | ||
| * - Direction (above or below) | ||
| * - Threshold value | ||
| * - Interval | ||
| * | ||
| * e.g. "p95(span.duration) above 100ms compared to past 1 hour" | ||
| */ | ||
| export function useAutoMetricDetectorName() { | ||
| useSetAutomaticName((form: FormModel): string | null => { | ||
| const detectionType = getFormFieldValue<MetricDetectorFormData['detectionType']>( | ||
| form, | ||
| METRIC_DETECTOR_FORM_FIELDS.detectionType | ||
| ); | ||
| const aggregate = getFormFieldValue<MetricDetectorFormData['aggregateFunction']>( | ||
| form, | ||
| METRIC_DETECTOR_FORM_FIELDS.aggregateFunction | ||
| ); | ||
| const interval = getFormFieldValue<MetricDetectorFormData['interval']>( | ||
| form, | ||
| METRIC_DETECTOR_FORM_FIELDS.interval | ||
| ); | ||
| const dataset = getFormFieldValue<MetricDetectorFormData['dataset']>( | ||
| form, | ||
| METRIC_DETECTOR_FORM_FIELDS.dataset | ||
| ); | ||
| const datasetConfig = getDatasetConfig(dataset); | ||
|
|
||
| if (!aggregate || !interval || !detectionType || !dataset) { | ||
| return null; | ||
| } | ||
|
|
||
| switch (detectionType) { | ||
| case 'static': { | ||
| const conditionType = getFormFieldValue<MetricDetectorFormData['conditionType']>( | ||
| form, | ||
| METRIC_DETECTOR_FORM_FIELDS.conditionType | ||
| ); | ||
| const conditionValue = getFormFieldValue< | ||
| MetricDetectorFormData['conditionValue'] | ||
| >(form, METRIC_DETECTOR_FORM_FIELDS.conditionValue); | ||
|
|
||
| if (!conditionType || !conditionValue) { | ||
| return null; | ||
| } | ||
|
|
||
| const suffix = getStaticDetectorThresholdSuffix(aggregate); | ||
| const direction = | ||
| conditionType === DataConditionType.GREATER ? t('above') : t('below'); | ||
|
|
||
| return t( | ||
| '%(aggregate)s %(aboveOrBelow)s %(value)s%(unit)s over past %(interval)s', | ||
| { | ||
| aggregate: datasetConfig.formatAggregateForTitle?.(aggregate) ?? aggregate, | ||
| aboveOrBelow: direction, | ||
| value: conditionValue, | ||
| unit: suffix, | ||
| interval: getDuration(interval), | ||
| } | ||
| ); | ||
| } | ||
| case 'percent': { | ||
| const conditionType = getFormFieldValue<MetricDetectorFormData['conditionType']>( | ||
| form, | ||
| METRIC_DETECTOR_FORM_FIELDS.conditionType | ||
| ); | ||
| const conditionValue = getFormFieldValue< | ||
| MetricDetectorFormData['conditionValue'] | ||
| >(form, METRIC_DETECTOR_FORM_FIELDS.conditionValue); | ||
|
|
||
| if (!conditionType || !conditionValue) { | ||
| return null; | ||
| } | ||
|
|
||
| const direction = | ||
| conditionType === DataConditionType.GREATER ? t('higher') : t('lower'); | ||
|
|
||
| return t( | ||
| '%(aggregate)s %(aboveOrBelow)s by %(value)s%% compared to past %(interval)s', | ||
| { | ||
| aggregate: datasetConfig.formatAggregateForTitle?.(aggregate) ?? aggregate, | ||
| aboveOrBelow: direction, | ||
| value: conditionValue, | ||
| interval: getDuration(interval), | ||
| } | ||
| ); | ||
| } | ||
| case 'dynamic': | ||
| return t('%(aggregate)s is anomalous', { | ||
| aggregate: datasetConfig.formatAggregateForTitle?.(aggregate) ?? aggregate, | ||
| }); | ||
| default: | ||
| unreachable(detectionType); | ||
| return null; | ||
| } | ||
| }); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new
getFormFieldValuehelper is declared to return the caller‑specified generic typeT, but the implementation always casts toFieldValue. BecauseFieldValueis only the constraint forT, this expression is not assignable toTand TypeScript will fail to compile withType 'FieldValue' is not assignable to type 'T'. To preserve the generic typing and satisfy the compiler the value should be asserted toTinstead ofFieldValue.Useful? React with 👍 / 👎.