Skip to content

Commit 8c6b4dd

Browse files
ref: fix typing for plugins.bases.issue{,2} (#83445)
<!-- Describe your PR here. -->
1 parent f5a97ca commit 8c6b4dd

File tree

8 files changed

+109
-174
lines changed

8 files changed

+109
-174
lines changed

pyproject.toml

-3
Original file line numberDiff line numberDiff line change
@@ -247,11 +247,8 @@ module = [
247247
"sentry.pipeline.views.base",
248248
"sentry.pipeline.views.nested",
249249
"sentry.plugins.bases.data_forwarding",
250-
"sentry.plugins.bases.issue",
251-
"sentry.plugins.bases.issue2",
252250
"sentry.plugins.bases.notify",
253251
"sentry.plugins.config",
254-
"sentry.plugins.endpoints",
255252
"sentry.receivers.releases",
256253
"sentry.release_health.metrics_sessions_v2",
257254
"sentry.replays.endpoints.project_replay_clicks_index",

src/sentry/plugins/bases/issue.py

+26-76
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,11 @@ class NewIssueForm(forms.Form):
2424
class IssueTrackingPlugin(Plugin):
2525
# project_conf_form = BaseIssueOptionsForm
2626
new_issue_form: type[forms.Form] = NewIssueForm
27-
link_issue_form = None
2827

2928
create_issue_template = "sentry/plugins/bases/issue/create_issue.html"
3029
not_configured_template = "sentry/plugins/bases/issue/not_configured.html"
3130
needs_auth_template = "sentry/plugins/bases/issue/needs_auth.html"
3231
auth_provider: str | None = None
33-
can_unlink_issues = False
34-
can_link_existing_issues = False
3532

3633
def get_plugin_type(self):
3734
return "issue-tracking"
@@ -58,7 +55,7 @@ def _get_group_title(self, request: Request, group, event):
5855
def is_configured(self, project) -> bool:
5956
raise NotImplementedError
6057

61-
def get_auth_for_user(self, user, **kwargs) -> RpcUserSocialAuth:
58+
def get_auth_for_user(self, user, **kwargs) -> RpcUserSocialAuth | None:
6259
"""
6360
Return a ``RpcUserSocialAuth`` object for the given user based on this plugins ``auth_provider``.
6461
"""
@@ -115,13 +112,6 @@ def get_new_issue_read_only_fields(self, *args, **kwargs):
115112
"""
116113
return []
117114

118-
def get_link_existing_issue_form(self, request: Request, group, event, **kwargs):
119-
if not self.link_issue_form:
120-
return None
121-
return self.link_issue_form(
122-
request.POST or None, initial=self.get_initial_link_form_data(request, group, event)
123-
)
124-
125115
def get_issue_url(self, group, issue_id: str) -> str:
126116
"""
127117
Given an issue_id (string) return an absolute URL to the issue's details
@@ -161,9 +151,6 @@ def get_initial_form_data(self, request: Request, group, event, **kwargs):
161151
"title": self._get_group_title(request, group, event),
162152
}
163153

164-
def get_initial_link_form_data(self, request: Request, group, event, **kwargs):
165-
return {}
166-
167154
def has_auth_configured(self, **kwargs):
168155
if not self.auth_provider:
169156
return True
@@ -201,8 +188,6 @@ def view(self, request: Request, group, **kwargs):
201188
)
202189

203190
if GroupMeta.objects.get_value(group, "%s:tid" % self.get_conf_key(), None):
204-
if self.can_unlink_issues and request.GET.get("unlink"):
205-
return self.handle_unlink_issue(request, group, **kwargs)
206191
return None
207192

208193
prefix = self.get_conf_key()
@@ -211,70 +196,40 @@ def view(self, request: Request, group, **kwargs):
211196
op = request.POST.get("op", "create")
212197

213198
create_form = self.get_new_issue_form(request, group, event)
214-
link_form = None
215-
if self.can_link_existing_issues:
216-
link_form = self.get_link_existing_issue_form(request, group, event)
217199

