Browse Source

ending session hhandling fixed

master
Mohsen Taba 1 month ago
parent
commit
e09b2e31fb
  1. 139
      apps/course/tests/test_webhook_live_session.py
  2. 21
      apps/course/views/webhook.py

139
apps/course/tests/test_webhook_live_session.py

@ -0,0 +1,139 @@
import hashlib
import hmac
import json
import tempfile
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import override_settings
from django.urls import reverse
from django.utils import timezone
from dj_language.models import Language
from rest_framework import status
from rest_framework.test import APITestCase
from apps.account.models import ProfessorUser, StudentUser
from apps.course.models import (
Course,
CourseCategory,
CourseLiveSession,
LiveSessionUser,
)
@override_settings(
PLUGNMEET_API_SECRET='test-secret',
MEDIA_ROOT=tempfile.gettempdir(),
)
class PlugNMeetWebhookLiveSessionTests(APITestCase):
def setUp(self):
Language.objects.update_or_create(
id=69,
defaults={
'name': 'English',
'code': 'en',
'status': True,
'countries': ['US'],
},
)
self.professor = ProfessorUser.objects.create(
email='webhook-prof@example.com',
fullname='Webhook Professor',
experience_years=8,
)
self.student = StudentUser.objects.create(
email='webhook-student@example.com',
fullname='Webhook Student',
)
self.category = CourseCategory.objects.create(
name='Webhook Category',
slug='webhook-category',
)
thumbnail = SimpleUploadedFile(
'thumb.jpg',
b'filecontent',
content_type='image/jpeg',
)
self.course = Course.objects.create(
title='Webhook Course',
slug='webhook-course',
category=self.category,
professor=self.professor,
thumbnail=thumbnail,
video_type=Course.VedioTypeChoices.YOUTUBE_LINK,
video_link='https://example.com/video',
is_online=True,
online_link='https://example.com/live',
level=Course.LevelChoices.BEGINNER,
duration=10,
lessons_count=1,
description='Description',
short_description='Short',
status=Course.StatusChoices.ONGOING,
is_free=True,
)
self.session = CourseLiveSession.objects.create(
course=self.course,
room_id='room-16-1781160705',
subject='Webhook Course Live Session',
started_at=timezone.now(),
)
LiveSessionUser.objects.create(
session=self.session,
user=self.student,
role='participant',
entered_at=timezone.now(),
is_online=True,
)
def _post_webhook(self, payload: dict):
body = json.dumps(payload, ensure_ascii=False).encode('utf-8')
signature = hmac.new(
b'test-secret',
body,
hashlib.sha256,
).hexdigest()
url = reverse('plugnmeet-webhook')
return self.client.post(
url,
data=body,
content_type='application/webhook+json',
HTTP_HASH_TOKEN=signature,
)
def test_room_finished_with_room_name_closes_live_session(self):
response = self._post_webhook(
{
'event': 'room_finished',
'room': {
'name': self.session.room_id,
},
}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
self.session.refresh_from_db()
self.assertIsNotNone(self.session.ended_at)
self.assertFalse(
LiveSessionUser.objects.get(session=self.session, user=self.student).is_online
)
def test_session_ended_event_closes_live_session(self):
second_session = CourseLiveSession.objects.create(
course=self.course,
room_id='room-16-1781160999',
subject='Second Live Session',
started_at=timezone.now(),
)
response = self._post_webhook(
{
'event': 'session_ended',
'room': {
'name': second_session.room_id,
},
}
)
self.assertEqual(response.status_code, status.HTTP_200_OK)
second_session.refresh_from_db()
self.assertIsNotNone(second_session.ended_at)

21
apps/course/views/webhook.py

@ -91,6 +91,7 @@ class PlugNMeetWebhookAPIView(APIView):
# 4. Route Event # 4. Route Event
handler_map = { handler_map = {
'room_finished': self._handle_room_finished, 'room_finished': self._handle_room_finished,
'session_ended': self._handle_room_finished,
'participant_joined': self._handle_participant_joined, 'participant_joined': self._handle_participant_joined,
'participant_left': self._handle_participant_left, 'participant_left': self._handle_participant_left,
'recording_proceeded': self._handle_recording_proceeded, 'recording_proceeded': self._handle_recording_proceeded,
@ -178,9 +179,21 @@ class PlugNMeetWebhookAPIView(APIView):
logger.error(f"❌ [PlugNMeet Webhook] Signature mismatch! \nReceived: {hash_token[:10]}...\nExpected HMAC: {expected_signature[:10]}...") logger.error(f"❌ [PlugNMeet Webhook] Signature mismatch! \nReceived: {hash_token[:10]}...\nExpected HMAC: {expected_signature[:10]}...")
return False return False
@staticmethod
def _extract_room_id(room_data: Dict[str, Any]) -> str | None:
if not isinstance(room_data, dict):
return None
return (
room_data.get('room_id')
or room_data.get('roomId')
or room_data.get('identity')
or room_data.get('name')
)
def _handle_room_finished(self, payload: Dict[str, Any]) -> Dict[str, Any]: def _handle_room_finished(self, payload: Dict[str, Any]) -> Dict[str, Any]:
room_data = payload.get('room', {}) room_data = payload.get('room', {})
room_id = room_data.get('room_id') or room_data.get('roomId') or room_data.get('identity')
room_id = self._extract_room_id(room_data)
if not room_id: if not room_id:
logger.warning(f"⚠️ [PlugNMeet Webhook] Missing room_id in room_finished event. Payload: {payload}") logger.warning(f"⚠️ [PlugNMeet Webhook] Missing room_id in room_finished event. Payload: {payload}")
@ -211,7 +224,7 @@ class PlugNMeetWebhookAPIView(APIView):
def _handle_participant_joined(self, payload: Dict[str, Any]) -> Dict[str, Any]: def _handle_participant_joined(self, payload: Dict[str, Any]) -> Dict[str, Any]:
room_data = payload.get('room', {}) room_data = payload.get('room', {})
participant_data = payload.get('participant', {}) participant_data = payload.get('participant', {})
room_id = room_data.get('room_id') or room_data.get('roomId') or room_data.get('identity')
room_id = self._extract_room_id(room_data)
user_id = participant_data.get('identity') or participant_data.get('user_id') or participant_data.get('userId') user_id = participant_data.get('identity') or participant_data.get('user_id') or participant_data.get('userId')
if not room_id or not user_id: if not room_id or not user_id:
@ -247,7 +260,7 @@ class PlugNMeetWebhookAPIView(APIView):
def _handle_participant_left(self, payload: Dict[str, Any]) -> Dict[str, Any]: def _handle_participant_left(self, payload: Dict[str, Any]) -> Dict[str, Any]:
room_data = payload.get('room', {}) room_data = payload.get('room', {})
participant_data = payload.get('participant', {}) participant_data = payload.get('participant', {})
room_id = room_data.get('room_id') or room_data.get('roomId') or room_data.get('identity')
room_id = self._extract_room_id(room_data)
user_id = participant_data.get('identity') or participant_data.get('user_id') or participant_data.get('userId') user_id = participant_data.get('identity') or participant_data.get('user_id') or participant_data.get('userId')
if not room_id or not user_id: if not room_id or not user_id:
@ -300,7 +313,7 @@ class PlugNMeetWebhookAPIView(APIView):
room_data = payload.get('room', {}) room_data = payload.get('room', {})
recording_info = payload.get('recording_info', {}) recording_info = payload.get('recording_info', {})
room_id = room_data.get('room_id') or room_data.get('roomId') or room_data.get('identity')
room_id = self._extract_room_id(room_data)
recording_id = ( recording_id = (
recording_info.get('record_id') or recording_info.get('record_id') or
recording_info.get('recordId') or recording_info.get('recordId') or

Loading…
Cancel
Save