Skip to content

Commit

Permalink
Finished follower endpoints
Browse files Browse the repository at this point in the history
Co-authored-by: Mai Nguyen <[email protected]>
  • Loading branch information
zacklassetter and in-mai-space committed Feb 2, 2024
1 parent 6e25748 commit aff5bb7
Show file tree
Hide file tree
Showing 11 changed files with 331 additions and 2 deletions.
10 changes: 10 additions & 0 deletions backend/src/controllers/club.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,13 @@ func (l *ClubController) DeleteClub(c *fiber.Ctx) error {

return c.SendStatus(fiber.StatusNoContent)
}

func (l *ClubController) GetUserFollowersForClub(c *fiber.Ctx) error {

clubs, err := l.clubService.GetUserFollowersForClub(c.Params("id"))
if err != nil {
return err.FiberError(c)
}

return c.Status(fiber.StatusOK).JSON(&clubs)
}
46 changes: 46 additions & 0 deletions backend/src/controllers/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,3 +137,49 @@ func (u *UserController) DeleteUser(c *fiber.Ctx) error {

return c.SendStatus(fiber.StatusNoContent)
}

func (u *UserController) GetUserTags(c *fiber.Ctx) error {
tags, err := u.userService.GetUserTags(c.Params("uid"))
if err != nil {
return err.FiberError(c)
}
return c.Status(fiber.StatusOK).JSON(&tags)
}

func (u *UserController) CreateUserTags(c *fiber.Ctx) error {
var requestBody models.CreateUserTagsBody
if err := c.BodyParser(&requestBody); err != nil {
return errors.FailedToParseRequestBody.FiberError(c)
}

tags, err := u.userService.CreateUserTags(c.Params("uid"), requestBody)
if err != nil {
return err.FiberError(c)
}
return c.Status(fiber.StatusCreated).JSON(&tags)
}

func (u *UserController) CreateFollowing(c *fiber.Ctx) error {
err := u.userService.CreateFollowing(c.Params("user_id"), c.Params("club_id"))
if err != nil {
return err.FiberError(c)
}
return c.SendStatus(fiber.StatusCreated)
}

func (u *UserController) DeleteFollowing(c *fiber.Ctx) error {

err := u.userService.DeleteFollowing(c.Params("user_id"), c.Params("club_id"))
if err != nil {
return err.FiberError(c)
}
return c.SendStatus(fiber.StatusNoContent)
}

