Skip to content

feat(aci): Edit detector form #93579

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

Merged
merged 25 commits into from
Jun 17, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
bc10e7b
feat(aci): Allow submitting detector form
scttcper Jun 11, 2025
64292e9
more support for new detector
scttcper Jun 12, 2025
a2dbcc2
fix priority tests
scttcper Jun 12, 2025
04485f6
type error
scttcper Jun 12, 2025
e38e7b2
Merge branch 'master' into scttcper/submit-detector-form
scttcper Jun 12, 2025
631eddc
add DetectorPriorityLevel with numbers
scttcper Jun 13, 2025
a4a2b7f
use DataConditionType
scttcper Jun 13, 2025
41bda89
add more DataConditionType
scttcper Jun 13, 2025
31ebe5d
template, more enum
scttcper Jun 13, 2025
6fecd84
reuse types
scttcper Jun 13, 2025
96c40b8
consolidate createConditions
scttcper Jun 13, 2025
9d8a3e9
feat(aci): Submit edit detector form
scttcper Jun 14, 2025
899e7d4
fix loading state
scttcper Jun 14, 2025
57f4708
Merge branch 'master' into scttcper/submit-detector-form
scttcper Jun 16, 2025
309b45c
preserve value when switching between kinds
scttcper Jun 16, 2025
eb27bac
Merge branch 'scttcper/submit-detector-form' into scttcper/edit-detec…
scttcper Jun 16, 2025
dce2f03
Merge branch 'master' into scttcper/edit-detector-form
scttcper Jun 16, 2025
2e9f5d4
rename detector type
scttcper Jun 16, 2025
3512dea
three dot unicode
scttcper Jun 16, 2025
ac8467a
setup detector configs
scttcper Jun 16, 2025
5f38734
remove exports
scttcper Jun 16, 2025
42e5612
remove comparison type, breaking too many types
scttcper Jun 16, 2025
2ac194b
switch to parseFunction from discover
scttcper Jun 16, 2025
4b86929
set minimum priority
scttcper Jun 16, 2025
9241551
fix type error
scttcper Jun 16, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Form from 'sentry/components/forms/form';
import PriorityControl from 'sentry/components/workflowEngine/form/control/priorityControl';
import * as Storybook from 'sentry/stories';
import {space} from 'sentry/styles/space';
import {DetectorPriorityLevel} from 'sentry/types/workflowEngine/dataConditions';

