1+ from __future__ import annotations
2+
13from collections .abc import Mapping , MutableMapping
24from typing import Any
35
6+ from django .contrib .auth .models import AnonymousUser
47from django .db import IntegrityError , router , transaction
58from rest_framework .request import Request
69from rest_framework .response import Response
1215from sentry .api .bases import GroupEndpoint
1316from sentry .api .serializers import serialize
1417from sentry .integrations .api .serializers .models .integration import IntegrationSerializer
15- from sentry .integrations .base import IntegrationFeatures , IntegrationInstallation
18+ from sentry .integrations .base import IntegrationFeatures
19+ from sentry .integrations .mixins .issues import IssueBasicIntegration
1620from sentry .integrations .models .external_issue import ExternalIssue
21+ from sentry .integrations .models .integration import Integration
1722from sentry .integrations .project_management .metrics import (
1823 ProjectManagementActionType ,
1924 ProjectManagementEvent ,
3035from sentry .signals import integration_issue_created , integration_issue_linked
3136from sentry .types .activity import ActivityType
3237from sentry .users .models .user import User
38+ from sentry .users .services .user .model import RpcUser
3339
3440MISSING_FEATURE_MESSAGE = "Your organization does not have access to this feature."
3541
@@ -46,7 +52,11 @@ def __init__(
4652 self .config = config
4753
4854 def serialize (
49- self , obj : RpcIntegration , attrs : Mapping [str , Any ], user : User , ** kwargs : Any
55+ self ,
56+ obj : Integration | RpcIntegration ,
57+ attrs : Mapping [str , Any ],
58+ user : User | RpcUser | AnonymousUser ,
59+ ** kwargs : Any ,
5060 ) -> MutableMapping [str , Any ]:
5161 data = super ().serialize (obj , attrs , user )
5262
@@ -68,7 +78,7 @@ class GroupIntegrationDetailsEndpoint(GroupEndpoint):
6878 "POST" : ApiPublishStatus .UNKNOWN ,
6979 }
7080
71- def _has_issue_feature (self , organization , user ):
81+ def _has_issue_feature (self , organization , user ) -> bool :
7282 has_issue_basic = features .has (
7383 "organizations:integrations-issue-basic" , organization , actor = user
7484 )
@@ -79,16 +89,24 @@ def _has_issue_feature(self, organization, user):
7989
8090 return has_issue_sync or has_issue_basic
8191
82- def _has_issue_feature_on_integration (self , integration : RpcIntegration ):
92+ def _has_issue_feature_on_integration (self , integration : RpcIntegration ) -> bool :
8393 return integration .has_feature (
8494 feature = IntegrationFeatures .ISSUE_BASIC
8595 ) or integration .has_feature (feature = IntegrationFeatures .ISSUE_SYNC )
8696
97+ def _get_installation (
98+ self , integration : RpcIntegration , organization_id : int
99+ ) -> IssueBasicIntegration :
100+ installation = integration .get_installation (organization_id = organization_id )
101+ if not isinstance (installation , IssueBasicIntegration ):
102+ raise ValueError (installation )
103+ return installation
104+
87105 def create_issue_activity (
88106 self ,
89107 request : Request ,
90108 group : Group ,
91- installation : IntegrationInstallation ,
109+ installation : IssueBasicIntegration ,
92110 external_issue : ExternalIssue ,
93111 new : bool ,
94112 ):
@@ -108,7 +126,9 @@ def create_issue_activity(
108126 )
109127
110128 def get (self , request : Request , group , integration_id ) -> Response :
111- if not self ._has_issue_feature (group .organization , request .user ):
129+ if not request .user .is_authenticated :
130+ return Response (status = 400 )
131+ elif not self ._has_issue_feature (group .organization , request .user ):
112132 return Response ({"detail" : MISSING_FEATURE_MESSAGE }, status = 400 )
113133
114134 # Keep link/create separate since create will likely require
@@ -132,16 +152,16 @@ def get(self, request: Request, group, integration_id) -> Response:
132152 {"detail" : "This feature is not supported for this integration." }, status = 400
133153 )
134154
135- installation = integration .get_installation (organization_id = organization_id )
136- config = None
155+ installation = self ._get_installation (integration , organization_id )
137156 try :
138157 if action == "link" :
139158 config = installation .get_link_issue_config (group , params = request .GET )
140-
141- if action == "create" :
159+ elif action == "create" :
142160 config = installation .get_create_issue_config (
143161 group , request .user , params = request .GET
144162 )
163+ else :
164+ raise AssertionError ("unreachable" )
145165 except IntegrationError as e :
146166 return Response ({"detail" : str (e )}, status = 400 )
147167
@@ -156,7 +176,9 @@ def get(self, request: Request, group, integration_id) -> Response:
156176
157177 # was thinking put for link an existing issue, post for create new issue?
158178 def put (self , request : Request , group , integration_id ) -> Response :
159- if not self ._has_issue_feature (group .organization , request .user ):
179+ if not request .user .is_authenticated :
180+ return Response (status = 400 )
181+ elif not self ._has_issue_feature (group .organization , request .user ):
160182 return Response ({"detail" : MISSING_FEATURE_MESSAGE }, status = 400 )
161183
162184 external_issue_id = request .data .get ("externalIssue" )
@@ -181,7 +203,7 @@ def put(self, request: Request, group, integration_id) -> Response:
181203 {"detail" : "This feature is not supported for this integration." }, status = 400
182204 )
183205
184- installation = integration . get_installation ( organization_id = organization_id )
206+ installation = self . _get_installation ( integration , organization_id )
185207
186208 try :
187209 data = installation .get_issue (external_issue_id , data = request .data )
@@ -254,7 +276,9 @@ def put(self, request: Request, group, integration_id) -> Response:
254276 return Response (context , status = 201 )
255277
256278 def post (self , request : Request , group , integration_id ) -> Response :
257- if not self ._has_issue_feature (group .organization , request .user ):
279+ if not request .user .is_authenticated :
280+ return Response (status = 400 )
281+ elif not self ._has_issue_feature (group .organization , request .user ):
258282 return Response ({"detail" : MISSING_FEATURE_MESSAGE }, status = 400 )
259283
260284 organization_id = group .project .organization_id
@@ -271,7 +295,7 @@ def post(self, request: Request, group, integration_id) -> Response:
271295 {"detail" : "This feature is not supported for this integration." }, status = 400
272296 )
273297
274- installation = integration . get_installation ( organization_id = organization_id )
298+ installation = self . _get_installation ( integration , organization_id )
275299
276300 with ProjectManagementEvent (
277301 action_type = ProjectManagementActionType .CREATE_EXTERNAL_ISSUE_VIA_ISSUE_DETAIL ,
0 commit comments