218200
if op == "create":
219-
issue_id = None
220201
if create_form.is_valid():
221202
try:
222203
issue_id = self.create_issue(
223204
group=group, form_data=create_form.cleaned_data, request=request
224205
)
225206
except forms.ValidationError as e:
226207
create_form.errors["__all__"] = ["Error creating issue: %s" % e]
227-
228-
if create_form.is_valid():
229-
GroupMeta.objects.set_value(group, "%s:tid" % prefix, issue_id)
230-
231-
issue_information = {
232-
"title": create_form.cleaned_data["title"],
233-
"provider": self.get_title(),
234-
"location": self.get_issue_url(group, issue_id),
235-
"label": self.get_issue_label(group=group, issue_id=issue_id),
236-
}
237-
Activity.objects.create(
238-
project=group.project,
239-
group=group,
240-
type=ActivityType.CREATE_ISSUE.value,
241-
user_id=request.user.id,
242-
data=issue_information,
243-
)
244-
245-
issue_tracker_used.send_robust(
246-
plugin=self,
247-
project=group.project,
248-
user=request.user,
249-
sender=IssueTrackingPlugin,
250-
)
251-
return self.redirect(group.get_absolute_url())
252-
253-
elif op == "link":
254-
if link_form.is_valid():
255-
try:
256-
self.link_issue(group=group, form_data=link_form.cleaned_data, request=request)
257-
except forms.ValidationError as e:
258-
link_form.errors["__all__"] = ["Error creating issue: %s" % e]
259-
260-
if link_form.is_valid():
261-
issue_id = int(link_form.cleaned_data["issue_id"])
262-
GroupMeta.objects.set_value(group, "%s:tid" % prefix, issue_id)
263-
issue_information = {
264-
"title": self.get_issue_title_by_id(request, group, issue_id),
265-
"provider": self.get_title(),
266-
"location": self.get_issue_url(group, issue_id),
267-
"label": self.get_issue_label(group=group, issue_id=issue_id),
268-
}
269-
Activity.objects.create(
270-
project=group.project,
271-
group=group,
272-
type=ActivityType.CREATE_ISSUE.value,
273-
user_id=request.user.id,
274-
data=issue_information,
275-
)
276-
277-
return self.redirect(group.get_absolute_url())
208+
else:
209+
if create_form.is_valid():
210+
GroupMeta.objects.set_value(group, "%s:tid" % prefix, issue_id)
211+
212+
issue_information = {
213+
"title": create_form.cleaned_data["title"],
214+
"provider": self.get_title(),
215+
"location": self.get_issue_url(group, issue_id),
216+
"label": self.get_issue_label(group=group, issue_id=issue_id),
217+
}
218+
Activity.objects.create(
219+
project=group.project,
220+
group=group,
221+
type=ActivityType.CREATE_ISSUE.value,
222+
user_id=request.user.id,
223+
data=issue_information,
224+
)
225+
226+
issue_tracker_used.send_robust(
227+
plugin=self,
228+
project=group.project,
229+
user=request.user,
230+
sender=IssueTrackingPlugin,
231+
)
232+
return self.redirect(group.get_absolute_url())
278233

279234
context = {
280235
"create_form": create_form,
@@ -283,7 +238,6 @@ def view(self, request: Request, group, **kwargs):
283238
"title": self.get_new_issue_title(),
284239
"read_only_fields": self.get_new_issue_read_only_fields(group=group),
285240
"can_link_existing_issues": self.can_link_existing_issues,
286-
"link_form": link_form,
287241
"op": op,
288242
}
289243

@@ -295,10 +249,6 @@ def actions(self, request: Request, group, action_list, **kwargs):
295249
prefix = self.get_conf_key()
296250
if not GroupMeta.objects.get_value(group, "%s:tid" % prefix, None):
297251
action_list.append((self.get_new_issue_title(), self.get_url(group)))
298-
elif self.can_unlink_issues:
299-
action_list.append(
300-
(self.get_unlink_issue_title(), "%s?unlink=1" % self.get_url(group).rstrip("/"))
301-
)
302252
return action_list
303253

304254
def tags(self, request: Request, group, tag_list, **kwargs):

src/sentry/plugins/bases/issue2.py

+50-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,25 @@
11
from __future__ import annotations
22

3+
from collections.abc import Callable
4+
from typing import TYPE_CHECKING, NotRequired, TypedDict
5+
36
from django.conf import settings
7+
from django.http.response import HttpResponseBase
48
from django.urls import re_path, reverse
59
from rest_framework.request import Request
610
from rest_framework.response import Response
711

812
from sentry.api.api_publish_status import ApiPublishStatus
913
from sentry.api.base import region_silo_endpoint
14+
from sentry.api.bases.group import GroupEndpoint
1015
from sentry.api.serializers.models.plugin import PluginSerializer
1116

1217
# api compat
1318
from sentry.exceptions import PluginError # NOQA
1419
from sentry.models.activity import Activity
20+
from sentry.models.group import Group
1521
from sentry.models.groupmeta import GroupMeta
1622
from sentry.plugins.base.v1 import Plugin
17-
from sentry.plugins.endpoints import PluginGroupEndpoint
1823
from sentry.signals import issue_tracker_used
1924
from sentry.types.activity import ActivityType
2025
from sentry.users.services.usersocialauth.model import RpcUserSocialAuth
@@ -23,6 +28,45 @@
2328
from sentry.utils.http import absolute_uri
2429
from sentry.utils.safe import safe_execute
2530