export default Storybook.story('Form Controls', story => {
story('PriorityControl', () => (
Expand All @@ -16,7 +17,7 @@ export default Storybook.story('Form Controls', story => {

<Form hideFooter>
<Flex column gap={space(2)}>
<PriorityControl />
<PriorityControl minimumPriority={DetectorPriorityLevel.LOW} />
</Flex>
</Form>
</Fragment>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ describe('PriorityControl', function () {
});
render(
<Form model={formModel} hideFooter>
<PriorityControl />
<PriorityControl minimumPriority={DetectorPriorityLevel.LOW} />
</Form>
);

Expand All @@ -37,7 +37,7 @@ describe('PriorityControl', function () {
});
render(
<Form model={formModel} hideFooter>
<PriorityControl />
<PriorityControl minimumPriority={DetectorPriorityLevel.LOW} />
</Form>
);
expect(await screen.findByRole('button', {name: 'Low'})).toBeInTheDocument();
Expand All @@ -62,7 +62,7 @@ describe('PriorityControl', function () {
});
render(
<Form model={formModel} hideFooter>
<PriorityControl />
<PriorityControl minimumPriority={DetectorPriorityLevel.LOW} />
</Form>
);
const medium = await screen.findByTestId('priority-control-medium');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import styled from '@emotion/styled';
import {GroupPriorityBadge} from 'sentry/components/badge/groupPriority';
import {Flex} from 'sentry/components/container/flex';
import {CompactSelect} from 'sentry/components/core/compactSelect';
import {FieldWrapper} from 'sentry/components/forms/fieldGroup/fieldWrapper';
import NumberField from 'sentry/components/forms/fields/numberField';
import FormContext from 'sentry/components/forms/formContext';
import InteractionStateLayer from 'sentry/components/interactionStateLayer';
Expand Down Expand Up @@ -67,12 +66,10 @@ function ChangePriority() {
}

interface PriorityControlProps {
minimumPriority?: DetectorPriorityLevel;
minimumPriority: DetectorPriorityLevel;
}

export default function PriorityControl({
minimumPriority = DetectorPriorityLevel.LOW,
}: PriorityControlProps) {
export default function PriorityControl({minimumPriority}: PriorityControlProps) {
// TODO: kind type not yet available from detector types
const detectorKind = useMetricDetectorFormField(METRIC_DETECTOR_FORM_FIELDS.kind);
const initialPriorityLevel = useMetricDetectorFormField(
Expand All @@ -84,7 +81,7 @@ export default function PriorityControl({
<PrioritizeRow
left={
<Flex align="center" column>
{!detectorKind || detectorKind === 'threshold' ? (
{!detectorKind || detectorKind === 'static' ? (
<ThresholdPriority />
) : (
<ChangePriority />
Expand All @@ -97,7 +94,7 @@ export default function PriorityControl({
{priorityIsConfigurable(initialPriorityLevel, DetectorPriorityLevel.MEDIUM) && (
<PrioritizeRow
left={
<NumberField
<SmallNumberField
alignRight
inline
hideLabel
Expand All @@ -116,7 +113,7 @@ export default function PriorityControl({
{priorityIsConfigurable(initialPriorityLevel, DetectorPriorityLevel.HIGH) && (
<PrioritizeRow
left={
<NumberField
<SmallNumberField
alignRight
inline
hideLabel
Expand Down Expand Up @@ -146,13 +143,9 @@ function priorityIsConfigurable(
function PrioritizeRow({left, right}: {left: React.ReactNode; right: React.ReactNode}) {
return (
<Row>
<Cell align="center" justify="flex-end">
{left}
</Cell>
<Cell>{left}</Cell>
<IconArrow color="gray300" direction="right" />
<Cell align="center" justify="flex-start">
{right}
</Cell>
<Cell>{right}</Cell>
</Row>
);
}
Expand Down Expand Up @@ -227,13 +220,16 @@ const Row = styled('div')`
display: contents;
`;

const Cell = styled(Flex)`
const Cell = styled('div')`
display: flex;
align-items: center;
justify-content: center;
padding: ${space(1)};
`;

${FieldWrapper} {
padding: 0;
width: 5rem;
}
const SmallNumberField = styled(NumberField)`
width: 5rem;
padding: 0;
`;

const SecondaryLabel = styled('div')`
Expand Down
36 changes: 35 additions & 1 deletion static/app/types/workflowEngine/detectors.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type {DataConditionGroup} from 'sentry/types/workflowEngine/dataConditions';
import type {AlertRuleSensitivity} from 'sentry/views/alerts/rules/metric/types';

/**
* See SnubaQuerySerializer
Expand Down Expand Up @@ -60,9 +61,42 @@ type DataSource = SnubaQueryDataSource | UptimeSubscriptionDataSource;

export type DetectorType = 'error' | 'metric_issue' | 'uptime_domain_failure';

interface BaseDetectorConfig {
threshold_period: number;
}

/**
* Configuration for static/threshold-based detection
*/
interface StaticDetectorConfig extends BaseDetectorConfig {
detection_type: 'static';
}

/**
* Configuration for percentage-based change detection
*/
interface PercentDetectorConfig extends BaseDetectorConfig {
comparison_delta: number;
detection_type: 'percent';
}

/**
* Configuration for dynamic/anomaly detection
*/
interface DynamicDetectorConfig extends BaseDetectorConfig {
detection_type: 'dynamic';
seasonality?: 'auto' | 'daily' | 'weekly' | 'monthly';
sensitivity?: AlertRuleSensitivity;
}

export type DetectorConfig =
| StaticDetectorConfig
| PercentDetectorConfig
| DynamicDetectorConfig;

interface NewDetector {
conditionGroup: DataConditionGroup | null;
config: Record<string, unknown>;
config: DetectorConfig;
dataSources: DataSource[] | null;
disabled: boolean;
name: string;
Expand Down
27 changes: 27 additions & 0 deletions static/app/views/detectors/components/detectorSubtitle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import {Flex} from 'sentry/components/container/flex';
import {ProjectAvatar} from 'sentry/components/core/avatar/projectAvatar';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import useProjects from 'sentry/utils/useProjects';

interface DetectorSubtitleProps {
environment: string;
projectId: string;
}

export function DetectorSubtitle({projectId, environment}: DetectorSubtitleProps) {
const {projects} = useProjects();
const project = projects.find(p => p.id === projectId);
return (
<Flex gap={space(1)} align="center">
{project && (
<Flex gap={space(1)} align="center">
<ProjectAvatar project={project} title={project.slug} />
<span>{project.slug}</span>
</Flex>
)}
<div aria-hidden>|</div>
<div>{environment || t('All Environments')}</div>
</Flex>
);
}
18 changes: 18 additions & 0 deletions static/app/views/detectors/components/forms/fullHeightForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import styled from '@emotion/styled';

import Form from 'sentry/components/forms/form';

/**
* Extends the Form component to be full height and have a sticky footer.
*/
export const FullHeightForm = styled(Form)`
display: flex;
flex-direction: column;
flex: 1 1 0%;

& > div:first-child {
display: flex;
flex-direction: column;
flex: 1;
}
`;
23 changes: 12 additions & 11 deletions static/app/views/detectors/components/forms/metric.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,10 @@ import {IconAdd} from 'sentry/icons';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {TagCollection} from 'sentry/types/group';
import {DataConditionType} from 'sentry/types/workflowEngine/dataConditions';
import {
DataConditionType,
DetectorPriorityLevel,
} from 'sentry/types/workflowEngine/dataConditions';
import {
ALLOWED_EXPLORE_VISUALIZE_AGGREGATES,
FieldKey,
Expand Down Expand Up @@ -48,12 +51,8 @@ export function MetricDetectorForm() {

function MonitorKind() {
const options: Array<[MetricDetectorFormData['kind'], string, string]> = [
[
'threshold',
t('Threshold'),
t('Absolute-valued thresholds, for non-seasonal data.'),
],
['change', t('Change'), t('Percentage changes over defined time windows.')],
['static', t('Threshold'), t('Absolute-valued thresholds, for non-seasonal data.')],
['percent', t('Change'), t('Percentage changes over defined time windows.')],
[
'dynamic',
t('Dynamic'),
Expand All @@ -63,7 +62,7 @@ function MonitorKind() {

return (
<MonitorKindField
label={t('...and monitor for changes in the following way:')}
label={t('\u2026and monitor for changes in the following way:')}
flexibleControlStateSize
inline={false}
name={METRIC_DETECTOR_FORM_FIELDS.kind}
Expand Down Expand Up @@ -149,7 +148,9 @@ function PrioritizeSection() {
: t('Update issue priority when the following thresholds are met:')
}
>
{kind !== 'dynamic' && <PriorityControl />}
{kind !== 'dynamic' && (
<PriorityControl minimumPriority={DetectorPriorityLevel.MEDIUM} />
)}
</Section>
</Container>
);
Expand Down Expand Up @@ -193,7 +194,7 @@ function DetectSection() {
</FirstRow>
<MonitorKind />
<Flex column>
{(!kind || kind === 'threshold') && (
{(!kind || kind === 'static') && (
<Flex column>
<MutedText>{t('An issue will be created when query value is:')}</MutedText>
<Flex align="center" gap={space(1)}>
Expand Down Expand Up @@ -224,7 +225,7 @@ function DetectSection() {
</Flex>
</Flex>
)}
{kind === 'change' && (
{kind === 'percent' && (
<Flex column>
<MutedText>{t('An issue will be created when query value is:')}</MutedText>
<Flex align="center" gap={space(1)}>
Expand Down
Loading
Loading