Skip to content

Commit

Permalink
Edit Unavailability Endpoint (#206)
Browse files Browse the repository at this point in the history
## Describe your changes
The logic for updating unavailability is simple. Updating any field in
the unavailability with the matching fields in the payload. The most
important part is the payload validation, the payload data is checked in
the repository function and raise custom errors to catch later in the
controller. Some features:
- a user can only edit their own unavailability (this feature will be
later discussed within the meeting to see if it should be applied for
other API endpoints)
- custom errors (EventNotFoundError, InvalidArgumentError)
## Issue ticket number and link
  • Loading branch information
TaiHaDev authored Apr 7, 2024
1 parent 81f451b commit b3d7951
Show file tree
Hide file tree
Showing 6 changed files with 66 additions and 34 deletions.
26 changes: 17 additions & 9 deletions controllers/v2/unavailability/api.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from flask_restful import reqparse, Resource, marshal_with, inputs

from .response_models import volunteer_unavailability_time
from domain import UserType
from domain import UserType, session_scope
from repository.volunteer_unavailability_v2 import EventRepository
from services.jwk import requires_auth, is_user_or_has_role
from controllers.v2.v2_blueprint import v2_api

import logging
from exception import EventNotFoundError, InvalidArgumentError
edit_parser = reqparse.RequestParser()
edit_parser.add_argument("title", type=str)
edit_parser.add_argument("start", type=inputs.datetime_from_iso8601)
Expand All @@ -22,14 +23,21 @@ def __init__(self, event_repository: EventRepository = EventRepository()):
@requires_auth
@is_user_or_has_role(None, UserType.ROOT_ADMIN)
def put(self, user_id, event_id):
args = edit_parser.parse_args()
success = self.event_repository.edit_event(user_id, event_id, **args)
if success is True:
try:
with session_scope() as session:
args = edit_parser.parse_args()
self.event_repository.edit_event(session, user_id, event_id, **args)
return {"message": "Updated successfully"}, 200
elif success is False:
return {"message": "Event not found"}, 404
else:
return {"message": "Unexpected Error Occurred"}, 400
except InvalidArgumentError as argumentException:
logging.warning(argumentException)
return {"message": "Invalid argument from the payload"}, 400
except EventNotFoundError as notFoundError:
logging.warning(notFoundError)
return {"message": f"Event {event_id} can not be found"}, 404
except Exception as ex:
logging.error(ex)
return {"message": "Unexpected error happened within the database"}, 500


@requires_auth
@is_user_or_has_role(None, UserType.ROOT_ADMIN)
Expand Down
2 changes: 2 additions & 0 deletions exception/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .client_exception import EventNotFoundError, InvalidArgumentError

20 changes: 20 additions & 0 deletions exception/client_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from .fireapp_exception import FireAppException


class EventNotFoundError(FireAppException):
def __init__(self, event_id, *args):
super().__init__(f"Event not found", *args)
self.event_id = event_id

def __str__(self):
# Optionally customize the string representation for this specific error
return f"{self.message}: Event ID {self.event_id} could not be located."


class InvalidArgumentError(FireAppException):
def __init__(self, *args):
super().__init__(f"Invalid argument(s)", *args)

def __str__(self):
# Optionally customize the string representation for this specific error
return f"{self.message}: unexpected values in the payload"
Empty file added exception/database_exception.py
Empty file.
3 changes: 3 additions & 0 deletions exception/fireapp_exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class FireAppException(Exception):
pass

49 changes: 24 additions & 25 deletions repository/volunteer_unavailability_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,36 @@

from flask import jsonify

from datetime import datetime

from datetime import datetime, timezone
from exception import EventNotFoundError, InvalidArgumentError
from domain import UnavailabilityTime, session_scope



class EventRepository:
def __init__(self):
pass

def edit_event(self, userId, eventId, title=None, start=None, end=None, periodicity=None):
with session_scope() as session:
try:
event = session.query(UnavailabilityTime).filter(UnavailabilityTime.eventId == eventId,
UnavailabilityTime.userId == userId).first()
if event is None:
return False
if title is not None:
event.title = title
if start is not None:
event.start = start
if end is not None:
event.end = end
if end is not None:
event.periodicity = periodicity
session.commit()
return True
except Exception as e:
session.rollback()
logging.error(e)
return None
def edit_event(self, session, userId, eventId, title=None, start=None, end=None, periodicity=None):
now = datetime.now(timezone.utc)
event = session.query(UnavailabilityTime).filter(UnavailabilityTime.eventId == eventId,
UnavailabilityTime.userId == userId).first()
if event is None:
raise EventNotFoundError(eventId)
# validate user input
if start is not None and start < now:
raise InvalidArgumentError()
if end is not None and (end < now or end < start):
raise InvalidArgumentError()
# Edit fields with new values
if title is not None:
event.title = title
if start is not None:
event.start = start
if end is not None:
event.end = end
if end is not None:
event.periodicity = periodicity
session.commit()

def get_event(self, userId):
"""
Expand Down Expand Up @@ -121,4 +120,4 @@ def check_overlapping_events(self, userId, startTime, endTime, periodicity):
# Add any other attributes you need
})

return overlapping_details
return overlapping_details

0 comments on commit b3d7951

Please sign in to comment.