Skip to content

Commit 7d027c5

Browse files
authored
ref(aci): group data condition choices (#92438)
updated dataConditionNodeList to use response data from the data conditions endpoint + added grouping by handler subgroup had to add special logic for the frequency conditions that have additional branching to determine whether to use count/percent frequency so that we don't list those conditions twice <img width="911" alt="Screenshot 2025-05-28 at 1 10 36 PM" src="https://github.com/user-attachments/assets/d23b8a8e-40da-4bd6-96d6-72b0cad1aa18" /> example of branching frequency condition, for reference: <img width="555" alt="Screenshot 2025-05-28 at 1 15 09 PM" src="https://github.com/user-attachments/assets/9180d78a-d59a-44dd-b067-031a1c0f1f5a" />
1 parent bf48c42 commit 7d027c5

File tree

7 files changed

+131
-50
lines changed

7 files changed

+131
-50
lines changed

static/app/types/workflowEngine/dataConditions.tsx

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,3 +65,22 @@ export interface DataConditionGroup {
6565
logicType: DataConditionGroupLogicType;
6666
actions?: Action[];
6767
}
68+
69+
export enum DataConditionHandlerGroupType {
70+
DETECTOR_TRIGGER = 'detector_trigger',
71+
WORKFLOW_TRIGGER = 'workflow_trigger',
72+
ACTION_FILTER = 'action_filter',
73+
}
74+
75+
export enum DataConditionHandlerSubgroupType {
76+
ISSUE_ATTRIBUTES = 'issue_attributes',
77+
FREQUENCY = 'frequency',
78+
EVENT_ATTRIBUTES = 'event_attributes',
79+
}
80+
81+
export interface DataConditionHandler {
82+
comparisonJsonSchema: Record<string, any>;
83+
handlerGroup: DataConditionHandlerGroupType;
84+
handlerSubgroup: DataConditionHandlerSubgroupType;
85+
type: DataConditionType;
86+
}

static/app/views/automations/components/actionFilters/constants.tsx

Lines changed: 1 addition & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,12 @@
11
import {t} from 'sentry/locale';
2-
import {
3-
DataConditionGroupLogicType,
4-
DataConditionType,
5-
} from 'sentry/types/workflowEngine/dataConditions';
2+
import {DataConditionGroupLogicType} from 'sentry/types/workflowEngine/dataConditions';
63

74
export const FILTER_MATCH_OPTIONS = [
85
{value: DataConditionGroupLogicType.ALL, label: t('all')},
96
{value: DataConditionGroupLogicType.ANY_SHORT_CIRCUIT, label: t('any')},
107
{value: DataConditionGroupLogicType.NONE, label: t('none')},
118
];
129

13-
export const FILTER_DATA_CONDITION_TYPES = [
14-
DataConditionType.AGE_COMPARISON,
15-
DataConditionType.ISSUE_OCCURRENCES,
16-
DataConditionType.ASSIGNED_TO,
17-
DataConditionType.ISSUE_PRIORITY_EQUALS,
18-
DataConditionType.ISSUE_PRIORITY_GREATER_OR_EQUAL,
19-
DataConditionType.LATEST_ADOPTED_RELEASE,
20-
DataConditionType.LATEST_RELEASE,
21-
DataConditionType.EVENT_ATTRIBUTE,
22-
DataConditionType.TAGGED_EVENT,
23-
DataConditionType.LEVEL,
24-
DataConditionType.EVENT_FREQUENCY,
25-
DataConditionType.EVENT_UNIQUE_USER_FREQUENCY,
26-
DataConditionType.PERCENT_SESSIONS,
27-
];
28-
2910
export enum MatchType {
3011
CONTAINS = 'co',
3112
ENDS_WITH = 'ew',

static/app/views/automations/components/automationBuilder.tsx

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,14 @@ import {IconAdd, IconDelete, IconMail} from 'sentry/icons';
1111
import {t, tct} from 'sentry/locale';
1212
import {space} from 'sentry/styles/space';
1313
import type {DataConditionGroup} from 'sentry/types/workflowEngine/dataConditions';
14+
import {DataConditionHandlerGroupType} from 'sentry/types/workflowEngine/dataConditions';
1415
import useApi from 'sentry/utils/useApi';
1516
import useOrganization from 'sentry/utils/useOrganization';
16-
import {
17-
FILTER_DATA_CONDITION_TYPES,
18-
FILTER_MATCH_OPTIONS,
19-
} from 'sentry/views/automations/components/actionFilters/constants';
17+
import {FILTER_MATCH_OPTIONS} from 'sentry/views/automations/components/actionFilters/constants';
2018
import ActionNodeList from 'sentry/views/automations/components/actionNodeList';
2119
import {useAutomationBuilderContext} from 'sentry/views/automations/components/automationBuilderContext';
2220
import DataConditionNodeList from 'sentry/views/automations/components/dataConditionNodeList';
23-
import {
24-
TRIGGER_DATA_CONDITION_TYPES,
25-
TRIGGER_MATCH_OPTIONS,
26-
} from 'sentry/views/automations/components/triggers/constants';
21+
import {TRIGGER_MATCH_OPTIONS} from 'sentry/views/automations/components/triggers/constants';
2722

2823
export default function AutomationBuilder() {
2924
const {state, actions} = useAutomationBuilderContext();
@@ -69,8 +64,7 @@ export default function AutomationBuilder() {
6964
</StepLead>
7065
</Step>
7166
<DataConditionNodeList
72-
// TODO: replace constant dataConditionTypes with DataConditions API response
73-
dataConditionTypes={TRIGGER_DATA_CONDITION_TYPES}
67+
handlerGroup={DataConditionHandlerGroupType.WORKFLOW_TRIGGER}
7468
placeholder={t('Select a trigger...')}
7569
conditions={state.triggers.conditions}
7670
group="triggers"
@@ -154,8 +148,7 @@ function ActionFilterBlock({actionFilter}: ActionFilterBlockProps) {
154148
/>
155149
</Flex>
156150
<DataConditionNodeList
157-
// TODO: replace constant dataConditionTypes with DataConditions API response
158-
dataConditionTypes={FILTER_DATA_CONDITION_TYPES}
151+
handlerGroup={DataConditionHandlerGroupType.ACTION_FILTER}
159152
placeholder={t('Filter by...')}
160153
group={`actionFilters.${actionFilter.id}`}
161154
conditions={actionFilter?.conditions || []}

static/app/views/automations/components/dataConditionNodeList.tsx

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,41 @@
1-
import {Fragment} from 'react';
1+
import {Fragment, useMemo} from 'react';
22
import styled from '@emotion/styled';
33

44
import {Select} from 'sentry/components/core/select';
5-
import type {
6-
DataCondition,
5+
import {t} from 'sentry/locale';
6+
import type {DataCondition} from 'sentry/types/workflowEngine/dataConditions';
7+
import {
8+
DataConditionHandlerGroupType,
9+
DataConditionHandlerSubgroupType,
710
DataConditionType,
811
} from 'sentry/types/workflowEngine/dataConditions';
912
import AutomationBuilderRow from 'sentry/views/automations/components/automationBuilderRow';
1013
import {
1114
DataConditionNodeContext,
1215
dataConditionNodesMap,
16+
frequencyTypeMapping,
1317
useDataConditionNodeContext,
1418
} from 'sentry/views/automations/components/dataConditionNodes';
19+
import {useDataConditionsQuery} from 'sentry/views/automations/hooks';
1520

1621
interface DataConditionNodeListProps {
1722
conditions: DataCondition[];
18-
dataConditionTypes: DataConditionType[];
1923
group: string;
24+
handlerGroup: DataConditionHandlerGroupType;
2025
onAddRow: (type: DataConditionType) => void;
2126
onDeleteRow: (id: string) => void;
2227
placeholder: string;
2328
updateCondition: (id: string, condition: Record<string, any>) => void;
2429
updateConditionType?: (id: string, type: DataConditionType) => void;
2530
}
2631

32+
interface Option {
33+
label: string;
34+
value: DataConditionType;
35+
}
36+
2737
export default function DataConditionNodeList({
28-
dataConditionTypes,
38+
handlerGroup,
2939
group,
3040
placeholder,
3141
conditions,
@@ -34,9 +44,67 @@ export default function DataConditionNodeList({
3444
updateCondition,
3545
updateConditionType,
3646
}: DataConditionNodeListProps) {
37-
const options = Array.from(dataConditionNodesMap.entries())
38-
.map(([value, {label}]) => ({value, label}))
39-
.filter(({value}) => dataConditionTypes.includes(value));
47+
const {data: dataConditionHandlers = []} = useDataConditionsQuery(handlerGroup);
48+
49+
const options = useMemo(() => {
50+
if (handlerGroup === DataConditionHandlerGroupType.WORKFLOW_TRIGGER) {
51+
return dataConditionHandlers.map(handler => ({
52+
value: handler.type,
53+
label: dataConditionNodesMap.get(handler.type)?.label || handler.type,
54+
}));
55+
}
56+
57+
const issueAttributeOptions: Option[] = [];
58+
const frequencyOptions: Option[] = [];
59+
const eventAttributeOptions: Option[] = [];
60+
61+
const percentageTypes = [
62+
DataConditionType.EVENT_FREQUENCY_PERCENT,
63+
DataConditionType.PERCENT_SESSIONS_PERCENT,
64+
DataConditionType.EVENT_UNIQUE_USER_FREQUENCY_PERCENT,
65+
];
66+
67+
dataConditionHandlers.forEach(handler => {
68+
if (percentageTypes.includes(handler.type)) {
69+
return; // Skip percentage types so that frequency conditions are not duplicated
70+
}
71+
72+
const conditionType = frequencyTypeMapping[handler.type] || handler.type;
73+
74+
const newDataCondition: Option = {
75+
value: conditionType,
76+
label: dataConditionNodesMap.get(handler.type)?.label || handler.type,
77+
};
78+
79+
if (handler.handlerSubgroup === DataConditionHandlerSubgroupType.EVENT_ATTRIBUTES) {
80+
eventAttributeOptions.push(newDataCondition);
81+
} else if (handler.handlerSubgroup === DataConditionHandlerSubgroupType.FREQUENCY) {
82+
frequencyOptions.push(newDataCondition);
83+
} else if (
84+
handler.handlerSubgroup === DataConditionHandlerSubgroupType.ISSUE_ATTRIBUTES
85+
) {
86+
issueAttributeOptions.push(newDataCondition);
87+
}
88+
});
89+
90+
return [
91+
{
92+
key: DataConditionHandlerSubgroupType.ISSUE_ATTRIBUTES,
93+
label: t('Filter by Issue Attributes'),
94+
options: issueAttributeOptions,
95+
},
96+
{
97+
key: DataConditionHandlerSubgroupType.FREQUENCY,
98+
label: t('Filter by Frequency'),
99+
options: frequencyOptions,
100+
},
101+
{
102+
key: DataConditionHandlerSubgroupType.EVENT_ATTRIBUTES,
103+
label: t('Filter by Event Attributes'),
104+
options: eventAttributeOptions,
105+
},
106+
];
107+
}, [dataConditionHandlers, handlerGroup]);
40108

41109
return (
42110
<Fragment>

static/app/views/automations/components/dataConditionNodes.tsx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,15 @@ export const dataConditionNodesMap = new Map<DataConditionType, DataConditionNod
189189
},
190190
],
191191
]);
192+
193+
export const frequencyTypeMapping: Partial<Record<DataConditionType, DataConditionType>> =
194+
{
195+
[DataConditionType.PERCENT_SESSIONS_COUNT]: DataConditionType.PERCENT_SESSIONS,
196+
[DataConditionType.PERCENT_SESSIONS_PERCENT]: DataConditionType.PERCENT_SESSIONS,
197+
[DataConditionType.EVENT_FREQUENCY_COUNT]: DataConditionType.EVENT_FREQUENCY,
198+
[DataConditionType.EVENT_FREQUENCY_PERCENT]: DataConditionType.EVENT_FREQUENCY,
199+
[DataConditionType.EVENT_UNIQUE_USER_FREQUENCY_COUNT]:
200+
DataConditionType.EVENT_UNIQUE_USER_FREQUENCY,
201+
[DataConditionType.EVENT_UNIQUE_USER_FREQUENCY_PERCENT]:
202+
DataConditionType.EVENT_UNIQUE_USER_FREQUENCY,
203+
};
Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,7 @@
11
import {t} from 'sentry/locale';
2-
import {
3-
DataConditionGroupLogicType,
4-
DataConditionType,
5-
} from 'sentry/types/workflowEngine/dataConditions';
2+
import {DataConditionGroupLogicType} from 'sentry/types/workflowEngine/dataConditions';
63

74
export const TRIGGER_MATCH_OPTIONS = [
85
{value: DataConditionGroupLogicType.ALL, label: t('all')},
96
{value: DataConditionGroupLogicType.ANY_SHORT_CIRCUIT, label: t('any')},
107
];
11-
12-
export const TRIGGER_DATA_CONDITION_TYPES = [
13-
DataConditionType.FIRST_SEEN_EVENT,
14-
DataConditionType.REGRESSION_EVENT,
15-
DataConditionType.REAPPEARED_EVENT,
16-
];

static/app/views/automations/hooks/index.tsx

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import type {ActionHandler} from 'sentry/types/workflowEngine/actions';
22
import type {Automation} from 'sentry/types/workflowEngine/automations';
3+
import type {
4+
DataConditionHandler,
5+
DataConditionHandlerGroupType,
6+
} from 'sentry/types/workflowEngine/dataConditions';
37
import type {ApiQueryKey} from 'sentry/utils/queryClient';
48
import {useApiQuery} from 'sentry/utils/queryClient';
59
import useOrganization from 'sentry/utils/useOrganization';
@@ -16,6 +20,19 @@ export function useAutomationsQuery(_options: UseAutomationsQueryOptions = {}) {
1620
retry: false,
1721
});
1822
}
23+
24+
export function useDataConditionsQuery(groupType: DataConditionHandlerGroupType) {
25+
const {slug} = useOrganization();
26+
27+
return useApiQuery<DataConditionHandler[]>(
28+
[`/organizations/${slug}/data-conditions/`, {query: {group: groupType}}],
29+
{
30+
staleTime: Infinity,
31+
retry: false,
32+
}
33+
);
34+
}
35+
1936
const makeAutomationsQueryKey = (orgSlug: string): ApiQueryKey => [
2037
`/organizations/${orgSlug}/workflows/`,
2138
];

0 commit comments

Comments
 (0)