From aff5bb743a2ea9705d33bfa8c53a4253d6715be1 Mon Sep 17 00:00:00 2001 From: Zack Lassetter Date: Fri, 2 Feb 2024 17:35:39 -0500 Subject: [PATCH 1/8] Finished follower endpoints Co-authored-by: Mai Nguyen --- backend/src/controllers/club.go | 10 ++++ backend/src/controllers/user.go | 46 +++++++++++++++ backend/src/errors/club.go | 4 ++ backend/src/errors/user.go | 4 ++ backend/src/models/user.go | 2 +- backend/src/server/server.go | 12 ++++ backend/src/services/club.go | 9 +++ backend/src/services/user.go | 69 ++++++++++++++++++++++ backend/src/transactions/club.go | 14 ++++- backend/src/transactions/user.go | 65 +++++++++++++++++++++ backend/tests/api/user_test.go | 98 ++++++++++++++++++++++++++++++++ 11 files changed, 331 insertions(+), 2 deletions(-) diff --git a/backend/src/controllers/club.go b/backend/src/controllers/club.go index 4eab57c55..722738801 100644 --- a/backend/src/controllers/club.go +++ b/backend/src/controllers/club.go @@ -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) +} diff --git a/backend/src/controllers/user.go b/backend/src/controllers/user.go index 1b509bf3d..1edf8d31c 100644 --- a/backend/src/controllers/user.go +++ b/backend/src/controllers/user.go @@ -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) +} diff --git a/backend/src/errors/club.go b/backend/src/errors/club.go index 7b05d03bd..725aa68fa 100644 --- a/backend/src/errors/club.go +++ b/backend/src/errors/club.go @@ -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", + } ) diff --git a/backend/src/errors/user.go b/backend/src/errors/user.go index de07afbb7..dbc70d6d8 100644 --- a/backend/src/errors/user.go +++ b/backend/src/errors/user.go @@ -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", + } ) diff --git a/backend/src/models/user.go b/backend/src/models/user.go index 46eee1311..09ac25b9d 100644 --- a/backend/src/models/user.go +++ b/backend/src/models/user.go @@ -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:"-"` diff --git a/backend/src/server/server.go b/backend/src/server/server.go index 3b892d65f..a3dadca7d 100644 --- a/backend/src/server/server.go +++ b/backend/src/server/server.go @@ -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 } @@ -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) { diff --git a/backend/src/services/club.go b/backend/src/services/club.go index c05610c1f..011a1f6fa 100644 --- a/backend/src/services/club.go +++ b/backend/src/services/club.go @@ -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 { @@ -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) +} diff --git a/backend/src/services/user.go b/backend/src/services/user.go index 646976e09..d2585982e 100644 --- a/backend/src/services/user.go +++ b/backend/src/services/user.go @@ -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 { @@ -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) +} diff --git a/backend/src/transactions/club.go b/backend/src/transactions/club.go index a7fe4495c..b83e52bce 100644 --- a/backend/src/transactions/club.go +++ b/backend/src/transactions/club.go @@ -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 +} diff --git a/backend/src/transactions/user.go b/backend/src/transactions/user.go index 14d52e2f5..3f31a5b60 100644 --- a/backend/src/transactions/user.go +++ b/backend/src/transactions/user.go @@ -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 @@ -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 +} diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go index a1f74a8c8..c17106775 100644 --- a/backend/tests/api/user_test.go +++ b/backend/tests/api/user_test.go @@ -550,3 +550,101 @@ func TestCreateUserFailsOnMissingFields(t *testing.T) { } appAssert.Close() } + +// test create user following works +func TestCreateUserFollowingWorks(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatusAndDB(t, &appAssert, + DBTesterWithStatus{ + Status: fiber.StatusCreated, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var dbUser models.User + err := app.Conn.Preload("Follower").First(&dbUser, userUUID).Error + assert.NilError(err) + + assert.Equal(len(dbUser.Follower), 1) + }, + }, + ) + appAssert.Close() +} + +func TestCreateUserFollowingFailsClubIdBadRequest(t *testing.T) { + appAssert, userUUID := CreateSampleUser(t, nil) + + badRequests := []string{ + "0", + "-1", + "1.1", + "foo", + "null", + } + + for _, badRequest := range badRequests { + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, badRequest), + }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() + } +} + +func TestCreateUserFollowingFailsUserIdBadRequest(t *testing.T) { + appAssert, _, clubUUID := CreateSampleClub(t, nil) + + badRequests := []string{ + "0", + "-1", + "1.1", + "foo", + "null", + } + + for _, badRequest := range badRequests { + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", badRequest, clubUUID), + }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() + } +} + +func TestCreateUserFollowingFailsUserNotExist(t *testing.T) { + appAssert, _, clubUUID := CreateSampleClub(t, nil) + userUUIDNotExist := uuid.New() + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUIDNotExist, clubUUID), + }.TestOnErrorAndDB(t, &appAssert, + ErrorWithDBTester{ + Error: errors.UserNotFound, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var user models.User + err := app.Conn.Where("id = ?", userUUIDNotExist).First(&user).Error + assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) + }, + }, + ).Close() +} + +func TestCreateUserFollowingFailsClubNotExist(t *testing.T) { + appAssert, userUUID := CreateSampleUser(t, nil) + clubUUIDNotExist := uuid.New() + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUIDNotExist), + }.TestOnErrorAndDB(t, &appAssert, + ErrorWithDBTester{ + Error: errors.ClubNotFound, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var club models.Club + err := app.Conn.Where("id = ?", clubUUIDNotExist).First(&club).Error + assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) + }, + }, + ).Close() +} From 347310dadb39696e77e4c662b4b265f304a19112 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Fri, 2 Feb 2024 18:38:18 -0500 Subject: [PATCH 2/8] refactor associations into seperate verticals | gen clean up --- backend/src/controllers/category.go | 20 ++-- backend/src/controllers/category_tag.go | 8 +- backend/src/controllers/club.go | 30 ++---- backend/src/controllers/club_follower.go | 23 +++++ backend/src/controllers/user.go | 46 --------- backend/src/controllers/user_follower.go | 38 ++++++++ backend/src/controllers/user_tag.go | 8 +- backend/src/middleware/club.go | 2 +- backend/src/server/server.go | 41 +++++--- backend/src/services/club_follower.go | 32 +++++++ backend/src/services/user.go | 37 ------- backend/src/services/user_follower.go | 58 +++++++++++ backend/src/transactions/club_follower.go | 21 ++++ backend/src/transactions/user.go | 52 ---------- backend/src/transactions/user_follower.go | 59 ++++++++++++ backend/tests/api/club_follower_test.go | 3 + backend/tests/api/user_follower_test.go | 112 ++++++++++++++++++++++ backend/tests/api/user_test.go | 98 ------------------- 18 files changed, 403 insertions(+), 285 deletions(-) create mode 100644 backend/src/controllers/club_follower.go create mode 100644 backend/src/controllers/user_follower.go create mode 100644 backend/src/services/club_follower.go create mode 100644 backend/src/services/user_follower.go create mode 100644 backend/src/transactions/club_follower.go create mode 100644 backend/src/transactions/user_follower.go create mode 100644 backend/tests/api/club_follower_test.go create mode 100644 backend/tests/api/user_follower_test.go diff --git a/backend/src/controllers/category.go b/backend/src/controllers/category.go index 317e312bc..3e171bf35 100644 --- a/backend/src/controllers/category.go +++ b/backend/src/controllers/category.go @@ -31,14 +31,14 @@ func NewCategoryController(categoryService services.CategoryServiceInterface) *C // @Failure 400 {string} string "category with that name already exists" // @Failure 500 {string} string "failed to create category" // @Router /api/v1/category/ [post] -func (t *CategoryController) CreateCategory(c *fiber.Ctx) error { +func (cat *CategoryController) CreateCategory(c *fiber.Ctx) error { var categoryBody models.CategoryRequestBody if err := c.BodyParser(&categoryBody); err != nil { return errors.FailedToParseRequestBody.FiberError(c) } - newCategory, err := t.categoryService.CreateCategory(categoryBody) + newCategory, err := cat.categoryService.CreateCategory(categoryBody) if err != nil { return err.FiberError(c) } @@ -56,11 +56,11 @@ func (t *CategoryController) CreateCategory(c *fiber.Ctx) error { // @Success 200 {object} []models.Category // @Failure 500 {string} string "unable to retrieve categories" // @Router /api/v1/category/ [get] -func (t *CategoryController) GetCategories(c *fiber.Ctx) error { +func (cat *CategoryController) GetCategories(c *fiber.Ctx) error { defaultLimit := 10 defaultPage := 1 - categories, err := t.categoryService.GetCategories(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage))) + categories, err := cat.categoryService.GetCategories(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage))) if err != nil { return err.FiberError(c) } @@ -80,8 +80,8 @@ func (t *CategoryController) GetCategories(c *fiber.Ctx) error { // @Failure 404 {string} string "faied to find category" // @Failure 500 {string} string "failed to retrieve category" // @Router /api/v1/category/{id} [get] -func (t *CategoryController) GetCategory(c *fiber.Ctx) error { - category, err := t.categoryService.GetCategory(c.Params("id")) +func (cat *CategoryController) GetCategory(c *fiber.Ctx) error { + category, err := cat.categoryService.GetCategory(c.Params("id")) if err != nil { return err.FiberError(c) } @@ -101,8 +101,8 @@ func (t *CategoryController) GetCategory(c *fiber.Ctx) error { // @Failure 404 {string} string "failed to find category" // @Failure 500 {string} string "failed to delete category" // @Router /api/v1/category/{id} [delete] -func (t *CategoryController) DeleteCategory(c *fiber.Ctx) error { - if err := t.categoryService.DeleteCategory(c.Params("id")); err != nil { +func (cat *CategoryController) DeleteCategory(c *fiber.Ctx) error { + if err := cat.categoryService.DeleteCategory(c.Params("id")); err != nil { return err.FiberError(c) } @@ -121,14 +121,14 @@ func (t *CategoryController) DeleteCategory(c *fiber.Ctx) error { // @Failure 404 {string} string "failed to find category" // @Failure 500 {string} string "failed to update category" // @Router /api/v1/category/{id} [patch] -func (t *CategoryController) UpdateCategory(c *fiber.Ctx) error { +func (cat *CategoryController) UpdateCategory(c *fiber.Ctx) error { var category models.CategoryRequestBody if err := c.BodyParser(&category); err != nil { return errors.FailedToValidateCategory.FiberError(c) } - updatedCategory, err := t.categoryService.UpdateCategory(c.Params("id"), category) + updatedCategory, err := cat.categoryService.UpdateCategory(c.Params("id"), category) if err != nil { return err.FiberError(c) } diff --git a/backend/src/controllers/category_tag.go b/backend/src/controllers/category_tag.go index 0c3fa7a27..65c189ae6 100644 --- a/backend/src/controllers/category_tag.go +++ b/backend/src/controllers/category_tag.go @@ -16,11 +16,11 @@ func NewCategoryTagController(categoryTagService services.CategoryTagServiceInte return &CategoryTagController{categoryTagService: categoryTagService} } -func (t *CategoryTagController) GetTagsByCategory(c *fiber.Ctx) error { +func (ct *CategoryTagController) GetTagsByCategory(c *fiber.Ctx) error { defaultLimit := 10 defaultPage := 1 - tags, err := t.categoryTagService.GetTagsByCategory(c.Params("categoryID"), c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage))) + tags, err := ct.categoryTagService.GetTagsByCategory(c.Params("categoryID"), c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage))) if err != nil { return err.FiberError(c) } @@ -28,8 +28,8 @@ func (t *CategoryTagController) GetTagsByCategory(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(&tags) } -func (t *CategoryTagController) GetTagByCategory(c *fiber.Ctx) error { - tag, err := t.categoryTagService.GetTagByCategory(c.Params("categoryID"), c.Params("tagID")) +func (ct *CategoryTagController) GetTagByCategory(c *fiber.Ctx) error { + tag, err := ct.categoryTagService.GetTagByCategory(c.Params("categoryID"), c.Params("tagID")) if err != nil { return err.FiberError(c) } diff --git a/backend/src/controllers/club.go b/backend/src/controllers/club.go index 722738801..44748d61d 100644 --- a/backend/src/controllers/club.go +++ b/backend/src/controllers/club.go @@ -17,11 +17,11 @@ func NewClubController(clubService services.ClubServiceInterface) *ClubControlle return &ClubController{clubService: clubService} } -func (l *ClubController) GetAllClubs(c *fiber.Ctx) error { +func (cc *ClubController) GetAllClubs(c *fiber.Ctx) error { defaultLimit := 10 defaultPage := 1 - clubs, err := l.clubService.GetClubs(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage))) + clubs, err := cc.clubService.GetClubs(c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage))) if err != nil { return err.FiberError(c) } @@ -29,13 +29,13 @@ func (l *ClubController) GetAllClubs(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(clubs) } -func (l *ClubController) CreateClub(c *fiber.Ctx) error { +func (cc *ClubController) CreateClub(c *fiber.Ctx) error { var clubBody models.CreateClubRequestBody if err := c.BodyParser(&clubBody); err != nil { return errors.FailedToParseRequestBody.FiberError(c) } - club, err := l.clubService.CreateClub(clubBody) + club, err := cc.clubService.CreateClub(clubBody) if err != nil { return err.FiberError(c) } @@ -43,8 +43,8 @@ func (l *ClubController) CreateClub(c *fiber.Ctx) error { return c.Status(fiber.StatusCreated).JSON(club) } -func (l *ClubController) GetClub(c *fiber.Ctx) error { - club, err := l.clubService.GetClub(c.Params("id")) +func (cc *ClubController) GetClub(c *fiber.Ctx) error { + club, err := cc.clubService.GetClub(c.Params("id")) if err != nil { return err.FiberError(c) } @@ -52,14 +52,14 @@ func (l *ClubController) GetClub(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(club) } -func (l *ClubController) UpdateClub(c *fiber.Ctx) error { +func (cc *ClubController) UpdateClub(c *fiber.Ctx) error { var clubBody models.UpdateClubRequestBody if err := c.BodyParser(&clubBody); err != nil { return errors.FailedToParseRequestBody.FiberError(c) } - updatedClub, err := l.clubService.UpdateClub(c.Params("id"), clubBody) + updatedClub, err := cc.clubService.UpdateClub(c.Params("id"), clubBody) if err != nil { return err.FiberError(c) } @@ -67,21 +67,11 @@ func (l *ClubController) UpdateClub(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(updatedClub) } -func (l *ClubController) DeleteClub(c *fiber.Ctx) error { - err := l.clubService.DeleteClub(c.Params("id")) +func (cc *ClubController) DeleteClub(c *fiber.Ctx) error { + err := cc.clubService.DeleteClub(c.Params("id")) if err != nil { return err.FiberError(c) } 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) -} diff --git a/backend/src/controllers/club_follower.go b/backend/src/controllers/club_follower.go new file mode 100644 index 000000000..b6beb8f02 --- /dev/null +++ b/backend/src/controllers/club_follower.go @@ -0,0 +1,23 @@ +package controllers + +import ( + "github.com/GenerateNU/sac/backend/src/services" + "github.com/gofiber/fiber/v2" +) + +type ClubFollowerController struct { + clubFollowerService services.ClubFollowerServiceInterface +} + +func NewClubFollowerController(clubFollowerService services.ClubFollowerServiceInterface) *ClubFollowerController { + return &ClubFollowerController{clubFollowerService: clubFollowerService} +} + +func (cf *ClubFollowerController) GetUserFollowingClubs(c *fiber.Ctx) error { + clubs, err := cf.clubFollowerService.GetUserFollowingClubs(c.Params("userID")) + if err != nil { + return err.FiberError(c) + } + + return c.Status(fiber.StatusOK).JSON(&clubs) +} diff --git a/backend/src/controllers/user.go b/backend/src/controllers/user.go index 1edf8d31c..1b509bf3d 100644 --- a/backend/src/controllers/user.go +++ b/backend/src/controllers/user.go @@ -137,49 +137,3 @@ 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) -} diff --git a/backend/src/controllers/user_follower.go b/backend/src/controllers/user_follower.go new file mode 100644 index 000000000..67c0ff36e --- /dev/null +++ b/backend/src/controllers/user_follower.go @@ -0,0 +1,38 @@ +package controllers + +import ( + "github.com/GenerateNU/sac/backend/src/services" + "github.com/gofiber/fiber/v2" +) + +type UserFollowerController struct { + userFollowerService services.UserFollowerServiceInterface +} + +func NewUserFollowerController(userFollowerService services.UserFollowerServiceInterface) *UserFollowerController { + return &UserFollowerController{userFollowerService: userFollowerService} +} + +func (uf *UserFollowerController) CreateFollowing(c *fiber.Ctx) error { + err := uf.userFollowerService.CreateFollowing(c.Params("userID"), c.Params("clubID")) + if err != nil { + return err.FiberError(c) + } + return c.SendStatus(fiber.StatusCreated) +} + +func (uf *UserFollowerController) DeleteFollowing(c *fiber.Ctx) error { + err := uf.userFollowerService.DeleteFollowing(c.Params("userID"), c.Params("clubID")) + if err != nil { + return err.FiberError(c) + } + return c.SendStatus(fiber.StatusNoContent) +} + +func (uf *UserFollowerController) GetAllFollowing(c *fiber.Ctx) error { + clubs, err := uf.userFollowerService.GetFollowing(c.Params("userID")) + if err != nil { + return err.FiberError(c) + } + return c.Status(fiber.StatusOK).JSON(clubs) +} diff --git a/backend/src/controllers/user_tag.go b/backend/src/controllers/user_tag.go index eb1f7bd0b..228baa77a 100644 --- a/backend/src/controllers/user_tag.go +++ b/backend/src/controllers/user_tag.go @@ -15,21 +15,21 @@ func NewUserTagController(userTagService services.UserTagServiceInterface) *User return &UserTagController{userTagService: userTagService} } -func (u *UserTagController) GetUserTags(c *fiber.Ctx) error { - tags, err := u.userTagService.GetUserTags(c.Params("userID")) +func (ut *UserTagController) GetUserTags(c *fiber.Ctx) error { + tags, err := ut.userTagService.GetUserTags(c.Params("userID")) if err != nil { return err.FiberError(c) } return c.Status(fiber.StatusOK).JSON(&tags) } -func (u *UserTagController) CreateUserTags(c *fiber.Ctx) error { +func (ut *UserTagController) CreateUserTags(c *fiber.Ctx) error { var requestBody models.CreateUserTagsBody if err := c.BodyParser(&requestBody); err != nil { return errors.FailedToParseRequestBody.FiberError(c) } - tags, err := u.userTagService.CreateUserTags(c.Params("userID"), requestBody) + tags, err := ut.userTagService.CreateUserTags(c.Params("userID"), requestBody) if err != nil { return err.FiberError(c) } diff --git a/backend/src/middleware/club.go b/backend/src/middleware/club.go index c8529415e..87647d985 100644 --- a/backend/src/middleware/club.go +++ b/backend/src/middleware/club.go @@ -32,7 +32,7 @@ func (m *MiddlewareService) ClubAuthorizeById(c *fiber.Ctx) error { return errors.FailedToParseAccessToken.FiberError(c) } - // use club_id to get the list of admin for a certain club + // use clubID to get the list of admin for a certain club clubAdmin, clubErr := transactions.GetAdminIDs(m.DB, *clubUUID) if clubErr != nil { return err diff --git a/backend/src/server/server.go b/backend/src/server/server.go index a3dadca7d..f75dde7e5 100644 --- a/backend/src/server/server.go +++ b/backend/src/server/server.go @@ -35,12 +35,18 @@ func Init(db *gorm.DB, settings config.Settings) *fiber.App { apiv1.Use(middlewareService.Authenticate) utilityRoutes(app) + authRoutes(apiv1, services.NewAuthService(db, validate), settings.Auth) + userRouter := userRoutes(apiv1, services.NewUserService(db, validate), middlewareService) - userTagRouter(userRouter, services.NewUserTagService(db, validate)) - clubRoutes(apiv1, services.NewClubService(db, validate), middlewareService) - categoryRouter := categoryRoutes(apiv1, services.NewCategoryService(db, validate)) + userTagRoutes(userRouter, services.NewUserTagService(db, validate)) + userFollowerRoutes(userRouter, services.NewUserFollowerService(db, validate)) + + clubRouter := clubRoutes(apiv1, services.NewClubService(db, validate), middlewareService) + clubFollowerRoutes(clubRouter, services.NewClubFollowerService(db, validate)) + tagRoutes(apiv1, services.NewTagService(db, validate)) + categoryRouter := categoryRoutes(apiv1, services.NewCategoryService(db, validate)) categoryTagRoutes(categoryRouter, services.NewCategoryTagService(db, validate)) return app @@ -92,18 +98,20 @@ 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) + return users +} + +func userFollowerRoutes(router fiber.Router, userFollowerService services.UserFollowerServiceInterface) { + userFollowerController := controllers.NewUserFollowerController(userFollowerService) - userTags := users.Group("/:uid/tags") + userFollower := router.Group("/:userID/follower") - userTags.Post("/", userController.CreateUserTags) - userTags.Get("/", userController.GetUserTags) - return users + userFollower.Put("/:clubID", userFollowerController.CreateFollowing) + userFollower.Delete("/:clubID", userFollowerController.DeleteFollowing) + userFollower.Get("/", userFollowerController.GetAllFollowing) } -func userTagRouter(router fiber.Router, userTagService services.UserTagServiceInterface) { +func userTagRoutes(router fiber.Router, userTagService services.UserTagServiceInterface) { userTagController := controllers.NewUserTagController(userTagService) userTags := router.Group("/:userID/tags") @@ -112,7 +120,7 @@ func userTagRouter(router fiber.Router, userTagService services.UserTagServiceIn userTags.Get("/", userTagController.GetUserTags) } -func clubRoutes(router fiber.Router, clubService services.ClubServiceInterface, middlewareService middleware.MiddlewareInterface) { +func clubRoutes(router fiber.Router, clubService services.ClubServiceInterface, middlewareService middleware.MiddlewareInterface) fiber.Router { clubController := controllers.NewClubController(clubService) clubs := router.Group("/clubs") @@ -130,7 +138,14 @@ func clubRoutes(router fiber.Router, clubService services.ClubServiceInterface, clubs.Get("/:id", clubController.GetClub) clubs.Patch("/:id", clubController.UpdateClub) clubs.Delete("/:id", clubController.DeleteClub) - clubs.Get("/:id/follower", clubController.GetUserFollowersForClub) + + return clubs +} + +func clubFollowerRoutes(router fiber.Router, clubFollowerService services.ClubFollowerServiceInterface) { + clubFollowerController := controllers.NewClubFollowerController(clubFollowerService) + + router.Get("/:userID/follower", clubFollowerController.GetUserFollowingClubs) } func authRoutes(router fiber.Router, authService services.AuthServiceInterface, authSettings config.AuthSettings) { diff --git a/backend/src/services/club_follower.go b/backend/src/services/club_follower.go new file mode 100644 index 000000000..921c4c658 --- /dev/null +++ b/backend/src/services/club_follower.go @@ -0,0 +1,32 @@ +package services + +import ( + "github.com/GenerateNU/sac/backend/src/errors" + "github.com/GenerateNU/sac/backend/src/models" + "github.com/GenerateNU/sac/backend/src/transactions" + "github.com/GenerateNU/sac/backend/src/utilities" + "github.com/go-playground/validator/v10" + "gorm.io/gorm" +) + +type ClubFollowerServiceInterface interface { + GetUserFollowingClubs(userID string) ([]models.Club, *errors.Error) +} + +type ClubFollowerService struct { + DB *gorm.DB + Validate *validator.Validate +} + +func NewClubFollowerService(db *gorm.DB, validate *validator.Validate) *ClubFollowerService { + return &ClubFollowerService{DB: db, Validate: validate} +} + +func (cf *ClubFollowerService) GetUserFollowingClubs(userID string) ([]models.Club, *errors.Error) { + userIDAsUUID, err := utilities.ValidateID(userID) + if err != nil { + return nil, err + } + + return transactions.GetClubFollowing(cf.DB, *userIDAsUUID) +} diff --git a/backend/src/services/user.go b/backend/src/services/user.go index d2585982e..4a985332b 100644 --- a/backend/src/services/user.go +++ b/backend/src/services/user.go @@ -21,9 +21,6 @@ type UserServiceInterface interface { 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 { @@ -137,7 +134,6 @@ func (u *UserService) CreateUserTags(id string, tagIDs models.CreateUserTagsBody // Retrieve a list of valid tags from the ids: tags, err := transactions.GetTagsByIDs(u.DB, tagIDs.Tags) - if err != nil { return nil, err } @@ -145,36 +141,3 @@ func (u *UserService) CreateUserTags(id string, tagIDs models.CreateUserTagsBody // 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) -} diff --git a/backend/src/services/user_follower.go b/backend/src/services/user_follower.go new file mode 100644 index 000000000..aea27ab2f --- /dev/null +++ b/backend/src/services/user_follower.go @@ -0,0 +1,58 @@ +package services + +import ( + "github.com/GenerateNU/sac/backend/src/errors" + "github.com/GenerateNU/sac/backend/src/models" + "github.com/GenerateNU/sac/backend/src/transactions" + "github.com/GenerateNU/sac/backend/src/utilities" + "github.com/go-playground/validator/v10" + "gorm.io/gorm" +) + +type UserFollowerServiceInterface interface { + CreateFollowing(userId string, clubId string) *errors.Error + DeleteFollowing(userId string, clubId string) *errors.Error + GetFollowing(userId string) ([]models.Club, *errors.Error) +} + +type UserFollowerService struct { + DB *gorm.DB + Validate *validator.Validate +} + +func NewUserFollowerService(db *gorm.DB, validate *validator.Validate) *UserFollowerService { + return &UserFollowerService{DB: db, Validate: validate} +} + +func (u *UserFollowerService) 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 *UserFollowerService) 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 *UserFollowerService) GetFollowing(userId string) ([]models.Club, *errors.Error) { + userIdAsUUID, err := utilities.ValidateID(userId) + if err != nil { + return nil, err + } + + return transactions.GetClubFollowing(u.DB, *userIdAsUUID) +} diff --git a/backend/src/transactions/club_follower.go b/backend/src/transactions/club_follower.go new file mode 100644 index 000000000..748646a9d --- /dev/null +++ b/backend/src/transactions/club_follower.go @@ -0,0 +1,21 @@ +package transactions + +import ( + "github.com/GenerateNU/sac/backend/src/errors" + "github.com/GenerateNU/sac/backend/src/models" + "github.com/google/uuid" + "gorm.io/gorm" +) + +func GetUserFollowingClubs(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 +} diff --git a/backend/src/transactions/user.go b/backend/src/transactions/user.go index 3f31a5b60..8f161681d 100644 --- a/backend/src/transactions/user.go +++ b/backend/src/transactions/user.go @@ -98,55 +98,3 @@ 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 -} diff --git a/backend/src/transactions/user_follower.go b/backend/src/transactions/user_follower.go new file mode 100644 index 000000000..449b72c44 --- /dev/null +++ b/backend/src/transactions/user_follower.go @@ -0,0 +1,59 @@ +package transactions + +import ( + "github.com/GenerateNU/sac/backend/src/errors" + "github.com/GenerateNU/sac/backend/src/models" + "github.com/google/uuid" + "gorm.io/gorm" +) + +// 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 +} diff --git a/backend/tests/api/club_follower_test.go b/backend/tests/api/club_follower_test.go new file mode 100644 index 000000000..4ae3e4101 --- /dev/null +++ b/backend/tests/api/club_follower_test.go @@ -0,0 +1,3 @@ +package tests + +// TODO diff --git a/backend/tests/api/user_follower_test.go b/backend/tests/api/user_follower_test.go new file mode 100644 index 000000000..19ce215c6 --- /dev/null +++ b/backend/tests/api/user_follower_test.go @@ -0,0 +1,112 @@ +package tests + +import ( + stdliberrors "errors" + "fmt" + "net/http" + "testing" + + "github.com/GenerateNU/sac/backend/src/errors" + "github.com/GenerateNU/sac/backend/src/models" + "github.com/gofiber/fiber/v2" + "github.com/google/uuid" + "github.com/huandu/go-assert" + "gorm.io/gorm" +) + +func TestCreateUserFollowingWorks(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatusAndDB(t, &appAssert, + DBTesterWithStatus{ + Status: fiber.StatusCreated, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var dbUser models.User + err := app.Conn.Preload("Follower").First(&dbUser, userUUID).Error + assert.NilError(err) + + assert.Equal(len(dbUser.Follower), 1) + }, + }, + ) + appAssert.Close() +} + +func TestCreateUserFollowingFailsClubIdBadRequest(t *testing.T) { + appAssert, userUUID := CreateSampleUser(t, nil) + + badRequests := []string{ + "0", + "-1", + "1.1", + "foo", + "null", + } + + for _, badRequest := range badRequests { + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, badRequest), + }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() + } +} + +func TestCreateUserFollowingFailsUserIdBadRequest(t *testing.T) { + appAssert, _, clubUUID := CreateSampleClub(t, nil) + + badRequests := []string{ + "0", + "-1", + "1.1", + "foo", + "null", + } + + for _, badRequest := range badRequests { + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", badRequest, clubUUID), + }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() + } +} + +func TestCreateUserFollowingFailsUserNotExist(t *testing.T) { + appAssert, _, clubUUID := CreateSampleClub(t, nil) + userUUIDNotExist := uuid.New() + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUIDNotExist, clubUUID), + }.TestOnErrorAndDB(t, &appAssert, + ErrorWithDBTester{ + Error: errors.UserNotFound, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var user models.User + err := app.Conn.Where("id = ?", userUUIDNotExist).First(&user).Error + assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) + }, + }, + ).Close() +} + +func TestCreateUserFollowingFailsClubNotExist(t *testing.T) { + appAssert, userUUID := CreateSampleUser(t, nil) + clubUUIDNotExist := uuid.New() + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUIDNotExist), + }.TestOnErrorAndDB(t, &appAssert, + ErrorWithDBTester{ + Error: errors.ClubNotFound, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var club models.Club + err := app.Conn.Where("id = ?", clubUUIDNotExist).First(&club).Error + assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) + }, + }, + ).Close() +} diff --git a/backend/tests/api/user_test.go b/backend/tests/api/user_test.go index c17106775..a1f74a8c8 100644 --- a/backend/tests/api/user_test.go +++ b/backend/tests/api/user_test.go @@ -550,101 +550,3 @@ func TestCreateUserFailsOnMissingFields(t *testing.T) { } appAssert.Close() } - -// test create user following works -func TestCreateUserFollowingWorks(t *testing.T) { - appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatusAndDB(t, &appAssert, - DBTesterWithStatus{ - Status: fiber.StatusCreated, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var dbUser models.User - err := app.Conn.Preload("Follower").First(&dbUser, userUUID).Error - assert.NilError(err) - - assert.Equal(len(dbUser.Follower), 1) - }, - }, - ) - appAssert.Close() -} - -func TestCreateUserFollowingFailsClubIdBadRequest(t *testing.T) { - appAssert, userUUID := CreateSampleUser(t, nil) - - badRequests := []string{ - "0", - "-1", - "1.1", - "foo", - "null", - } - - for _, badRequest := range badRequests { - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, badRequest), - }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() - } -} - -func TestCreateUserFollowingFailsUserIdBadRequest(t *testing.T) { - appAssert, _, clubUUID := CreateSampleClub(t, nil) - - badRequests := []string{ - "0", - "-1", - "1.1", - "foo", - "null", - } - - for _, badRequest := range badRequests { - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", badRequest, clubUUID), - }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() - } -} - -func TestCreateUserFollowingFailsUserNotExist(t *testing.T) { - appAssert, _, clubUUID := CreateSampleClub(t, nil) - userUUIDNotExist := uuid.New() - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUIDNotExist, clubUUID), - }.TestOnErrorAndDB(t, &appAssert, - ErrorWithDBTester{ - Error: errors.UserNotFound, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var user models.User - err := app.Conn.Where("id = ?", userUUIDNotExist).First(&user).Error - assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) - }, - }, - ).Close() -} - -func TestCreateUserFollowingFailsClubNotExist(t *testing.T) { - appAssert, userUUID := CreateSampleUser(t, nil) - clubUUIDNotExist := uuid.New() - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUIDNotExist), - }.TestOnErrorAndDB(t, &appAssert, - ErrorWithDBTester{ - Error: errors.ClubNotFound, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var club models.Club - err := app.Conn.Where("id = ?", clubUUIDNotExist).First(&club).Error - assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) - }, - }, - ).Close() -} From 5b76f074e12e0218f6f2159dbe60d541d9d3be13 Mon Sep 17 00:00:00 2001 From: mai Date: Sat, 3 Feb 2024 10:52:38 -0500 Subject: [PATCH 3/8] DELETE and GET test cases for club and user --- backend/tests/api/club_follower_test.go | 97 ++++++++++++- backend/tests/api/user_follower_test.go | 183 ++++++++++++++++++++++++ go.work.sum | 1 + 3 files changed, 280 insertions(+), 1 deletion(-) diff --git a/backend/tests/api/club_follower_test.go b/backend/tests/api/club_follower_test.go index 4ae3e4101..3285f5ca4 100644 --- a/backend/tests/api/club_follower_test.go +++ b/backend/tests/api/club_follower_test.go @@ -1,3 +1,98 @@ package tests -// TODO +import ( + stdliberrors "errors" + "fmt" + "net/http" + "testing" + + "github.com/GenerateNU/sac/backend/src/errors" + "github.com/GenerateNU/sac/backend/src/models" + "github.com/GenerateNU/sac/backend/src/transactions" + "github.com/gofiber/fiber/v2" + "github.com/google/uuid" + "github.com/huandu/go-assert" + "gorm.io/gorm" +) + + +func TestGetClubFollowersWorks(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatus(t, &appAssert, fiber.StatusCreated) + + TestRequest{ + Method: fiber.MethodGet, + Path: fmt.Sprintf("/api/v1/clubs/%s/follower", clubUUID), + }.TestOnStatusAndDB(t, &appAssert, + DBTesterWithStatus{ + Status: fiber.StatusCreated, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var dbClub *models.Club + err := app.Conn.Preload("Follower").First(dbClub, clubUUID).Error + assert.NilError(err) + assert.Equal(len(dbClub.Follower), 1) + + var user *models.User + err = app.Conn.First(user, userUUID).Error + assert.NilError(err) + + user, _ = transactions.GetUser(app.Conn, userUUID) + userFollower := &dbClub.Follower[0] + assert.Equal(userFollower, user) + }, + }, + ) + appAssert.Close() +} + + +func TestGetClubFollowersFailsClubNotExist(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) + clubUUIDNotExist := uuid.New() + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatus(t, &appAssert, fiber.StatusCreated) + + TestRequest{ + Method: fiber.MethodGet, + Path: fmt.Sprintf("/api/v1/clubs/%s/follower", clubUUIDNotExist), + }.TestOnErrorAndDB(t, &appAssert, + ErrorWithDBTester{ + Error: errors.ClubNotFound, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var club models.Club + err := app.Conn.Where("id = ?", clubUUIDNotExist).First(&club).Error + assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) + }, + }, + ).Close() +} + +func TestGetClubFollowersFailsClubIdBadRequest(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatus(t, &appAssert, fiber.StatusCreated) + + badRequests := []string{ + "0", + "-1", + "1.1", + "foo", + "null", + } + for _, badRequest := range badRequests { + TestRequest{ + Method: fiber.MethodGet, + Path: fmt.Sprintf("/api/v1/clubs/%s/follower", badRequest), + }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() + } +} \ No newline at end of file diff --git a/backend/tests/api/user_follower_test.go b/backend/tests/api/user_follower_test.go index 19ce215c6..52837fd00 100644 --- a/backend/tests/api/user_follower_test.go +++ b/backend/tests/api/user_follower_test.go @@ -8,6 +8,7 @@ import ( "github.com/GenerateNU/sac/backend/src/errors" "github.com/GenerateNU/sac/backend/src/models" + "github.com/GenerateNU/sac/backend/src/transactions" "github.com/gofiber/fiber/v2" "github.com/google/uuid" "github.com/huandu/go-assert" @@ -110,3 +111,185 @@ func TestCreateUserFollowingFailsClubNotExist(t *testing.T) { }, ).Close() } + +func TestDeleteUserFollowingWorks(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatus(t, &appAssert, fiber.StatusCreated) + TestRequest{ + Method: fiber.MethodDelete, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatusAndDB(t, &appAssert, + DBTesterWithStatus{ + Status: fiber.StatusCreated, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var dbUser models.User + err := app.Conn.Preload("Follower").First(&dbUser, userUUID).Error + assert.NilError(err) + + assert.Equal(len(dbUser.Follower), 0) + }, + }, + ) + appAssert.Close() +} + +func TestDeleteUserFollowingFailsClubIdBadRequest(t *testing.T) { + appAssert, userUUID := CreateSampleUser(t, nil) + + badRequests := []string{ + "0", + "-1", + "1.1", + "foo", + "null", + } + + for _, badRequest := range badRequests { + TestRequest{ + Method: fiber.MethodDelete, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, badRequest), + }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() + } +} + +func TestDeleteUserFollowingFailsUserIdBadRequest(t *testing.T) { + appAssert, _, clubUUID := CreateSampleClub(t, nil) + + badRequests := []string{ + "0", + "-1", + "1.1", + "foo", + "null", + } + + for _, badRequest := range badRequests { + TestRequest{ + Method: fiber.MethodDelete, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", badRequest, clubUUID), + }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() + } +} + + +func TestDeleteUserFollowingFailsUserNotExist(t *testing.T) { + appAssert, _, clubUUID := CreateSampleClub(t, nil) + userUUIDNotExist := uuid.New() + + TestRequest{ + Method: fiber.MethodDelete, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUIDNotExist, clubUUID), + }.TestOnErrorAndDB(t, &appAssert, + ErrorWithDBTester{ + Error: errors.UserNotFound, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var user models.User + err := app.Conn.Where("id = ?", userUUIDNotExist).First(&user).Error + assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) + }, + }, + ).Close() +} + +func TestDeleteUserFollowingFailsClubNotExist(t *testing.T) { + appAssert, userUUID := CreateSampleUser(t, nil) + clubUUIDNotExist := uuid.New() + + TestRequest{ + Method: fiber.MethodDelete, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUIDNotExist), + }.TestOnErrorAndDB(t, &appAssert, + ErrorWithDBTester{ + Error: errors.ClubNotFound, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var club models.Club + err := app.Conn.Where("id = ?", clubUUIDNotExist).First(&club).Error + assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) + }, + }, + ).Close() +} + +func TestGetUserFollowingWorks(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatus(t, &appAssert, fiber.StatusCreated) + TestRequest{ + Method: fiber.MethodGet, + Path: fmt.Sprintf("/api/v1/users/%s/follower", userUUID), + }.TestOnStatusAndDB(t, &appAssert, + DBTesterWithStatus{ + Status: fiber.StatusCreated, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var dbUser *models.User + err := app.Conn.Preload("Follower").First(dbUser, userUUID).Error + assert.NilError(err) + assert.Equal(len(dbUser.Follower), 1) + + var club *models.Club + err = app.Conn.First(club, clubUUID).Error + assert.NilError(err) + + club, _ = transactions.GetClub(app.Conn, clubUUID) + clubFollowed := &dbUser.Follower[0] + assert.Equal(clubFollowed, club) + }, + }, + ) + appAssert.Close() +} + + +func TestGetUserFailsUserNotExist(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) + userUUIDNotExist := uuid.New() + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatus(t, &appAssert, fiber.StatusCreated) + + TestRequest{ + Method: fiber.MethodGet, + Path: fmt.Sprintf("/api/v1/users/%s/follower", userUUIDNotExist), + }.TestOnErrorAndDB(t, &appAssert, + ErrorWithDBTester{ + Error: errors.ClubNotFound, + DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { + var user models.User + err := app.Conn.Where("id = ?", userUUIDNotExist).First(&user).Error + assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) + }, + }, + ).Close() +} + +func TestGetUserFailsUserIdBadRequest(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) + + TestRequest{ + Method: fiber.MethodPut, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }.TestOnStatus(t, &appAssert, fiber.StatusCreated) + + badRequests := []string{ + "0", + "-1", + "1.1", + "foo", + "null", + } + for _, badRequest := range badRequests { + TestRequest{ + Method: fiber.MethodGet, + Path: fmt.Sprintf("/api/v1/users/%s/follower", badRequest), + }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() + } +} \ No newline at end of file diff --git a/go.work.sum b/go.work.sum index 9dfa3c6d2..cf29a0930 100644 --- a/go.work.sum +++ b/go.work.sum @@ -12,5 +12,6 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= +golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= From 47d824ec1160bafd18f15c85a730e8a60ce16372 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sat, 10 Feb 2024 16:13:29 -0500 Subject: [PATCH 4/8] GML init clean up / refactor --- backend/src/controllers/club_follower.go | 11 +- backend/src/server/server.go | 11 - backend/src/services/club_follower.go | 20 +- backend/src/transactions/club_follower.go | 9 +- backend/src/transactions/contacts.go | 39 --- backend/src/transactions/user_follower.go | 6 - backend/tests/api/club_follower_test.go | 97 ------- backend/tests/api/user_follower_test.go | 294 ---------------------- 8 files changed, 24 insertions(+), 463 deletions(-) diff --git a/backend/src/controllers/club_follower.go b/backend/src/controllers/club_follower.go index b6beb8f02..76239e150 100644 --- a/backend/src/controllers/club_follower.go +++ b/backend/src/controllers/club_follower.go @@ -1,6 +1,8 @@ package controllers import ( + "strconv" + "github.com/GenerateNU/sac/backend/src/services" "github.com/gofiber/fiber/v2" ) @@ -13,11 +15,14 @@ func NewClubFollowerController(clubFollowerService services.ClubFollowerServiceI return &ClubFollowerController{clubFollowerService: clubFollowerService} } -func (cf *ClubFollowerController) GetUserFollowingClubs(c *fiber.Ctx) error { - clubs, err := cf.clubFollowerService.GetUserFollowingClubs(c.Params("userID")) +func (cf *ClubFollowerController) GetClubFollowers(c *fiber.Ctx) error { + defaultLimit := 10 + defaultPage := 1 + + followers, err := cf.clubFollowerService.GetClubFollowers(c.Params("clubID"), c.Query("limit", strconv.Itoa(defaultLimit)), c.Query("page", strconv.Itoa(defaultPage))) if err != nil { return err.FiberError(c) } - return c.Status(fiber.StatusOK).JSON(&clubs) + return c.Status(fiber.StatusOK).JSON(followers) } diff --git a/backend/src/server/server.go b/backend/src/server/server.go index 9da9724d1..c1a8d08ab 100644 --- a/backend/src/server/server.go +++ b/backend/src/server/server.go @@ -73,14 +73,3 @@ func newFiberApp() *fiber.App { return app } - -func userFollowerRoutes(router fiber.Router, userFollowerService services.UserFollowerServiceInterface) { - userFollowerController := controllers.NewUserFollowerController(userFollowerService) - - userFollower := router.Group("/:userID/follower") - - userFollower.Put("/:clubID", userFollowerController.CreateFollowing) - userFollower.Delete("/:clubID", userFollowerController.DeleteFollowing) - userFollower.Get("/", userFollowerController.GetAllFollowing) -} - diff --git a/backend/src/services/club_follower.go b/backend/src/services/club_follower.go index 921c4c658..1cd636e04 100644 --- a/backend/src/services/club_follower.go +++ b/backend/src/services/club_follower.go @@ -10,7 +10,7 @@ import ( ) type ClubFollowerServiceInterface interface { - GetUserFollowingClubs(userID string) ([]models.Club, *errors.Error) + GetClubFollowers(clubID string, limit string, page string) ([]models.User, *errors.Error) } type ClubFollowerService struct { @@ -22,11 +22,21 @@ func NewClubFollowerService(db *gorm.DB, validate *validator.Validate) *ClubFoll return &ClubFollowerService{DB: db, Validate: validate} } -func (cf *ClubFollowerService) GetUserFollowingClubs(userID string) ([]models.Club, *errors.Error) { - userIDAsUUID, err := utilities.ValidateID(userID) +func (cf *ClubFollowerService) GetClubFollowers(clubID string, limit string, page string) ([]models.User, *errors.Error) { + idAsUUID, err := utilities.ValidateID(clubID) if err != nil { - return nil, err + return nil, &errors.FailedToValidateID } - return transactions.GetClubFollowing(cf.DB, *userIDAsUUID) + limitAsInt, err := utilities.ValidateNonNegative(limit) + if err != nil { + return nil, &errors.FailedToValidateLimit + } + + pageAsInt, err := utilities.ValidateNonNegative(page) + if err != nil { + return nil, &errors.FailedToValidatePage + } + + return transactions.GetClubFollowers(cf.DB, *idAsUUID, *limitAsInt, *pageAsInt) } diff --git a/backend/src/transactions/club_follower.go b/backend/src/transactions/club_follower.go index 748646a9d..a2e747c80 100644 --- a/backend/src/transactions/club_follower.go +++ b/backend/src/transactions/club_follower.go @@ -7,15 +7,8 @@ import ( "gorm.io/gorm" ) -func GetUserFollowingClubs(db *gorm.DB, club_id uuid.UUID) ([]models.User, *errors.Error) { +func GetClubFollowers(db *gorm.DB, clubID uuid.UUID, limit int, page int) ([]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 } diff --git a/backend/src/transactions/contacts.go b/backend/src/transactions/contacts.go index 51ac2bb00..a47405fae 100644 --- a/backend/src/transactions/contacts.go +++ b/backend/src/transactions/contacts.go @@ -42,42 +42,3 @@ func DeleteContact(db *gorm.DB, id uuid.UUID) *errors.Error { } return nil } - -func UpdateClub(db *gorm.DB, id uuid.UUID, club models.Club) (*models.Club, *errors.Error) { - result := db.Model(&models.User{}).Where("id = ?", id).Updates(club) - if result.Error != nil { - if stdliberrors.Is(result.Error, gorm.ErrRecordNotFound) { - return nil, &errors.UserNotFound - } else { - return nil, &errors.FailedToUpdateClub - } - } - var existingClub models.Club - - err := db.First(&existingClub, id).Error - if err != nil { - if stdliberrors.Is(err, gorm.ErrRecordNotFound) { - return nil, &errors.ClubNotFound - } else { - return nil, &errors.FailedToCreateClub - } - } - - if err := db.Model(&existingClub).Updates(&club).Error; err != nil { - return nil, &errors.FailedToUpdateUser - } - - return &existingClub, nil -} - -func DeleteClub(db *gorm.DB, id uuid.UUID) *errors.Error { - if result := db.Delete(&models.Club{}, id); result.RowsAffected == 0 { - if result.Error == nil { - return &errors.ClubNotFound - } else { - return &errors.FailedToDeleteClub - } - } - - return nil -} diff --git a/backend/src/transactions/user_follower.go b/backend/src/transactions/user_follower.go index 449b72c44..1b24e1e47 100644 --- a/backend/src/transactions/user_follower.go +++ b/backend/src/transactions/user_follower.go @@ -7,7 +7,6 @@ import ( "gorm.io/gorm" ) -// 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 { @@ -24,7 +23,6 @@ func CreateFollowing(db *gorm.DB, userId uuid.UUID, clubId uuid.UUID) *errors.Er 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 { @@ -34,16 +32,12 @@ func DeleteFollowing(db *gorm.DB, userId uuid.UUID, clubId uuid.UUID) *errors.Er 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 diff --git a/backend/tests/api/club_follower_test.go b/backend/tests/api/club_follower_test.go index 3285f5ca4..ca8701d29 100644 --- a/backend/tests/api/club_follower_test.go +++ b/backend/tests/api/club_follower_test.go @@ -1,98 +1 @@ package tests - -import ( - stdliberrors "errors" - "fmt" - "net/http" - "testing" - - "github.com/GenerateNU/sac/backend/src/errors" - "github.com/GenerateNU/sac/backend/src/models" - "github.com/GenerateNU/sac/backend/src/transactions" - "github.com/gofiber/fiber/v2" - "github.com/google/uuid" - "github.com/huandu/go-assert" - "gorm.io/gorm" -) - - -func TestGetClubFollowersWorks(t *testing.T) { - appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatus(t, &appAssert, fiber.StatusCreated) - - TestRequest{ - Method: fiber.MethodGet, - Path: fmt.Sprintf("/api/v1/clubs/%s/follower", clubUUID), - }.TestOnStatusAndDB(t, &appAssert, - DBTesterWithStatus{ - Status: fiber.StatusCreated, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var dbClub *models.Club - err := app.Conn.Preload("Follower").First(dbClub, clubUUID).Error - assert.NilError(err) - assert.Equal(len(dbClub.Follower), 1) - - var user *models.User - err = app.Conn.First(user, userUUID).Error - assert.NilError(err) - - user, _ = transactions.GetUser(app.Conn, userUUID) - userFollower := &dbClub.Follower[0] - assert.Equal(userFollower, user) - }, - }, - ) - appAssert.Close() -} - - -func TestGetClubFollowersFailsClubNotExist(t *testing.T) { - appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) - clubUUIDNotExist := uuid.New() - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatus(t, &appAssert, fiber.StatusCreated) - - TestRequest{ - Method: fiber.MethodGet, - Path: fmt.Sprintf("/api/v1/clubs/%s/follower", clubUUIDNotExist), - }.TestOnErrorAndDB(t, &appAssert, - ErrorWithDBTester{ - Error: errors.ClubNotFound, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var club models.Club - err := app.Conn.Where("id = ?", clubUUIDNotExist).First(&club).Error - assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) - }, - }, - ).Close() -} - -func TestGetClubFollowersFailsClubIdBadRequest(t *testing.T) { - appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatus(t, &appAssert, fiber.StatusCreated) - - badRequests := []string{ - "0", - "-1", - "1.1", - "foo", - "null", - } - for _, badRequest := range badRequests { - TestRequest{ - Method: fiber.MethodGet, - Path: fmt.Sprintf("/api/v1/clubs/%s/follower", badRequest), - }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() - } -} \ No newline at end of file diff --git a/backend/tests/api/user_follower_test.go b/backend/tests/api/user_follower_test.go index 52837fd00..ca8701d29 100644 --- a/backend/tests/api/user_follower_test.go +++ b/backend/tests/api/user_follower_test.go @@ -1,295 +1 @@ package tests - -import ( - stdliberrors "errors" - "fmt" - "net/http" - "testing" - - "github.com/GenerateNU/sac/backend/src/errors" - "github.com/GenerateNU/sac/backend/src/models" - "github.com/GenerateNU/sac/backend/src/transactions" - "github.com/gofiber/fiber/v2" - "github.com/google/uuid" - "github.com/huandu/go-assert" - "gorm.io/gorm" -) - -func TestCreateUserFollowingWorks(t *testing.T) { - appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatusAndDB(t, &appAssert, - DBTesterWithStatus{ - Status: fiber.StatusCreated, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var dbUser models.User - err := app.Conn.Preload("Follower").First(&dbUser, userUUID).Error - assert.NilError(err) - - assert.Equal(len(dbUser.Follower), 1) - }, - }, - ) - appAssert.Close() -} - -func TestCreateUserFollowingFailsClubIdBadRequest(t *testing.T) { - appAssert, userUUID := CreateSampleUser(t, nil) - - badRequests := []string{ - "0", - "-1", - "1.1", - "foo", - "null", - } - - for _, badRequest := range badRequests { - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, badRequest), - }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() - } -} - -func TestCreateUserFollowingFailsUserIdBadRequest(t *testing.T) { - appAssert, _, clubUUID := CreateSampleClub(t, nil) - - badRequests := []string{ - "0", - "-1", - "1.1", - "foo", - "null", - } - - for _, badRequest := range badRequests { - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", badRequest, clubUUID), - }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() - } -} - -func TestCreateUserFollowingFailsUserNotExist(t *testing.T) { - appAssert, _, clubUUID := CreateSampleClub(t, nil) - userUUIDNotExist := uuid.New() - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUIDNotExist, clubUUID), - }.TestOnErrorAndDB(t, &appAssert, - ErrorWithDBTester{ - Error: errors.UserNotFound, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var user models.User - err := app.Conn.Where("id = ?", userUUIDNotExist).First(&user).Error - assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) - }, - }, - ).Close() -} - -func TestCreateUserFollowingFailsClubNotExist(t *testing.T) { - appAssert, userUUID := CreateSampleUser(t, nil) - clubUUIDNotExist := uuid.New() - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUIDNotExist), - }.TestOnErrorAndDB(t, &appAssert, - ErrorWithDBTester{ - Error: errors.ClubNotFound, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var club models.Club - err := app.Conn.Where("id = ?", clubUUIDNotExist).First(&club).Error - assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) - }, - }, - ).Close() -} - -func TestDeleteUserFollowingWorks(t *testing.T) { - appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatus(t, &appAssert, fiber.StatusCreated) - TestRequest{ - Method: fiber.MethodDelete, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatusAndDB(t, &appAssert, - DBTesterWithStatus{ - Status: fiber.StatusCreated, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var dbUser models.User - err := app.Conn.Preload("Follower").First(&dbUser, userUUID).Error - assert.NilError(err) - - assert.Equal(len(dbUser.Follower), 0) - }, - }, - ) - appAssert.Close() -} - -func TestDeleteUserFollowingFailsClubIdBadRequest(t *testing.T) { - appAssert, userUUID := CreateSampleUser(t, nil) - - badRequests := []string{ - "0", - "-1", - "1.1", - "foo", - "null", - } - - for _, badRequest := range badRequests { - TestRequest{ - Method: fiber.MethodDelete, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, badRequest), - }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() - } -} - -func TestDeleteUserFollowingFailsUserIdBadRequest(t *testing.T) { - appAssert, _, clubUUID := CreateSampleClub(t, nil) - - badRequests := []string{ - "0", - "-1", - "1.1", - "foo", - "null", - } - - for _, badRequest := range badRequests { - TestRequest{ - Method: fiber.MethodDelete, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", badRequest, clubUUID), - }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() - } -} - - -func TestDeleteUserFollowingFailsUserNotExist(t *testing.T) { - appAssert, _, clubUUID := CreateSampleClub(t, nil) - userUUIDNotExist := uuid.New() - - TestRequest{ - Method: fiber.MethodDelete, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUIDNotExist, clubUUID), - }.TestOnErrorAndDB(t, &appAssert, - ErrorWithDBTester{ - Error: errors.UserNotFound, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var user models.User - err := app.Conn.Where("id = ?", userUUIDNotExist).First(&user).Error - assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) - }, - }, - ).Close() -} - -func TestDeleteUserFollowingFailsClubNotExist(t *testing.T) { - appAssert, userUUID := CreateSampleUser(t, nil) - clubUUIDNotExist := uuid.New() - - TestRequest{ - Method: fiber.MethodDelete, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUIDNotExist), - }.TestOnErrorAndDB(t, &appAssert, - ErrorWithDBTester{ - Error: errors.ClubNotFound, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var club models.Club - err := app.Conn.Where("id = ?", clubUUIDNotExist).First(&club).Error - assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) - }, - }, - ).Close() -} - -func TestGetUserFollowingWorks(t *testing.T) { - appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatus(t, &appAssert, fiber.StatusCreated) - TestRequest{ - Method: fiber.MethodGet, - Path: fmt.Sprintf("/api/v1/users/%s/follower", userUUID), - }.TestOnStatusAndDB(t, &appAssert, - DBTesterWithStatus{ - Status: fiber.StatusCreated, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var dbUser *models.User - err := app.Conn.Preload("Follower").First(dbUser, userUUID).Error - assert.NilError(err) - assert.Equal(len(dbUser.Follower), 1) - - var club *models.Club - err = app.Conn.First(club, clubUUID).Error - assert.NilError(err) - - club, _ = transactions.GetClub(app.Conn, clubUUID) - clubFollowed := &dbUser.Follower[0] - assert.Equal(clubFollowed, club) - }, - }, - ) - appAssert.Close() -} - - -func TestGetUserFailsUserNotExist(t *testing.T) { - appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) - userUUIDNotExist := uuid.New() - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatus(t, &appAssert, fiber.StatusCreated) - - TestRequest{ - Method: fiber.MethodGet, - Path: fmt.Sprintf("/api/v1/users/%s/follower", userUUIDNotExist), - }.TestOnErrorAndDB(t, &appAssert, - ErrorWithDBTester{ - Error: errors.ClubNotFound, - DBTester: func(app TestApp, assert *assert.A, resp *http.Response) { - var user models.User - err := app.Conn.Where("id = ?", userUUIDNotExist).First(&user).Error - assert.Assert(stdliberrors.Is(err, gorm.ErrRecordNotFound)) - }, - }, - ).Close() -} - -func TestGetUserFailsUserIdBadRequest(t *testing.T) { - appAssert, userUUID, clubUUID := CreateSampleClub(t, nil) - - TestRequest{ - Method: fiber.MethodPut, - Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), - }.TestOnStatus(t, &appAssert, fiber.StatusCreated) - - badRequests := []string{ - "0", - "-1", - "1.1", - "foo", - "null", - } - for _, badRequest := range badRequests { - TestRequest{ - Method: fiber.MethodGet, - Path: fmt.Sprintf("/api/v1/users/%s/follower", badRequest), - }.TestOnError(t, &appAssert, errors.FailedToValidateID).Close() - } -} \ No newline at end of file From 4f355a8c88a449dfd5b31274acfcdd962b715772 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sat, 10 Feb 2024 16:14:04 -0500 Subject: [PATCH 5/8] register routes --- backend/src/server/routes/club_follower.go | 16 ++++++++++++++++ backend/src/server/routes/user_follower.go | 18 ++++++++++++++++++ backend/src/server/server.go | 2 ++ 3 files changed, 36 insertions(+) create mode 100644 backend/src/server/routes/club_follower.go create mode 100644 backend/src/server/routes/user_follower.go diff --git a/backend/src/server/routes/club_follower.go b/backend/src/server/routes/club_follower.go new file mode 100644 index 000000000..4050538e2 --- /dev/null +++ b/backend/src/server/routes/club_follower.go @@ -0,0 +1,16 @@ +package routes + +import ( + "github.com/GenerateNU/sac/backend/src/controllers" + "github.com/GenerateNU/sac/backend/src/services" + "github.com/gofiber/fiber/v2" +) + +func ClubFollower(clubsIDRouter fiber.Router, clubFollowerService services.ClubFollowerServiceInterface) { + clubFollowerController := controllers.NewClubFollowerController(clubFollowerService) + + clubFollower := clubsIDRouter.Group("/followers") + + // api/clubs/:clubID/followers/* + clubFollower.Get("/", clubFollowerController.GetClubFollowers) +} diff --git a/backend/src/server/routes/user_follower.go b/backend/src/server/routes/user_follower.go new file mode 100644 index 000000000..0fa50ee05 --- /dev/null +++ b/backend/src/server/routes/user_follower.go @@ -0,0 +1,18 @@ +package routes + +import ( + "github.com/GenerateNU/sac/backend/src/controllers" + "github.com/GenerateNU/sac/backend/src/services" + "github.com/gofiber/fiber/v2" +) + +func UserFollower(usersIDRouter fiber.Router, userFollowerService services.UserFollowerServiceInterface) { + userFollowerController := controllers.NewUserFollowerController(userFollowerService) + + userFollower := usersIDRouter.Group("/follower") + + // api/users/:userID/follower/* + userFollower.Put("/:clubID", userFollowerController.CreateFollowing) + userFollower.Delete("/:clubID", userFollowerController.DeleteFollowing) + userFollower.Get("/", userFollowerController.GetAllFollowing) +} diff --git a/backend/src/server/server.go b/backend/src/server/server.go index c1a8d08ab..c8287fd2a 100644 --- a/backend/src/server/server.go +++ b/backend/src/server/server.go @@ -42,10 +42,12 @@ func Init(db *gorm.DB, settings config.Settings) *fiber.App { userRouter := routes.User(apiv1, services.NewUserService(db, validate), middlewareService) routes.UserTag(userRouter, services.NewUserTagService(db, validate)) + routes.UserFollower(userRouter, services.NewUserFollowerService(db, validate)) routes.Contact(apiv1, services.NewContactService(db, validate)) clubsRouter := routes.Club(apiv1, services.NewClubService(db, validate), middlewareService) + routes.ClubFollower(clubsRouter, services.NewClubFollowerService(db, validate)) routes.ClubContact(clubsRouter, services.NewClubContactService(db, validate)) routes.Tag(apiv1, services.NewTagService(db, validate)) From d8ee450121385d636925adf8afceac3af9a25b17 Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sat, 10 Feb 2024 16:16:57 -0500 Subject: [PATCH 6/8] linter --- backend/src/controllers/club.go | 3 --- backend/src/transactions/club.go | 1 - 2 files changed, 4 deletions(-) diff --git a/backend/src/controllers/club.go b/backend/src/controllers/club.go index 790e05383..e9b3d433d 100644 --- a/backend/src/controllers/club.go +++ b/backend/src/controllers/club.go @@ -29,7 +29,6 @@ func (cl *ClubController) GetAllClubs(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(clubs) } - func (cl *ClubController) CreateClub(c *fiber.Ctx) error { var clubBody models.CreateClubRequestBody if err := c.BodyParser(&clubBody); err != nil { @@ -44,7 +43,6 @@ func (cl *ClubController) CreateClub(c *fiber.Ctx) error { return c.Status(fiber.StatusCreated).JSON(club) } - func (cl *ClubController) GetClub(c *fiber.Ctx) error { club, err := cl.clubService.GetClub(c.Params("clubID")) if err != nil { @@ -54,7 +52,6 @@ func (cl *ClubController) GetClub(c *fiber.Ctx) error { return c.Status(fiber.StatusOK).JSON(club) } - func (cl *ClubController) UpdateClub(c *fiber.Ctx) error { var clubBody models.UpdateClubRequestBody diff --git a/backend/src/transactions/club.go b/backend/src/transactions/club.go index 5b83fb4dc..4b2908ef1 100644 --- a/backend/src/transactions/club.go +++ b/backend/src/transactions/club.go @@ -124,4 +124,3 @@ func GetUserFollowersForClub(db *gorm.DB, club_id uuid.UUID) ([]models.User, *er } return users, nil } - From ea174d8b0e3f1c17a58c8edfcab2c6cd6cc876fa Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sat, 10 Feb 2024 16:22:12 -0500 Subject: [PATCH 7/8] format --- backend/src/controllers/category.go | 1 - 1 file changed, 1 deletion(-) diff --git a/backend/src/controllers/category.go b/backend/src/controllers/category.go index 2a778e3d0..40858ea89 100644 --- a/backend/src/controllers/category.go +++ b/backend/src/controllers/category.go @@ -129,7 +129,6 @@ func (cat *CategoryController) UpdateCategory(c *fiber.Ctx) error { } updatedCategory, err := cat.categoryService.UpdateCategory(c.Params("categoryID"), category) - if err != nil { return err.FiberError(c) } From c3cdebfa68256a2461ee64b492299932edd23dea Mon Sep 17 00:00:00 2001 From: garrettladley Date: Sat, 10 Feb 2024 22:01:03 -0500 Subject: [PATCH 8/8] i ripped my hair out but the test is passing now --- backend/src/server/routes/user_follower.go | 6 +-- backend/src/transactions/club_contact.go | 2 - backend/src/transactions/user_follower.go | 8 +++- backend/tests/api/club_test.go | 2 +- backend/tests/api/user_follower_test.go | 45 ++++++++++++++++++++++ backend/tests/auth_test.go | 2 +- 6 files changed, 56 insertions(+), 9 deletions(-) diff --git a/backend/src/server/routes/user_follower.go b/backend/src/server/routes/user_follower.go index 0fa50ee05..a6f355280 100644 --- a/backend/src/server/routes/user_follower.go +++ b/backend/src/server/routes/user_follower.go @@ -9,10 +9,10 @@ import ( func UserFollower(usersIDRouter fiber.Router, userFollowerService services.UserFollowerServiceInterface) { userFollowerController := controllers.NewUserFollowerController(userFollowerService) - userFollower := usersIDRouter.Group("/follower") + userFollower := usersIDRouter.Group("/:userID/follower") - // api/users/:userID/follower/* - userFollower.Put("/:clubID", userFollowerController.CreateFollowing) + // api/v1/users/:userID/follower/* + userFollower.Post("/:clubID", userFollowerController.CreateFollowing) userFollower.Delete("/:clubID", userFollowerController.DeleteFollowing) userFollower.Get("/", userFollowerController.GetAllFollowing) } diff --git a/backend/src/transactions/club_contact.go b/backend/src/transactions/club_contact.go index 7cb7afb87..d77f4de7d 100644 --- a/backend/src/transactions/club_contact.go +++ b/backend/src/transactions/club_contact.go @@ -2,7 +2,6 @@ package transactions import ( stdliberrors "errors" - "fmt" "github.com/GenerateNU/sac/backend/src/errors" "github.com/GenerateNU/sac/backend/src/models" @@ -17,7 +16,6 @@ func PutClubContact(db *gorm.DB, contact models.Contact) (*models.Contact, *erro DoUpdates: clause.AssignmentColumns([]string{"content"}), }).Create(&contact).Error if err != nil { - fmt.Println(err) if stdliberrors.Is(err, gorm.ErrRecordNotFound) || stdliberrors.Is(err, gorm.ErrForeignKeyViolated) { return nil, &errors.ClubNotFound } else { diff --git a/backend/src/transactions/user_follower.go b/backend/src/transactions/user_follower.go index 1b24e1e47..38e78ec58 100644 --- a/backend/src/transactions/user_follower.go +++ b/backend/src/transactions/user_follower.go @@ -8,18 +8,22 @@ import ( ) func CreateFollowing(db *gorm.DB, userId uuid.UUID, clubId uuid.UUID) *errors.Error { - user, err := GetUserWithFollowers(db, userId) + user, err := GetUser(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 { + user.Follower = append(user.Follower, *club) + + if err := db.Model(&user).Association("Follower").Append(&club); err != nil { return &errors.FailedToUpdateUser } + return nil } diff --git a/backend/tests/api/club_test.go b/backend/tests/api/club_test.go index bea54a011..2d40958dd 100644 --- a/backend/tests/api/club_test.go +++ b/backend/tests/api/club_test.go @@ -152,7 +152,7 @@ func CreateSampleClub(existingAppAssert h.ExistingAppAssert) (eaa h.ExistingAppA }, ) - return existingAppAssert, newAppAssert.App.TestUser.UUID, sampleClubUUID + return newAppAssert, newAppAssert.App.TestUser.UUID, sampleClubUUID } func TestCreateClubWorks(t *testing.T) { diff --git a/backend/tests/api/user_follower_test.go b/backend/tests/api/user_follower_test.go index ca8701d29..c1207c613 100644 --- a/backend/tests/api/user_follower_test.go +++ b/backend/tests/api/user_follower_test.go @@ -1 +1,46 @@ package tests + +import ( + "fmt" + "net/http" + "testing" + + "github.com/GenerateNU/sac/backend/src/models" + h "github.com/GenerateNU/sac/backend/tests/api/helpers" + "github.com/gofiber/fiber/v2" +) + +func TestCreateFollowingWorks(t *testing.T) { + appAssert, userUUID, clubUUID := CreateSampleClub(h.InitTest(t)) + + appAssert.TestOnStatusAndTester( + h.TestRequest{ + Method: fiber.MethodPost, + Path: fmt.Sprintf("/api/v1/users/%s/follower/%s", userUUID, clubUUID), + }, + h.TesterWithStatus{ + Status: fiber.StatusCreated, + Tester: func(eaa h.ExistingAppAssert, resp *http.Response) { + var user models.User + + err := eaa.App.Conn.Where("id = ?", userUUID).Preload("Follower").First(&user) + + eaa.Assert.NilError(err) + + eaa.Assert.Equal(1, len(user.Follower)) + + eaa.Assert.Equal(clubUUID, user.Follower[0].ID) + + var club models.Club + + err = eaa.App.Conn.Where("id = ?", clubUUID).Preload("Follower").First(&club) + + eaa.Assert.NilError(err) + + eaa.Assert.Equal(1, len(club.Follower)) + + eaa.Assert.Equal(userUUID, club.Follower[0].ID) + }, + }, + ).Close() +} diff --git a/backend/tests/auth_test.go b/backend/tests/auth_test.go index 3bfc1f530..bb29d577f 100644 --- a/backend/tests/auth_test.go +++ b/backend/tests/auth_test.go @@ -151,7 +151,7 @@ func TestSignTokenSuccess(t *testing.T) { signedToken, authErr := auth.SignToken(token, key) - assert.Assert(authErr == nil) + assert.NilError(authErr == nil) assert.Assert(signedToken != nil) }