31+
if TYPE_CHECKING:
32+
from django.utils.functional import _StrPromise
33+
34+
35+
@region_silo_endpoint
36+
class PluginGroupEndpoint(GroupEndpoint):
37+
publish_status = {
38+
"GET": ApiPublishStatus.PRIVATE,
39+
"POST": ApiPublishStatus.PRIVATE,
40+
}
41+
view: Callable[[Request, Group], HttpResponseBase] = None # type: ignore[assignment] # populated by .as_view
42+
43+
def _handle(self, request: Request, group, *args, **kwargs):
44+
GroupMeta.objects.populate_cache([group])
45+
46+
return self.view(request, group, *args, **kwargs)
47+
48+
def get(self, request: Request, group, *args, **kwargs) -> Response:
49+
return self._handle(request, group, *args, **kwargs)
50+
51+
def post(self, request: Request, group, *args, **kwargs) -> Response:
52+
return self._handle(request, group, *args, **kwargs)
53+
54+
def respond(self, *args, **kwargs):
55+
return Response(*args, **kwargs)
56+
57+
58+
class _PluginIssueIssue(TypedDict):
59+
issue_id: int
60+
url: str
61+
label: str
62+
63+
64+
class _PluginIssue(TypedDict):
65+
slug: str
66+
allowed_actions: tuple[str, ...]
67+
title: str | _StrPromise
68+
issue: NotRequired[_PluginIssueIssue]
69+
2670

2771
# TODO(dcramer): remove this in favor of GroupEndpoint
2872
@region_silo_endpoint
@@ -31,8 +75,8 @@ class IssueGroupActionEndpoint(PluginGroupEndpoint):
3175
"GET": ApiPublishStatus.PRIVATE,
3276
"POST": ApiPublishStatus.PRIVATE,
3377
}
34-
view_method_name = None
35-
plugin = None
78+
view_method_name: str = None # type: ignore[assignment] # populated by .as_view
79+
plugin: IssuePlugin2 = None # type: ignore[assignment] # populated by .as_view
3680

3781
def _handle(self, request: Request, group, *args, **kwargs):
3882
GroupMeta.objects.populate_cache([group])
@@ -90,7 +134,7 @@ def get_group_urls(self):
90134
)
91135
return _urls
92136

93-
def get_auth_for_user(self, user, **kwargs) -> RpcUserSocialAuth:
137+
def get_auth_for_user(self, user, **kwargs) -> RpcUserSocialAuth | None:
94138
"""
95139
Return a ``RpcUserSocialAuth`` object for the given user based on this plugins ``auth_provider``.
96140
"""
@@ -99,10 +143,9 @@ def get_auth_for_user(self, user, **kwargs) -> RpcUserSocialAuth:
99143
if not user.is_authenticated:
100144
return None
101145

102-
auth = usersocialauth_service.get_one_or_none(
146+
return usersocialauth_service.get_one_or_none(
103147
filter={"user_id": user.id, "provider": self.auth_provider}
104148
)
105-
return auth
106149

107150
def needs_auth(self, request: Request, project, **kwargs):
108151
"""
@@ -358,7 +401,7 @@ def plugin_issues(self, request: Request, group, plugin_issues, **kwargs) -> Non
358401
if not self.is_configured(project=group.project):
359402
return
360403

361-
item = {
404+
item: _PluginIssue = {
362405
"slug": self.slug,
363406
"allowed_actions": self.allowed_actions,
364407
"title": self.get_title(),

src/sentry/plugins/endpoints.py

-61
This file was deleted.

src/sentry/plugins/providers/base.py

-25
Original file line numberDiff line numberDiff line change
@@ -74,31 +74,6 @@ def get_auth_url(self, user, **kwargs):
7474

7575
return reverse("socialauth_associate", args=[self.auth_provider])
7676

77-
def needs_auth(self, user, **kwargs):
78-
"""
79-
Return ``True`` if the authenticated user needs to associate an auth
80-
service before performing actions with this provider.
81-
"""
82-
if self.auth_provider is None:
83-
return False
84-
85-
organization = kwargs.get("organization")
86-
if organization:
87-
ois = integration_service.get_organization_integrations(
88-
providers=[self.auth_provider], organization_id=organization.id
89-
)
90-
has_auth = len(ois) > 0
91-
if has_auth:
92-
return False
93-
94-
if not user.is_authenticated:
95-
return True
96-
97-
auths = usersocialauth_service.get_many(
98-
filter={"user_id": user.id, "provider": self.auth_provider}
99-
)
100-
return len(auths) == 0
101-
10277
def get_auth(self, user: RpcUser | User, **kwargs) -> RpcUserSocialAuth | None:
10378
if self.auth_provider is None:
10479
return None

0 commit comments

Comments
 (0)