Skip to content

Commit

Permalink
Add a few more exceptions and finish off schedule.py
Browse files Browse the repository at this point in the history
  • Loading branch information
MelissaAutumn committed Dec 21, 2023
1 parent 4bae60f commit 08a6267
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 61 deletions.
32 changes: 32 additions & 0 deletions backend/src/appointment/exceptions/validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ def get_msg(self):
return l10n('protected-route-fail')


class InvalidLinkException(APIException):
"""Raise when verify_subscriber_link fails"""
status_code = 400

def get_msg(self):
return l10n('invalid-link')


class SubscriberNotFoundException(APIException):
"""Raise when the calendar is not found during route validation"""
status_code = 404
Expand Down Expand Up @@ -87,6 +95,30 @@ def get_msg(self):
return l10n('schedule-not-auth')


class SlotNotFoundException(APIException):
"""Raise when a timeslot is not found during route validation"""
status_code = 404

def get_msg(self):
return l10n('slot-not-found')


class SlotAlreadyTakenException(APIException):
"""Raise when a timeslot is already taken during route validation"""
status_code = 403

def get_msg(self):
return l10n('slot-already-taken')


class SlotNotAuthorizedException(APIException):
"""Raise when a slot is owned by someone else during route validation"""
status_code = 403

def get_msg(self):
return l10n('slot-not-auth')


class ZoomNotConnectedException(APIException):
"""Raise if the user requires a zoom connection during route validation"""
status_code = 400
Expand Down
1 change: 1 addition & 0 deletions backend/src/appointment/l10n/en/main.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ username-not-available = This username has already been taken.
invalid-link = This link is no longer valid.
calendar-sync-fail = An error occurred while syncing calendars. Please try again later.
calendar-not-active = The calendar connection is not active.
slot-not-found = There are no available time slots to book.
slot-already-taken = The time slot you have selected is no longer available. Please try again.
slot-invalid-email = The email you have provided was not valid. Please try again.
Expand Down
64 changes: 33 additions & 31 deletions backend/src/appointment/routes/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
from ..dependencies.auth import get_subscriber
from ..dependencies.database import get_db
from ..dependencies.zoom import get_zoom_client
from ..exceptions.validation import CalendarNotFoundException, CalendarNotAuthorizedException, AppointmentNotFoundException, \
AppointmentNotAuthorizedException, SubscriberNotFoundException, ZoomNotConnectedException, CalendarNotConnectedException
from ..exceptions import validation
from ..l10n import l10n

