Skip to content

Commit c06c34e

Browse files
ref: fix typing for rules.actions.integrations.base (#84785)
rather than specifying form_cls / get_form_instance separately -- just define get_form_instance <!-- Describe your PR here. -->
1 parent 218f128 commit c06c34e

File tree

23 files changed

+58
-56
lines changed

23 files changed

+58
-56
lines changed

pyproject.toml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,6 @@ module = [
215215
"sentry.plugins.config",
216216
"sentry.receivers.releases",
217217
"sentry.release_health.metrics_sessions_v2",
218-
"sentry.rules.actions.integrations.base",
219218
"sentry.rules.actions.integrations.create_ticket.form",
220219
"sentry.rules.actions.integrations.create_ticket.utils",
221220
"sentry.rules.history.preview",

src/sentry/api/serializers/rest_framework/rule.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,10 +59,9 @@ def to_internal_value(self, data):
5959
node.self_validate()
6060
return data
6161

62-
if not node.form_cls:
63-
return data
64-
6562
form = node.get_form_instance()
63+
if not form:
64+
return data
6665

6766
if not form.is_valid():
6867
# XXX(epurkhiser): Very hacky, but we really just want validation

src/sentry/integrations/discord/actions/issue_alert/notification.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
class DiscordNotifyServiceAction(IntegrationEventAction):
2222
id = "sentry.integrations.discord.notify_action.DiscordNotifyServiceAction"
23-
form_cls = DiscordNotifyServiceForm
2423
label = "Send a notification to the {server} Discord server in the channel with ID or URL: {channel_id} and show tags {tags} in the notification."
2524
prompt = "Send a Discord notification"
2625
provider = "discord"
@@ -95,5 +94,5 @@ def render_label(self) -> str:
9594
def get_tags_list(self) -> Sequence[str]:
9695
return [s.strip() for s in self.get_option("tags", "").split(",")]
9796

98-
def get_form_instance(self) -> Any:
99-
return self.form_cls(self.data, integrations=self.get_integrations())
97+
def get_form_instance(self) -> DiscordNotifyServiceForm:
98+
return DiscordNotifyServiceForm(self.data, integrations=self.get_integrations())

src/sentry/integrations/jira/actions/create_ticket.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ class JiraCreateTicketAction(TicketEventAction):
1414
ticket_type = "a Jira issue"
1515
link = "https://docs.sentry.io/product/integrations/issue-tracking/jira/#issue-sync"
1616
provider = "jira"
17-
form_cls = JiraNotifyServiceForm
1817

1918
def __init__(self, *args: Any, **kwargs: Any) -> None:
2019
super().__init__(*args, **kwargs)
@@ -32,3 +31,6 @@ def generate_footer(self, rule_url: str) -> str:
3231
def translate_integration(self, integration: RpcIntegration) -> str:
3332
name = integration.metadata.get("domain_name", integration.name)
3433
return name.replace(".atlassian.net", "")
34+
35+
def get_form_instance(self) -> JiraNotifyServiceForm:
36+
return JiraNotifyServiceForm(self.data, integrations=self.get_integrations())

src/sentry/integrations/jira_server/actions/create_ticket.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ class JiraServerCreateTicketAction(TicketEventAction):
1414
ticket_type = "a Jira Server issue"
1515
link = "https://docs.sentry.io/product/integrations/issue-tracking/jira/#issue-sync"
1616
provider = "jira_server"
17-
form_cls = JiraServerNotifyServiceForm
1817

1918
def __init__(self, *args: Any, **kwargs: Any) -> None:
2019
super().__init__(*args, **kwargs)
@@ -31,3 +30,6 @@ def generate_footer(self, rule_url: str) -> str:
3130

3231
def translate_integration(self, integration: RpcIntegration) -> str:
3332
return integration.metadata.get("domain_name", integration.name)
33+
34+
def get_form_instance(self) -> JiraServerNotifyServiceForm:
35+
return JiraServerNotifyServiceForm(self.data, integrations=self.get_integrations())

src/sentry/integrations/msteams/actions/notification.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@
1919

2020
class MsTeamsNotifyServiceAction(IntegrationEventAction):
2121
id = "sentry.integrations.msteams.notify_action.MsTeamsNotifyServiceAction"
22-
form_cls = MsTeamsNotifyServiceForm
2322
label = "Send a notification to the {team} Team to {channel}"
2423
prompt = "Send a Microsoft Teams notification"
2524
provider = "msteams"
@@ -78,8 +77,8 @@ def render_label(self):
7877
team=self.get_integration_name(), channel=self.get_option("channel")
7978
)
8079

81-
def get_form_instance(self):
82-
return self.form_cls(
80+
def get_form_instance(self) -> MsTeamsNotifyServiceForm:
81+
return MsTeamsNotifyServiceForm(
8382
self.data, integrations=self.get_integrations(), channel_transformer=self.get_channel_id
8483
)
8584

src/sentry/integrations/opsgenie/actions/notification.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
class OpsgenieNotifyTeamAction(IntegrationEventAction):
1919
id = "sentry.integrations.opsgenie.notify_action.OpsgenieNotifyTeamAction"
20-
form_cls = OpsgenieNotifyTeamForm
2120
label = (
2221
"Send a notification to Opsgenie account {account} and team {team} with {priority} priority"
2322
)
@@ -127,8 +126,8 @@ def render_label(self) -> str:
127126
account=self.get_integration_name(), team=team_name, priority=priority
128127
)
129128

130-
def get_form_instance(self):
131-
return self.form_cls(
129+
def get_form_instance(self) -> OpsgenieNotifyTeamForm:
130+
return OpsgenieNotifyTeamForm(
132131
self.data,
133132
org_id=self.project.organization_id,
134133
integrations=self.get_integrations(),

src/sentry/integrations/pagerduty/actions/notification.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
class PagerDutyNotifyServiceAction(IntegrationEventAction):
1818
id = "sentry.integrations.pagerduty.notify_action.PagerDutyNotifyServiceAction"
19-
form_cls = PagerDutyNotifyServiceForm
2019
label = "Send a notification to PagerDuty account {account} and service {service} with {severity} severity"
2120
prompt = "Send a PagerDuty notification"
2221
provider = "pagerduty"
@@ -141,8 +140,8 @@ def render_label(self):
141140
account=self.get_integration_name(), service=service_name, severity=severity
142141
)
143142

144-
def get_form_instance(self):
145-
return self.form_cls(
143+
def get_form_instance(self) -> PagerDutyNotifyServiceForm:
144+
return PagerDutyNotifyServiceForm(
146145
self.data,
147146
integrations=self.get_integrations(),
148147
services=self.get_services(),

src/sentry/integrations/slack/actions/notification.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353

5454
class SlackNotifyServiceAction(IntegrationEventAction):
5555
id = "sentry.integrations.slack.notify_action.SlackNotifyServiceAction"
56-
form_cls = SlackNotifyServiceForm
5756
prompt = "Send a Slack notification"
5857
provider = "slack"
5958
integration_key = "workspace"
@@ -335,8 +334,8 @@ def render_label(self) -> str:
335334
def get_tags_list(self) -> Sequence[str]:
336335
return [s.strip() for s in self.get_option("tags", "").split(",")]
337336

338-
def get_form_instance(self) -> Any:
339-
return self.form_cls(
337+
def get_form_instance(self) -> SlackNotifyServiceForm:
338+
return SlackNotifyServiceForm(
340339
self.data, integrations=self.get_integrations(), channel_transformer=self.get_channel_id
341340
)
342341

src/sentry/mail/actions.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
class NotifyEmailAction(EventAction):
1919
id = "sentry.mail.actions.NotifyEmailAction"
20-
form_cls = NotifyEmailForm
2120
label = "Send a notification to {targetType} and if none can be found then send a notification to {fallthroughType}"
2221
prompt = "Send a notification"
2322
metrics_slug = "EmailAction"
@@ -73,5 +72,5 @@ def after(self, event, notification_uuid: str | None = None):
7372
)
7473
)
7574

76-
def get_form_instance(self):
77-
return self.form_cls(self.project, self.data)
75+
def get_form_instance(self) -> NotifyEmailForm:
76+
return NotifyEmailForm(self.project, self.data)

src/sentry/rules/actions/integrations/base.py

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
import abc
44

5-
from django import forms
6-
75
from sentry import analytics
86
from sentry.eventstore.models import GroupEvent
97
from sentry.integrations.services.integration import (
@@ -83,9 +81,6 @@ def get_organization_integration(self) -> RpcOrganizationIntegration | None:
8381
integration_id=self.get_integration_id(), organization_id=self.project.organization_id
8482
)
8583

86-
def get_form_instance(self) -> forms.Form:
87-
return self.form_cls(self.data, integrations=self.get_integrations())
88-
8984
def record_notification_sent(
9085
self,
9186
event: GroupEvent,

src/sentry/rules/actions/integrations/create_ticket/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
class TicketEventAction(IntegrationEventAction, abc.ABC):
1717
"""Shared ticket actions"""
1818

19-
form_cls = IntegrationNotifyServiceForm
2019
integration_key = "integration"
2120
link: str | None
2221
rule: Rule
@@ -97,3 +96,6 @@ def after(
9796
integration_id=integration_id,
9897
provider=self.provider,
9998
)
99+
100+
def get_form_instance(self) -> IntegrationNotifyServiceForm:
101+
return IntegrationNotifyServiceForm(self.data, integrations=self.get_integrations())

src/sentry/rules/actions/notify_event_service.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,6 @@ class NotifyEventServiceAction(EventAction):
119119
"""Used for notifying a *specific* plugin/sentry app with a generic webhook payload."""
120120

121121
id = "sentry.rules.actions.notify_event_service.NotifyEventServiceAction"
122-
form_cls = NotifyEventServiceForm
123122
label = "Send a notification via {service}"
124123
prompt = "Send a notification via an integration"
125124

@@ -197,5 +196,5 @@ def get_plugins(self) -> Sequence[PluginService]:
197196
def get_services(self) -> Sequence[Any]:
198197
return [*self.get_plugins(), *self.get_sentry_app_services()]
199198

200-
def get_form_instance(self) -> forms.Form:
201-
return self.form_cls(self.data, services=self.get_services())
199+
def get_form_instance(self) -> NotifyEventServiceForm:
200+
return NotifyEventServiceForm(self.data, services=self.get_services())

src/sentry/rules/base.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@
5555

5656

5757
class RuleBase(abc.ABC):
58-
form_cls: type[forms.Form] = None # type: ignore[assignment]
59-
6058
logger = logging.getLogger("sentry.rules")
6159

6260
def __init__(
@@ -82,18 +80,18 @@ def is_enabled(self) -> bool:
8280
def get_option(self, key: str, default: str | None = None) -> Any:
8381
return self.data.get(key, default)
8482

85-
def get_form_instance(self) -> forms.Form:
86-
return self.form_cls(self.data if self.had_data else None)
83+
def get_form_instance(self) -> forms.Form | None:
84+
return None
8785

8886
def render_label(self) -> str:
8987
return self.label.format(**self.data)
9088

9189
def validate_form(self) -> bool:
92-
if not self.form_cls:
90+
form = self.get_form_instance()
91+
if form is None:
9392
return True
94-
95-
is_valid: bool = self.get_form_instance().is_valid()
96-
return is_valid
93+
else:
94+
return form.is_valid()
9795

9896
def future(
9997
self,

src/sentry/rules/conditions/event_attribute.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,6 @@ class EventAttributeCondition(EventCondition):
9090
"""
9191

9292
id = "sentry.rules.conditions.event_attribute.EventAttributeCondition"
93-
form_cls = EventAttributeForm
9493
label = "The event's {attribute} value {match} {value}"
9594

9695
form_fields = {
@@ -189,6 +188,9 @@ def get_event_columns(self) -> dict[Dataset, Sequence[str]]:
189188
columns: dict[Dataset, Sequence[str]] = get_dataset_columns([column])
190189
return columns
191190

191+
def get_form_instance(self) -> EventAttributeForm:
192+
return EventAttributeForm(self.data)
193+
192194

193195
# Register attribute handlers
194196
@attribute_registry.register("platform")

src/sentry/rules/conditions/event_frequency.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,6 @@ class _QSTypedDict(TypedDict):
131131

132132
class BaseEventFrequencyCondition(EventCondition, abc.ABC):
133133
intervals = STANDARD_INTERVALS
134-
form_cls = EventFrequencyForm
135134

136135
def __init__(
137136
self,
@@ -409,6 +408,9 @@ def get_value_from_groups(
409408
result = group.get(value)
410409
return result
411410

411+
def get_form_instance(self) -> EventFrequencyForm:
412+
return EventFrequencyForm(self.data)
413+
412414

413415
class EventFrequencyCondition(BaseEventFrequencyCondition):
414416
id = "sentry.rules.conditions.event_frequency.EventFrequencyCondition"
@@ -820,7 +822,6 @@ class EventFrequencyPercentCondition(BaseEventFrequencyCondition):
820822

821823
def __init__(self, *args: Any, **kwargs: Any) -> None:
822824
self.intervals = PERCENT_INTERVALS
823-
self.form_cls = EventFrequencyPercentForm
824825
super().__init__(*args, **kwargs)
825826

826827
# Override form fields interval to hide 1 min option from ui, but leave
@@ -948,6 +949,9 @@ def passes_activity_frequency(
948949
) -> bool:
949950
raise NotImplementedError
950951

952+
def get_form_instance(self) -> EventFrequencyPercentForm:
953+
return EventFrequencyPercentForm(self.data)
954+
951955

952956
def bucket_count(start: datetime, end: datetime, buckets: dict[datetime, int]) -> int:
953957
rounded_end = round_to_five_minute(end)

src/sentry/rules/conditions/level.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ class LevelEventForm(forms.Form):
2626

2727
class LevelCondition(EventCondition):
2828
id = "sentry.rules.conditions.level.LevelCondition"
29-
form_cls = LevelEventForm
3029
label = "The event's level is {match} {level}"
3130
form_fields = {
3231
"level": {"type": "choice", "choices": list(LEVEL_CHOICES.items())},
@@ -81,3 +80,6 @@ def passes_activity(
8180
return self._passes(level)
8281
except (TypeError, KeyError):
8382
return False
83+
84+
def get_form_instance(self) -> LevelEventForm:
85+
return LevelEventForm(self.data)

src/sentry/rules/conditions/tagged_event.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ def clean(self) -> dict[str, Any] | None:
3636

3737
class TaggedEventCondition(EventCondition):
3838
id = "sentry.rules.conditions.tagged_event.TaggedEventCondition"
39-
form_cls = TaggedEventForm
4039
label = "The event's tags match {key} {match} {value}"
4140

4241
form_fields = {
@@ -113,3 +112,6 @@ def get_event_columns(self) -> dict[Dataset, Sequence[str]]:
113112
[Columns.TAGS_KEY, Columns.TAGS_VALUE]
114113
)
115114
return columns
115+
116+
def get_form_instance(self) -> TaggedEventForm:
117+
return TaggedEventForm(self.data)

src/sentry/rules/filters/age_comparison.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ class AgeComparisonForm(forms.Form):
3939

4040
class AgeComparisonFilter(EventFilter):
4141
id = "sentry.rules.filters.age_comparison.AgeComparisonFilter"
42-
form_cls = AgeComparisonForm
4342
form_fields = {
4443
"comparison_type": {"type": "choice", "choices": age_comparison_choices},
4544
"value": {"type": "number", "placeholder": 10},
@@ -89,3 +88,6 @@ def passes_activity(
8988
return False
9089

9190
return self._passes(group.first_seen, condition_activity.timestamp)
91+
92+
def get_form_instance(self) -> AgeComparisonForm:
93+
return AgeComparisonForm(self.data)

src/sentry/rules/filters/assigned_to.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
class AssignedToFilter(EventFilter):
2222
id = "sentry.rules.filters.assigned_to.AssignedToFilter"
23-
form_cls = AssignedToForm
2423
label = "The issue is assigned to {targetType}"
2524
prompt = "The issue is assigned to {no one/team/member}"
2625

@@ -53,8 +52,7 @@ def passes(self, event: GroupEvent, state: EventState) -> bool:
5352
return False
5453

5554
def get_form_instance(self) -> forms.Form:
56-
form: forms.Form = self.form_cls(self.project, self.data)
57-
return form
55+
return AssignedToForm(self.project, self.data)
5856

5957
def render_label(self) -> str:
6058
target_type = AssigneeTargetType(self.get_option("targetType"))

src/sentry/rules/filters/issue_category.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ class IssueCategoryForm(forms.Form):
1919

2020
class IssueCategoryFilter(EventFilter):
2121
id = "sentry.rules.filters.issue_category.IssueCategoryFilter"
22-
form_cls = IssueCategoryForm
2322
form_fields = {"value": {"type": "choice", "choices": list(CATEGORY_CHOICES.items())}}
2423
rule_type = "filter/event"
2524
label = "The issue's category is equal to {value}"
@@ -54,3 +53,6 @@ def render_label(self) -> str:
5453
title = CATEGORY_CHOICES.get(value)
5554
group_category_name = title.title() if title else ""
5655
return self.label.format(value=group_category_name)
56+
57+
def get_form_instance(self) -> IssueCategoryForm:
58+
return IssueCategoryForm(self.data)

src/sentry/rules/filters/issue_occurrences.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ class IssueOccurrencesForm(forms.Form):
1616

1717
class IssueOccurrencesFilter(EventFilter):
1818
id = "sentry.rules.filters.issue_occurrences.IssueOccurrencesFilter"
19-
form_cls = IssueOccurrencesForm
2019
form_fields = {"value": {"type": "number", "placeholder": 10}}
2120
label = "The issue has happened at least {value} times"
2221
prompt = "The issue has happened at least {x} times (Note: this is approximate)"
@@ -56,3 +55,6 @@ def passes_activity(
5655
)
5756

5857
return bool(guess >= value)
58+
59+
def get_form_instance(self) -> IssueOccurrencesForm:
60+
return IssueOccurrencesForm(self.data)

0 commit comments

Comments
 (0)