Skip to content

Commit

Permalink
Automatically configuring video settings for the Talk project. (fossa…
Browse files Browse the repository at this point in the history
…sia#242)

* Enable video plugin

* Fix black in pipeline

* Fix black in pipeline

* Add comment

* Update code

* Configure video settings for talk

* Fix black pipeline

* Fix black in pipeline

* Add comment

* using constant from http module instead of string

* using constant from http, using httpstatus

* No need to bother with this outside of CI.

* fix black code style

* rework code

* fix code style, remove unused import

* fix inconsistence for API response

* format code

* handle case input form invalid

* fix isort, black code style

* fix isort, black code style

* re format API response for case validation error

* fix pipeline

* make error message more clear for validation error

* coding style fix

---------

Co-authored-by: odkhang <[email protected]>
  • Loading branch information
odkhang and lcduong authored Dec 20, 2024
1 parent 5117573 commit 80a0676
Show file tree
Hide file tree
Showing 4 changed files with 151 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/pretalx/api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,4 +30,5 @@
"events/<event>/favourite-talk/",
submission.SubmissionFavouriteDeprecatedView.as_view(),
),
path("configure-video-settings/", event.ConfigureVideoSettingsView.as_view()),
]
143 changes: 143 additions & 0 deletions src/pretalx/api/views/event.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
import logging
from http import HTTPStatus

import jwt
from django.conf import settings
from django.core.exceptions import ValidationError
from django.http import Http404
from django_scopes import scopes_disabled
from pretalx_venueless.forms import VenuelessSettingsForm
from rest_framework import viewsets
from rest_framework.authentication import get_authorization_header
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.views import APIView

from pretalx.api.serializers.event import EventSerializer
from pretalx.common import exceptions
from pretalx.common.exceptions import VideoIntegrationError
from pretalx.event.models import Event

logger = logging.getLogger(__name__)


class EventViewSet(viewsets.ReadOnlyModelViewSet):
serializer_class = EventSerializer
Expand All @@ -24,3 +41,129 @@ def get_object(self):
if self.request.user.has_perm(self.permission_required, self.request.event):
return self.request.event
raise Http404()


class ConfigureVideoSettingsView(APIView):
"""
API View to configure video settings for an event.
"""

authentication_classes = []
permission_classes = [AllowAny]

def post(self, request, *args, **kwargs):
"""
Handle POST request to configure video settings.
@param request: request object
@return response object
"""
try:
video_settings = request.data.get("video_settings")
if not video_settings or "secret" not in video_settings:
raise VideoIntegrationError(
"Video settings are missing or secret is not provided"
)
payload = get_payload_from_token(request, video_settings)
event_slug = payload.get("event_slug")
video_tokens = payload.get("video_tokens")
with scopes_disabled():
event_instance = Event.objects.get(slug=event_slug)
save_video_settings_information(
event_slug, video_tokens, event_instance
)
return Response(
{
"status": "success",
"message": "Video settings configured successfully.",
},
status=HTTPStatus.OK,
)
except Event.DoesNotExist:
logger.error("Event does not exist.")
raise Http404("Event does not exist")
except ValidationError as e:
return Response({"detail": str(e)}, status=HTTPStatus.BAD_REQUEST)
except VideoIntegrationError as e:
logger.error("Error configuring video settings: %s", e)
return Response(
{"detail": "Video settings are missing, please try after sometime."},
status=HTTPStatus.SERVICE_UNAVAILABLE,
)
except AuthenticationFailed as e:
logger.error("Authentication failed: %s", e)
raise AuthenticationFailed("Authentication failed.")


def get_payload_from_token(request, video_settings):
"""
Verify the token and return the payload
@param request: request object
@param video_settings: dict containing video settings
@return: dict containing payload data from the token
"""
try:
auth_header = get_authorization_header(request).split()
if not auth_header:
raise exceptions.AuthenticationFailedError("No authorization header")

if len(auth_header) != 2 or auth_header[0].lower() != b"bearer":
raise exceptions.AuthenticationFailedError(
"Invalid token format. Must be 'Bearer <token>'"
)

token_decode = jwt.decode(
auth_header[1], video_settings.get("secret"), algorithms=["HS256"]
)

event_slug = token_decode.get("slug")
video_tokens = token_decode.get("video_tokens")

if not event_slug or not video_tokens:
raise exceptions.AuthenticationFailedError("Invalid token payload")

return {"event_slug": event_slug, "video_tokens": video_tokens}

except jwt.ExpiredSignatureError:
raise exceptions.AuthenticationFailedError("Token has expired")
except jwt.InvalidTokenError:
raise exceptions.AuthenticationFailedError("Invalid token")


def save_video_settings_information(event_slug, video_tokens, event_instance):
"""
Save video settings information
@param event_slug: A string representing the event slug
@param video_tokens: A list of video tokens
@param event_instance: An instance of the event
@return: Response object
"""

if not video_tokens:
raise VideoIntegrationError("Video tokens list is empty")

video_settings_data = {
"token": video_tokens[0],
"url": "{}/api/v1/worlds/{}/".format(
settings.EVENTYAY_VIDEO_BASE_PATH, event_slug
),
}

video_settings_form = VenuelessSettingsForm(
event=event_instance, data=video_settings_data
)

if video_settings_form.is_valid():
video_settings_form.save()
logger.info("Video settings configured successfully for event %s.", event_slug)
else:
errors = video_settings_form.errors.get_json_data()
formatted_errors = {
field: [error["message"] for error in error_list]
for field, error_list in errors.items()
}
logger.error(
"Failed to configure video settings for event %s - Validation errors: %s.",
event_slug,
formatted_errors,
)
raise ValidationError(formatted_errors)
4 changes: 4 additions & 0 deletions src/pretalx/common/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ class AuthenticationFailedError(Exception):
pass


class VideoIntegrationError(Exception):
pass


class PretalxExceptionReporter(ExceptionReporter):
def get_traceback_text(self): # pragma: no cover
traceback_text = super().get_traceback_text()
Expand Down
3 changes: 3 additions & 0 deletions src/pretalx/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,9 @@ def merge_csp(*options, config=None):
EVENTYAY_TICKET_BASE_PATH = config.get(
"urls", "eventyay-ticket", fallback="https://app-test.eventyay.com/tickets"
)
EVENTYAY_VIDEO_BASE_PATH = config.get(
"urls", "eventyay-video", fallback="https://app-test.eventyay.com/video"
)

SITE_ID = 1
# for now, customer must verified their email at eventyay-ticket, so this check not required
Expand Down

0 comments on commit 80a0676

Please sign in to comment.