func (u *UserController) GetAllFollowing(c *fiber.Ctx) error {
clubs, err := u.userService.GetFollowing(c.Params("user_id"))
if err != nil {
return err.FiberError(c)
}
return c.Status(fiber.StatusOK).JSON(clubs)
}
4 changes: 4 additions & 0 deletions backend/src/errors/club.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,8 @@ var (
StatusCode: fiber.StatusInternalServerError,
Message: "failed to get admin ids",
}
FailedToGetClubFollowers = Error{
StatusCode: fiber.StatusInternalServerError,
Message: "failed to get club followers",
}
)
4 changes: 4 additions & 0 deletions backend/src/errors/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,4 +43,8 @@ var (
StatusCode: fiber.StatusInternalServerError,
Message: "failed to compute password hash",
}
FailedToGetUserFollowing = Error{
StatusCode: fiber.StatusInternalServerError,
Message: "failed to get user following",
}
)
2 changes: 1 addition & 1 deletion backend/src/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ type User struct {

Tag []Tag `gorm:"many2many:user_tags;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
Member []Club `gorm:"many2many:user_club_members;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
Follower []Club `gorm:"many2many:user_club_followers;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
Follower []Club `gorm:"many2many:user_club_followers;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"clubs_followed,omitempty" validate:"-"`
IntendedApplicant []Club `gorm:"many2many:user_club_intended_applicants;constraint:OnUpdate:CASCADE,OnDelete:CASCADE;" json:"-" validate:"-"`
Asked []Comment `gorm:"foreignKey:AskedByID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"-" validate:"-"`
Answered []Comment `gorm:"foreignKey:AnsweredByID;constraint:OnUpdate:CASCADE,OnDelete:SET NULL;" json:"-" validate:"-"`
Expand Down
12 changes: 12 additions & 0 deletions backend/src/server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,14 @@ func userRoutes(router fiber.Router, userService services.UserServiceInterface,
users.Patch("/:id", userController.UpdateUser)
users.Delete("/:id", userController.DeleteUser)

users.Put("/:user_id/follower/:club_id", userController.CreateFollowing)
users.Delete("/:user_id/follower/:club_id", userController.DeleteFollowing)
users.Get("/:user_id/follower", userController.GetAllFollowing)

userTags := users.Group("/:uid/tags")

userTags.Post("/", userController.CreateUserTags)
userTags.Get("/", userController.GetUserTags)
return users
}

Expand Down Expand Up @@ -119,6 +127,10 @@ func clubRoutes(router fiber.Router, clubService services.ClubServiceInterface,
clubsID.Get("/", clubController.GetClub)
clubsID.Patch("/", middlewareService.Authorize(types.ClubWrite), clubController.UpdateClub)
clubsID.Delete("/", middlewareService.Authorize(types.ClubDelete), clubController.DeleteClub)
clubs.Get("/:id", clubController.GetClub)
clubs.Patch("/:id", clubController.UpdateClub)
clubs.Delete("/:id", clubController.DeleteClub)
clubs.Get("/:id/follower", clubController.GetUserFollowersForClub)
}

func authRoutes(router fiber.Router, authService services.AuthServiceInterface, authSettings config.AuthSettings) {
Expand Down
9 changes: 9 additions & 0 deletions backend/src/services/club.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ type ClubServiceInterface interface {
CreateClub(clubBody models.CreateClubRequestBody) (*models.Club, *errors.Error)
UpdateClub(id string, clubBody models.UpdateClubRequestBody) (*models.Club, *errors.Error)
DeleteClub(id string) *errors.Error
GetUserFollowersForClub(id string) ([]models.User, *errors.Error)
}

type ClubService struct {
Expand Down Expand Up @@ -91,3 +92,11 @@ func (c *ClubService) DeleteClub(id string) *errors.Error {

return transactions.DeleteClub(c.DB, *idAsUUID)
}

func (c *ClubService) GetUserFollowersForClub(id string) ([]models.User, *errors.Error) {
idAsUUID, err := utilities.ValidateID(id)
if err != nil {
return nil, &errors.FailedToValidateID
}
return transactions.GetUserFollowersForClub(c.DB, *idAsUUID)
}
69 changes: 69 additions & 0 deletions backend/src/services/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ type UserServiceInterface interface {
GetUser(id string) (*models.User, *errors.Error)
UpdateUser(id string, userBody models.UpdateUserRequestBody) (*models.User, *errors.Error)
DeleteUser(id string) *errors.Error
GetUserTags(id string) ([]models.Tag, *errors.Error)
CreateUserTags(id string, tagIDs models.CreateUserTagsBody) ([]models.Tag, *errors.Error)
CreateFollowing(userId string, clubId string) *errors.Error
DeleteFollowing(userId string, clubId string) *errors.Error
GetFollowing(userId string) ([]models.Club, *errors.Error)
}

type UserService struct {
Expand Down Expand Up @@ -109,3 +114,67 @@ func (u *UserService) DeleteUser(id string) *errors.Error {

return transactions.DeleteUser(u.DB, *idAsUUID)
}

func (u *UserService) GetUserTags(id string) ([]models.Tag, *errors.Error) {
idAsUUID, err := utilities.ValidateID(id)
if err != nil {
return nil, err
}

return transactions.GetUserTags(u.DB, *idAsUUID)
}

func (u *UserService) CreateUserTags(id string, tagIDs models.CreateUserTagsBody) ([]models.Tag, *errors.Error) {
// Validate the id:
idAsUUID, err := utilities.ValidateID(id)
if err != nil {
return nil, err
}

if err := u.Validate.Struct(tagIDs); err != nil {
return nil, &errors.FailedToValidateUserTags
}

// Retrieve a list of valid tags from the ids:
tags, err := transactions.GetTagsByIDs(u.DB, tagIDs.Tags)

if err != nil {
return nil, err
}

// Update the user to reflect the new tags:
return transactions.CreateUserTags(u.DB, *idAsUUID, tags)
}

func (u *UserService) CreateFollowing(userId string, clubId string) *errors.Error {
userIdAsUUID, err := utilities.ValidateID(userId)
if err != nil {
return err
}
clubIdAsUUID, err := utilities.ValidateID(clubId)
if err != nil {
return err
}
return transactions.CreateFollowing(u.DB, *userIdAsUUID, *clubIdAsUUID)
}

func (u *UserService) DeleteFollowing(userId string, clubId string) *errors.Error {
userIdAsUUID, err := utilities.ValidateID(userId)
if err != nil {
return err
}
clubIdAsUUID, err := utilities.ValidateID(clubId)
if err != nil {
return err
}
return transactions.DeleteFollowing(u.DB, *userIdAsUUID, *clubIdAsUUID)
}

func (u *UserService) GetFollowing(userId string) ([]models.Club, *errors.Error) {
userIdAsUUID, err := utilities.ValidateID(userId)
if err != nil {
return nil, err
}

return transactions.GetClubFollowing(u.DB, *userIdAsUUID)
}
14 changes: 13 additions & 1 deletion backend/src/transactions/club.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,18 @@ func DeleteClub(db *gorm.DB, id uuid.UUID) *errors.Error {
return &errors.FailedToDeleteClub
}
}

return nil
}

func GetUserFollowersForClub(db *gorm.DB, club_id uuid.UUID) ([]models.User, *errors.Error) {
var users []models.User
club, err := GetClub(db, club_id)
if err != nil {
return nil, &errors.ClubNotFound
}

if err := db.Model(&club).Association("Follower").Find(&users); err != nil {
return nil, &errors.FailedToGetClubFollowers
}
return users, nil
}
65 changes: 65 additions & 0 deletions backend/src/transactions/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,19 @@ func GetUser(db *gorm.DB, id uuid.UUID) (*models.User, *errors.Error) {
return &user, nil
}

func GetUserWithFollowers(db *gorm.DB, id uuid.UUID) (*models.User, *errors.Error) {
var user models.User
if err := db.Preload("Follower").Omit("password_hash").First(&user, id).Error; err != nil {
if stdliberrors.Is(err, gorm.ErrRecordNotFound) {
return nil, &errors.UserNotFound
} else {
return nil, &errors.FailedToGetUser
}
}

return &user, nil
}

func UpdateUser(db *gorm.DB, id uuid.UUID, user models.User) (*models.User, *errors.Error) {
var existingUser models.User

Expand Down Expand Up @@ -85,3 +98,55 @@ func DeleteUser(db *gorm.DB, id uuid.UUID) *errors.Error {
}
return nil
}

// Create following for a user
func CreateFollowing(db *gorm.DB, userId uuid.UUID, clubId uuid.UUID) *errors.Error {

user, err := GetUserWithFollowers(db, userId)
if err != nil {
return &errors.UserNotFound
}
club, err := GetClub(db, clubId)
if err != nil {
return &errors.ClubNotFound
}

if err := db.Model(&user).Association("Follower").Replace(append(user.Follower, *club)); err != nil {
return &errors.FailedToUpdateUser
}
return nil
}

// Delete following for a user
func DeleteFollowing(db *gorm.DB, userId uuid.UUID, clubId uuid.UUID) *errors.Error {
user, err := GetUser(db, userId)
if err != nil {
return &errors.UserNotFound
}
club, err := GetClub(db, clubId)
if err != nil {
return &errors.ClubNotFound
}
//What to return here?
//Should we return User or Success message?
if err := db.Model(&user).Association("Follower").Delete(club); err != nil {
return &errors.FailedToUpdateUser
}
return nil
}

// Get all following for a user

func GetClubFollowing(db *gorm.DB, userId uuid.UUID) ([]models.Club, *errors.Error) {
var clubs []models.Club

user, err := GetUser(db, userId)
if err != nil {
return nil, &errors.UserNotFound
}

if err := db.Model(&user).Association("Follower").Find(&clubs); err != nil {
return nil, &errors.FailedToGetUserFollowing
}
return clubs, nil
}
Loading

0 comments on commit aff5bb7

Please sign in to comment.