@@ -113,68 +113,9 @@ def get_object(self) -> Incident:
113113 return incident
114114
115115
116- class SyncIncidentParticipantsView (generics .GenericAPIView ):
117- """
118- Force sync incident participants from Slack channel.
119-
120- POST /api/ui/incidents/{incident_id}/sync-participants/
121-
122- Accepts incident_id in format: INC-2000
123- Returns sync statistics.
124- Bypasses throttle (force=True).
125-
126- Authentication enforced via DEFAULT_PERMISSION_CLASSES in settings.
127- """
128-
129- def get_queryset (self ) -> QuerySet [Incident ]:
130- return Incident .objects .all ()
131-
132- def get_incident (self ) -> Incident :
133- incident_id = self .kwargs ["incident_id" ]
134- project_key = settings .PROJECT_KEY
135-
136- incident_pattern = rf"^{ re .escape (project_key )} -(\d+)$"
137- match = re .match (incident_pattern , incident_id )
138-
139- if not match :
140- raise ValidationError (
141- f"Invalid incident ID format. Expected format: { project_key } -<number> (e.g., { project_key } -123)"
142- )
143-
144- numeric_id = int (match .group (1 ))
145- queryset = self .get_queryset ()
146- queryset = filter_visible_to_user (queryset , self .request .user )
147-
148- return get_object_or_404 (queryset , id = numeric_id )
149-
150- def post (self , request : Request , incident_id : str ) -> Response :
151- incident = self .get_incident ()
152-
153- try :
154- stats = sync_incident_participants_from_slack (incident , force = True )
155- return Response ({"success" : True , "stats" : asdict (stats )})
156- except Exception as e :
157- logger .error (
158- f"Failed to force sync participants for incident { incident .id } : { e } " ,
159- exc_info = True ,
160- )
161- error_stats = ParticipantsSyncStats (
162- errors = ["Failed to sync participants from Slack" ]
163- )
164- return Response (
165- {
166- "success" : False ,
167- "error" : "Failed to sync participants from Slack" ,
168- "stats" : asdict (error_stats ),
169- },
170- status = 500 ,
171- )
172-
173-
174116# View aliases for cleaner URL imports
175117incident_list_ui = IncidentListUIView .as_view ()
176118incident_detail_ui = IncidentDetailUIView .as_view ()
177- sync_incident_participants = SyncIncidentParticipantsView .as_view ()
178119
179120
180121class IncidentListCreateAPIView (generics .ListCreateAPIView ):
@@ -275,3 +216,77 @@ def get_object(self) -> Incident:
275216 )
276217
277218 return obj
219+
220+
221+ class SyncIncidentParticipantsView (generics .GenericAPIView ):
222+ """
223+ Force sync incident participants from Slack channel.
224+
225+ POST /api/incidents/{incident_id}/sync-participants/
226+
227+ Accepts incident_id in format: INC-2000
228+ Returns sync statistics.
229+ Bypasses throttle (force=True).
230+
231+ Uses IncidentPermission for access control.
232+ """
233+
234+ permission_classes = [IncidentPermission ]
235+
236+ def get_queryset (self ) -> QuerySet [Incident ]:
237+ return Incident .objects .all ()
238+
239+ def get_object (self ) -> Incident :
240+ """
241+ Parse INC-2000 format and check permissions.
242+
243+ Returns incident if found and user has access, otherwise 404.
244+ """
245+ incident_id = self .kwargs ["incident_id" ]
246+ project_key = settings .PROJECT_KEY
247+
248+ incident_pattern = rf"^{ re .escape (project_key )} -(\d+)$"
249+ match = re .match (incident_pattern , incident_id )
250+
251+ if not match :
252+ raise ValidationError (
253+ f"Invalid incident ID format. Expected format: { project_key } -<number> (e.g., { project_key } -123)"
254+ )
255+
256+ numeric_id = int (match .group (1 ))
257+ queryset = self .get_queryset ()
258+ queryset = filter_visible_to_user (queryset , self .request .user )
259+
260+ obj = get_object_or_404 (queryset , id = numeric_id )
261+
262+ # Check object permissions
263+ self .check_object_permissions (self .request , obj )
264+
265+ return obj
266+
267+ def post (self , request : Request , incident_id : str ) -> Response :
268+ incident = self .get_object ()
269+
270+ try :
271+ stats = sync_incident_participants_from_slack (incident , force = True )
272+ return Response ({"success" : True , "stats" : asdict (stats )})
273+ except Exception as e :
274+ logger .error (
275+ f"Failed to force sync participants for incident { incident .id } : { e } " ,
276+ exc_info = True ,
277+ )
278+ error_stats = ParticipantsSyncStats (
279+ errors = ["Failed to sync participants from Slack" ]
280+ )
281+ return Response (
282+ {
283+ "success" : False ,
284+ "error" : "Failed to sync participants from Slack" ,
285+ "stats" : asdict (error_stats ),
286+ },
287+ status = 500 ,
288+ )
289+
290+
291+ # View alias for sync endpoint
292+ sync_incident_participants = SyncIncidentParticipantsView .as_view ()
0 commit comments