diff --git a/backend/entities/clubs/members/service.go b/backend/entities/clubs/members/service.go index 4e23be33..3c900220 100644 --- a/backend/entities/clubs/members/service.go +++ b/backend/entities/clubs/members/service.go @@ -8,8 +8,8 @@ import ( ) type ClubMemberServiceInterface interface { - GetClubMembers(clubID string, pageInfo fiberpaginate.PageInfo) ([]models.User, error) CreateClubMember(clubID string, userID string) error + GetClubMembers(clubID string, pageInfo fiberpaginate.PageInfo) ([]models.User, error) DeleteClubMember(clubID string, userID string) error } @@ -21,15 +21,6 @@ func NewClubMemberService(params types.ServiceParams) ClubMemberServiceInterface return &ClubMemberService{params} } -func (cms *ClubMemberService) GetClubMembers(clubID string, pageInfo fiberpaginate.PageInfo) ([]models.User, error) { - clubIDAsUUID, err := utilities.ValidateID(clubID) - if err != nil { - return nil, err - } - - return GetClubMembers(cms.DB, *clubIDAsUUID, pageInfo) -} - func (cms *ClubMemberService) CreateClubMember(clubID string, userID string) error { clubIDAsUUID, err := utilities.ValidateID(clubID) if err != nil { @@ -44,6 +35,15 @@ func (cms *ClubMemberService) CreateClubMember(clubID string, userID string) err return CreateClubMember(cms.DB, *clubIDAsUUID, *userIDAsUUID) } +func (cms *ClubMemberService) GetClubMembers(clubID string, pageInfo fiberpaginate.PageInfo) ([]models.User, error) { + clubIDAsUUID, err := utilities.ValidateID(clubID) + if err != nil { + return nil, err + } + + return GetClubMembers(cms.DB, *clubIDAsUUID, pageInfo) +} + func (cms *ClubMemberService) DeleteClubMember(clubID string, userID string) error { clubIDAsUUID, err := utilities.ValidateID(clubID) if err != nil { diff --git a/backend/entities/clubs/transactions.go b/backend/entities/clubs/transactions.go index 44a0daae..2dbf36cb 100644 --- a/backend/entities/clubs/transactions.go +++ b/backend/entities/clubs/transactions.go @@ -44,3 +44,14 @@ func GetAdminIDs(db *gorm.DB, clubID uuid.UUID) ([]uuid.UUID, error) { return adminUUIDs, nil } + +func IsMember(db *gorm.DB, clubID uuid.UUID, userID uuid.UUID) (bool, error) { + db = cache.SetUseCache(db, true) + + var count int64 + if err := db.Table("user_club_members").Where("club_id = ? AND user_id = ?", clubID, userID).Count(&count).Error; err != nil { + return false, err + } + + return count > 0, nil +} diff --git a/backend/entities/events/rsvps/controller.go b/backend/entities/events/rsvps/controller.go index 88ee9592..3ec83b0a 100644 --- a/backend/entities/events/rsvps/controller.go +++ b/backend/entities/events/rsvps/controller.go @@ -1,6 +1,12 @@ package rsvps -import "github.com/gofiber/fiber/v2" +import ( + "net/http" + + "github.com/GenerateNU/sac/backend/utilities" + "github.com/garrettladley/fiberpaginate" + "github.com/gofiber/fiber/v2" +) type Controller struct { service Service @@ -10,14 +16,32 @@ func NewController(service Service) *Controller { return &Controller{service: service} } -func (co *Controller) CreateEventRSVP(c *fiber.Ctx) error { - return nil +func (co *Controller) GetEventRSVPs(c *fiber.Ctx) error { + pageInfo, ok := fiberpaginate.FromContext(c) + if !ok { + return utilities.ErrExpectedPageInfo + } + + followers, err := co.service.GetEventRSVPs(c.Params("eventID"), *pageInfo) + if err != nil { + return err + } + + return c.Status(http.StatusOK).JSON(followers) } -func (co *Controller) GetEventRSVPs(c *fiber.Ctx) error { - return nil +func (co *Controller) CreateEventRSVP(c *fiber.Ctx) error { + if err := co.service.CreateEventRSVP(c.Params("eventID"), c.Params("userID")); err != nil { + return err + } + + return c.SendStatus(http.StatusCreated) } func (co *Controller) DeleteEventRSVP(c *fiber.Ctx) error { - return nil + if err := co.service.DeleteEventRSVP(c.Params("eventID"), c.Params("userID")); err != nil { + return err + } + + return c.SendStatus(http.StatusNoContent) } diff --git a/backend/entities/events/rsvps/service.go b/backend/entities/events/rsvps/service.go index 638450d5..3b874b42 100644 --- a/backend/entities/events/rsvps/service.go +++ b/backend/entities/events/rsvps/service.go @@ -3,6 +3,7 @@ package rsvps import ( "github.com/GenerateNU/sac/backend/entities/models" "github.com/GenerateNU/sac/backend/types" + "github.com/GenerateNU/sac/backend/utilities" "github.com/garrettladley/fiberpaginate" ) @@ -20,17 +21,39 @@ func NewHandler(params types.ServiceParams) Service { return &Handler{params} } -// handle if event is private and user is not member -// handle if event is full, then return appropriate http status code -// to accomplish ^, add new rsvp capacity and waitlist capacity fields to event. they should be optional and provide a reasonable default func (h *Handler) CreateEventRSVP(eventID string, userID string) error { - return nil + eventIDAsUUID, err := utilities.ValidateID(eventID) + if err != nil { + return err + } + + userIDAsUUID, err := utilities.ValidateID(userID) + if err != nil { + return err + } + + return CreateEventRSVP(h.DB, *eventIDAsUUID, *userIDAsUUID) } func (h *Handler) GetEventRSVPs(eventID string, pageInfo fiberpaginate.PageInfo) ([]models.User, error) { - return nil, nil + eventIDAsUUID, err := utilities.ValidateID(eventID) + if err != nil { + return nil, err + } + + return GetEventRSVPs(h.DB, *eventIDAsUUID, pageInfo) } func (h *Handler) DeleteEventRSVP(eventID string, userID string) error { - return nil + eventIDAsUUID, err := utilities.ValidateID(eventID) + if err != nil { + return err + } + + userIDAsUUID, err := utilities.ValidateID(userID) + if err != nil { + return err + } + + return DeleteEventRSVP(h.DB, *eventIDAsUUID, *userIDAsUUID) } diff --git a/backend/entities/events/rsvps/transactions.go b/backend/entities/events/rsvps/transactions.go index 3b6dfc25..da3eaf6d 100644 --- a/backend/entities/events/rsvps/transactions.go +++ b/backend/entities/events/rsvps/transactions.go @@ -1 +1,93 @@ package rsvps + +import ( + "errors" + + "github.com/GenerateNU/sac/backend/database/cache" + "github.com/GenerateNU/sac/backend/entities/clubs" + "github.com/GenerateNU/sac/backend/entities/events" + "github.com/GenerateNU/sac/backend/entities/models" + "github.com/GenerateNU/sac/backend/entities/users" + "github.com/GenerateNU/sac/backend/utilities" + "github.com/garrettladley/fiberpaginate" + "github.com/google/uuid" + "gorm.io/gorm" +) + +func CreateEventRSVP(db *gorm.DB, eventID uuid.UUID, userID uuid.UUID) error { + db = cache.SetUseCache(db, true) + + event, err := events.GetEvent(db, eventID) + if err != nil { + return err + } + + if event.IsArchived { + return utilities.ForbiddenReason(errors.New("cannot RSVP to an archived event")) + } + + if event.IsDraft { + return utilities.ForbiddenReason(errors.New("cannot RSVP to a draft event")) + } + + user, err := users.GetUser(db, userID) + if err != nil { + return err + } + + if !event.IsPublic { + isMember, err := clubs.IsMember(db, *event.Host, userID) + if err != nil { + return err + } + + if !isMember { + return utilities.ForbiddenReason(errors.New("cannot RSVP to a private event when not a member")) + } + } + + if err := db.Model(&user).Association("RSVP").Append(event); err != nil { + return err + } + + return nil +} + +func GetEventRSVPs(db *gorm.DB, eventID uuid.UUID, pageInfo fiberpaginate.PageInfo) ([]models.User, error) { + db = cache.SetUseCache(db, true) + + event, err := events.GetEvent(db, eventID) + if err != nil { + return nil, err + } + + var users []models.User + if err := db.Scopes(utilities.IntoScope(pageInfo, db)).Model(&event).Association("RSVP").Find(&users); err != nil { + return nil, err + } + + return users, nil +} + +func DeleteEventRSVP(db *gorm.DB, eventID uuid.UUID, userID uuid.UUID) error { + db = cache.SetUseCache(db, true) + + user, err := users.GetUser(db, userID) + if err != nil { + return err + } + + event, err := events.GetEvent(db, eventID) + if err != nil { + return err + } + + if err := db.Model(&user).Association("RSVP").Delete(event); err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return utilities.BadRequest(errors.New("user was never RSVPed to this event")) + } + return err + } + + return nil +} diff --git a/backend/utilities/api_error.go b/backend/utilities/api_error.go index 840a0e79..8a18566a 100644 --- a/backend/utilities/api_error.go +++ b/backend/utilities/api_error.go @@ -55,23 +55,27 @@ func BadRequest(err error) APIError { } func InvalidJSON() APIError { - return NewAPIError(http.StatusBadRequest, fmt.Errorf("invalid JSON request data")) + return NewAPIError(http.StatusBadRequest, errors.New("invalid JSON request data")) } func Unauthorized() APIError { - return NewAPIError(http.StatusUnauthorized, fmt.Errorf("unauthorized")) + return NewAPIError(http.StatusUnauthorized, errors.New("unauthorized")) } func Forbidden() APIError { - return NewAPIError(http.StatusForbidden, fmt.Errorf("forbidden")) + return NewAPIError(http.StatusForbidden, errors.New("forbidden")) +} + +func ForbiddenReason(err error) APIError { + return NewAPIError(http.StatusForbidden, err) } func Conflict() APIError { - return NewAPIError(http.StatusConflict, fmt.Errorf("conflict")) + return NewAPIError(http.StatusConflict, errors.New("conflict")) } func InternalServerError() APIError { - return NewAPIError(http.StatusInternalServerError, fmt.Errorf("internal server error")) + return NewAPIError(http.StatusInternalServerError, errors.New("internal server error")) } func ErrorHandler(c *fiber.Ctx, err error) error {