Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically configuring video settings for the Talk project. #242

Open
wants to merge 33 commits into
base: development
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
ecd2a5c
Enable video plugin
lcduong Dec 2, 2024
2579880
Fix black in pipeline
lcduong Dec 2, 2024
07fc00b
Fix black in pipeline
lcduong Dec 2, 2024
af2191e
Add comment
lcduong Dec 2, 2024
c5d487a
Update code
lcduong Dec 3, 2024
b57dda0
Configure video settings for talk
lcduong Dec 6, 2024
c87348f
Merge branch 'development' of github.com:odkhang/eventyay-talk into a…
odkhang Dec 6, 2024
0adbf20
Fix black pipeline
odkhang Dec 6, 2024
2b18319
Update code
odkhang Dec 6, 2024
d0d81cd
Update code
odkhang Dec 6, 2024
06ee69e
Update code
odkhang Dec 6, 2024
0a7f382
Update code
odkhang Dec 6, 2024
9cfb2f0
Fix black in pipeline
odkhang Dec 6, 2024
a340e38
Add comment
odkhang Dec 6, 2024
6dd84ae
Update code
odkhang Dec 9, 2024
9fe1191
Update code
odkhang Dec 9, 2024
463cd05
Update code
odkhang Dec 9, 2024
1d3617f
using constant from http module instead of string
odkhang Dec 16, 2024
b2a9bd3
using constant from http, using httpstatus
odkhang Dec 16, 2024
8766a3e
Merge branch 'development' into auto-fill-talk-link
odkhang Dec 16, 2024
7bcca21
No need to bother with this outside of CI.
odkhang Dec 16, 2024
bc5a53a
fix black code style
odkhang Dec 16, 2024
60df621
rework code
odkhang Dec 17, 2024
68f221f
fix code style, remove unused import
odkhang Dec 17, 2024
bea82c0
fix inconsistence for API response
odkhang Dec 18, 2024
a845cf4
format code
odkhang Dec 18, 2024
3eb6d17
handle case input form invalid
odkhang Dec 18, 2024
a3ef9fc
fix isort, black code style
odkhang Dec 18, 2024
d98fbde
fix isort, black code style
odkhang Dec 18, 2024
7947d31
re format API response for case validation error
odkhang Dec 19, 2024
1b6ce53
fix pipeline
odkhang Dec 19, 2024
99717c9
make error message more clear for validation error
odkhang Dec 19, 2024
f28ab8e
coding style fix
odkhang Dec 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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(
odkhang marked this conversation as resolved.
Show resolved Hide resolved
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],
odkhang marked this conversation as resolved.
Show resolved Hide resolved
"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 @@ -689,6 +689,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
21 changes: 0 additions & 21 deletions src/tests/agenda/test_agenda_schedule_export.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,27 +39,6 @@ def test_schedule_xsd_is_up_to_date():
assert response.data.decode() == schema_content


@pytest.mark.skipif(
"CI" not in os.environ or not os.environ["CI"],
reason="No need to bother with this outside of CI.",
)
def test_schedule_json_schema_is_up_to_date():
"""If this test fails:

http -d https://raw.githubusercontent.com/voc/schedule/master/validator/json/schema.json >! tests/fixtures/schedule.json
"""
http = urllib3.PoolManager()
response = http.request(
"GET",
"https://raw.githubusercontent.com/voc/schedule/master/validator/json/schema.json",
)
assert response.status == 200
path = Path(__file__).parent / "../fixtures/schedule.json"
with open(path) as schema:
schema_content = schema.read()
assert response.data.decode() == schema_content


@pytest.mark.django_db
def test_schedule_frab_xml_export(
slot,
Expand Down
Loading