router = APIRouter()
Expand Down Expand Up @@ -100,7 +99,7 @@ def verify_signature(url: str = Body(..., embed=True), db: Session = Depends(get
if repo.verify_subscriber_link(db, url):
return True

raise HTTPException(400, l10n('invalid-link'))
raise validation.InvalidLinkException()


@router.post("/cal", response_model=schemas.CalendarOut)
Expand All @@ -124,9 +123,9 @@ def read_my_calendar(id: int, db: Session = Depends(get_db), subscriber: Subscri
cal = repo.get_calendar(db, calendar_id=id)

if cal is None:
raise CalendarNotFoundException()
raise validation.CalendarNotFoundException()
if not repo.calendar_is_owned(db, calendar_id=id, subscriber_id=subscriber.id):
raise CalendarNotAuthorizedException()
raise validation.CalendarNotAuthorizedException()

return schemas.CalendarConnectionOut(
id=cal.id,
Expand All @@ -148,9 +147,9 @@ def update_my_calendar(
):
"""endpoint to update an existing calendar connection for authenticated subscriber"""
if not repo.calendar_exists(db, calendar_id=id):
raise CalendarNotFoundException()
raise validation.CalendarNotFoundException()
if not repo.calendar_is_owned(db, calendar_id=id, subscriber_id=subscriber.id):
raise CalendarNotAuthorizedException()
raise validation.CalendarNotAuthorizedException()

cal = repo.update_subscriber_calendar(db=db, calendar=calendar, calendar_id=id)
return schemas.CalendarOut(id=cal.id, title=cal.title, color=cal.color, connected=cal.connected)
Expand All @@ -164,9 +163,9 @@ def connect_my_calendar(
):
"""endpoint to update an existing calendar connection for authenticated subscriber"""
if not repo.calendar_exists(db, calendar_id=id):
raise CalendarNotFoundException()
raise validation.CalendarNotFoundException()
if not repo.calendar_is_owned(db, calendar_id=id, subscriber_id=subscriber.id):
raise CalendarNotAuthorizedException()
raise validation.CalendarNotAuthorizedException()

try:
cal = repo.update_subscriber_calendar_connection(db=db, calendar_id=id, is_connected=True)
Expand All @@ -179,9 +178,9 @@ def connect_my_calendar(
def delete_my_calendar(id: int, db: Session = Depends(get_db), subscriber: Subscriber = Depends(get_subscriber)):
"""endpoint to remove a calendar from db"""
if not repo.calendar_exists(db, calendar_id=id):
raise CalendarNotFoundException()
raise validation.CalendarNotFoundException()
if not repo.calendar_is_owned(db, calendar_id=id, subscriber_id=subscriber.id):
raise CalendarNotAuthorizedException()
raise validation.CalendarNotAuthorizedException()

cal = repo.delete_subscriber_calendar(db=db, calendar_id=id)
return schemas.CalendarOut(id=cal.id, title=cal.title, color=cal.color, connected=cal.connected)
Expand Down Expand Up @@ -246,7 +245,7 @@ def read_remote_events(
db_calendar = repo.get_calendar(db, calendar_id=id)

if db_calendar is None:
raise CalendarNotFoundException()
raise validation.CalendarNotFoundException()

if db_calendar.provider == CalendarProvider.google:
con = GoogleConnector(
Expand All @@ -271,13 +270,13 @@ def create_my_calendar_appointment(
):
"""endpoint to add a new appointment with slots for a given calendar"""
if not repo.calendar_exists(db, calendar_id=a_s.appointment.calendar_id):
raise CalendarNotFoundException()
raise validation.CalendarNotFoundException()
if not repo.calendar_is_owned(db, calendar_id=a_s.appointment.calendar_id, subscriber_id=subscriber.id):
raise CalendarNotAuthorizedException()
raise validation.CalendarNotAuthorizedException()
if not repo.calendar_is_connected(db, calendar_id=a_s.appointment.calendar_id):
raise CalendarNotConnectedException()
raise validation.CalendarNotConnectedException()
if a_s.appointment.meeting_link_provider == MeetingLinkProviderType.zoom and subscriber.get_external_connection(ExternalConnectionType.zoom) is None:
raise ZoomNotConnectedException()
raise validation.ZoomNotConnectedException()
return repo.create_calendar_appointment(db=db, appointment=a_s.appointment, slots=a_s.slots)


Expand All @@ -287,9 +286,9 @@ def read_my_appointment(id: str, db: Session = Depends(get_db), subscriber: Subs
db_appointment = repo.get_appointment(db, appointment_id=id)

if db_appointment is None:
raise AppointmentNotFoundException()
raise validation.AppointmentNotFoundException()
if not repo.appointment_is_owned(db, appointment_id=id, subscriber_id=subscriber.id):
raise AppointmentNotAuthorizedException()
raise validation.AppointmentNotAuthorizedException()

return db_appointment

Expand All @@ -305,9 +304,9 @@ def update_my_appointment(
db_appointment = repo.get_appointment(db, appointment_id=id)

if db_appointment is None:
raise AppointmentNotFoundException()
raise validation.AppointmentNotFoundException()
if not repo.appointment_is_owned(db, appointment_id=id, subscriber_id=subscriber.id):
raise AppointmentNotAuthorizedException()
raise validation.AppointmentNotAuthorizedException()

return repo.update_calendar_appointment(db=db, appointment=a_s.appointment, slots=a_s.slots, appointment_id=id)

Expand All @@ -318,9 +317,9 @@ def delete_my_appointment(id: int, db: Session = Depends(get_db), subscriber: Su
db_appointment = repo.get_appointment(db, appointment_id=id)

if db_appointment is None:
raise AppointmentNotFoundException()
raise validation.AppointmentNotFoundException()
if not repo.appointment_is_owned(db, appointment_id=id, subscriber_id=subscriber.id):
raise AppointmentNotAuthorizedException()
raise validation.AppointmentNotAuthorizedException()

return repo.delete_calendar_appointment(db=db, appointment_id=id)

Expand All @@ -330,10 +329,10 @@ def read_public_appointment(slug: str, db: Session = Depends(get_db)):
"""endpoint to retrieve an appointment from db via public link and only expose necessary data"""
a = repo.get_public_appointment(db, slug=slug)
if a is None:
raise AppointmentNotFoundException()
raise validation.AppointmentNotFoundException()
s = repo.get_subscriber_by_appointment(db=db, appointment_id=a.id)
if s is None:
raise SubscriberNotFoundException()
raise validation.SubscriberNotFoundException()
slots = [
schemas.SlotOut(id=sl.id, start=sl.start, duration=sl.duration, attendee_id=sl.attendee_id) for sl in a.slots
]
Expand All @@ -352,14 +351,14 @@ def update_public_appointment_slot(
"""endpoint to update a time slot for an appointment via public link and create an event in remote calendar"""
db_appointment = repo.get_public_appointment(db, slug=slug)
if db_appointment is None:
raise AppointmentNotFoundException()
raise validation.AppointmentNotFoundException()
db_calendar = repo.get_calendar(db, calendar_id=db_appointment.calendar_id)
if db_calendar is None:
raise CalendarNotFoundException()
raise validation.CalendarNotFoundException()
if not repo.appointment_has_slot(db, appointment_id=db_appointment.id, slot_id=s_a.slot_id):
raise HTTPException(status_code=404, detail=l10n('slot-not-found'))
raise validation.SlotNotFoundException()
if not repo.slot_is_available(db, slot_id=s_a.slot_id):
raise HTTPException(status_code=403, detail=l10n('slot-already-taken'))
raise validation.SlotAlreadyTakenException()
if not validators.email(s_a.attendee.email):
raise HTTPException(status_code=400, detail=l10n('slot-invalid-email'))

Expand Down Expand Up @@ -441,12 +440,15 @@ def public_appointment_serve_ics(slug: str, slot_id: int, db: Session = Depends(
"""endpoint to serve ICS file for time slot to download"""
db_appointment = repo.get_public_appointment(db, slug=slug)
if db_appointment is None:
raise AppointmentNotFoundException()
raise validation.AppointmentNotFoundException()

if not repo.appointment_has_slot(db, appointment_id=db_appointment.id, slot_id=slot_id):
raise HTTPException(status_code=404, detail=l10n('slot-not-auth'))
raise validation.SlotNotFoundException()

slot = repo.get_slot(db=db, slot_id=slot_id)
if slot is None:
raise HTTPException(status_code=404, detail=l10n('slot-not-found'))
raise validation.SlotNotFoundException()

organizer = repo.get_subscriber_by_appointment(db=db, appointment_id=db_appointment.id)

return schemas.FileDownload(
Expand Down
Loading

0 comments on commit 08a6267

Please sign in to comment.