Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Delete User Tags #319

Merged
merged 5 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions backend/src/controllers/user_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,3 +66,27 @@ func (ut *UserTagController) CreateUserTags(c *fiber.Ctx) error {

return c.Status(fiber.StatusCreated).JSON(&tags)
}

// DeleteUserTag godoc
//
// @Summary Create user tags
// @Description Creates tags for a user
// @ID create-user-tags
// @Tags user-tag
// @Accept json
// @Produce json
// @Param userID path string true "User ID"
// @Success 201 {object} []models.Tag
// @Failure 400 {object} errors.Error
// @Failure 401 {object} errors.Error
// @Failure 404 {object} errors.Error
// @Failure 500 {object} errors.Error
// @Router /users/{userID}/tags/ [post]
func (ut *UserTagController) DeleteUserTag(c *fiber.Ctx) error {
err := ut.userTagService.DeleteUserTag(c.Params("userID"), c.Params("tagID"))
if err != nil {
return err.FiberError(c)
}

return c.SendStatus(fiber.StatusNoContent)
}
3 changes: 3 additions & 0 deletions backend/src/server/routes/user_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,7 @@ func UserTag(usersRouter fiber.Router, userTagService services.UserTagServiceInt

userTags.Post("/", userTagController.CreateUserTags)
userTags.Get("/", userTagController.GetUserTags)

tagID := userTags.Group("/:tagID")
DOOduneye marked this conversation as resolved.
Show resolved Hide resolved
tagID.Delete("/", userTagController.DeleteUserTag)
DOOduneye marked this conversation as resolved.
Show resolved Hide resolved
}
16 changes: 16 additions & 0 deletions backend/src/services/user_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
type UserTagServiceInterface interface {
GetUserTags(id string) ([]models.Tag, *errors.Error)
CreateUserTags(id string, tagIDs models.CreateUserTagsBody) ([]models.Tag, *errors.Error)
DeleteUserTag(id string, tagID string) *errors.Error
}

type UserTagService struct {
Expand Down Expand Up @@ -52,3 +53,18 @@ func (u *UserTagService) CreateUserTags(id string, tagIDs models.CreateUserTagsB
// Update the user to reflect the new tags:
return transactions.CreateUserTags(u.DB, *idAsUUID, tags)
}

func (u *UserTagService) DeleteUserTag(id string, tagID string) *errors.Error {
// Validate the userID:
userIDAsUUID, err := utilities.ValidateID(id)
if err != nil {
return err
}

tagIDAsUUID, err := utilities.ValidateID(tagID)
if err != nil {
return err
}

return transactions.DeleteUserTag(u.DB, *userIDAsUUID, *tagIDAsUUID)
}
18 changes: 18 additions & 0 deletions backend/src/transactions/user_tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,21 @@ func CreateUserTags(db *gorm.DB, id uuid.UUID, tags []models.Tag) ([]models.Tag,

return tags, nil
}

func DeleteUserTag(db *gorm.DB, id uuid.UUID, tagID uuid.UUID) *errors.Error {
user, err := GetUser(db, id, PreloadTag())
if err != nil {
return err
}

tag, err := GetTag(db, tagID)
if err != nil {
return err
}

if err := db.Model(&user).Association("Tag").Delete(&tag); err != nil {
return &errors.FailedToUpdateUser
}

return nil
}
202 changes: 202 additions & 0 deletions backend/tests/api/user_tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/GenerateNU/sac/backend/src/errors"
"github.com/GenerateNU/sac/backend/src/models"
"github.com/GenerateNU/sac/backend/src/transactions"
h "github.com/GenerateNU/sac/backend/tests/api/helpers"
"github.com/goccy/go-json"
"github.com/gofiber/fiber/v2"
Expand Down Expand Up @@ -368,3 +369,204 @@ func TestGetUserTagsReturnsCorrectList(t *testing.T) {
},
).Close()
}

func TestDeleteUserTagFailsOnNonExistentUser(t *testing.T) {
userID := uuid.New()
tagID := uuid.New()

h.InitTest(t).TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/users/%s/tags/%s/", userID, tagID),
Role: &models.Super,
},
h.ErrorWithTester{
Error: errors.UserNotFound,
Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var dbUser models.User

err := eaa.App.Conn.First(&dbUser, userID).Error

eaa.Assert.Assert(err != nil)
},
},
).Close()
}

func TestDeleteUserTagFailsOnNonExistentTag(t *testing.T) {
tagID := uuid.New()

h.InitTest(t).TestOnErrorAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/users/:userID/tags/%s/", tagID),
Role: &models.Super,
TestUserIDReplaces: h.StringToPointer(":userID"),
},
h.ErrorWithTester{
Error: errors.TagNotFound,
Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var dbTag models.Tag

err := eaa.App.Conn.First(&dbTag, tagID).Error
eaa.Assert.Assert(err != nil)
},
},
).Close()
}

func TestDeleteUserTagFailsOnInvalidUserUUID(t *testing.T) {
appAssert := h.InitTest(t)

badUserUUIDs := []string{
"0",
"-1",
"1.1",
"foo",
"null",
}

for _, badUserUUID := range badUserUUIDs {
appAssert = appAssert.TestOnError(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/users/%s/tags/%s/", badUserUUID, uuid.New()),
Role: &models.Super,
},
errors.FailedToValidateID,
)
}

appAssert.Close()
}

func TestDeleteUserTagFailsOnInvalidTagUUID(t *testing.T) {
appAssert := h.InitTest(t)

badTagUUIDs := []string{
"0",
"-1",
"1.1",
"foo",
"null",
}

for _, badTagUUID := range badTagUUIDs {
appAssert = appAssert.TestOnError(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/users/:userID/tags/%s/", badTagUUID),
Role: &models.Super,
TestUserIDReplaces: h.StringToPointer(":userID"),
},
errors.FailedToValidateID,
)
}

appAssert.Close()
}

func TestDeleteUserTagDoesNotAlterTagListOnNonAssociation(t *testing.T) {
tagUUIDs, appAssert := CreateSetOfTags(h.InitTest(t))
appAssert.Assert.Assert(len(tagUUIDs) > 1)

// Tag to be queried:
tagID := tagUUIDs[0]

// Tags to be added to the user:
tagUUIDs = tagUUIDs[1:]

appAssert.TestOnStatus(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/:userID/tags/",
Body: SampleTagIDsFactory(&tagUUIDs),
Role: &models.Student,
TestUserIDReplaces: h.StringToPointer(":userID"),
},
fiber.StatusCreated,
)

userTagsBeforeDeletion, err := transactions.GetUserTags(appAssert.App.Conn, appAssert.App.TestUser.UUID)
appAssert.Assert.NilError(&err)

appAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/users/:userID/tags/%s/", tagID),
Role: &models.Super,
TestUserIDReplaces: h.StringToPointer(":userID"),
},
h.TesterWithStatus{
Status: fiber.StatusNoContent,
Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var dbUser models.User

err := eaa.App.Conn.Where("id = ?", appAssert.App.TestUser.UUID).Preload("Tag").First(&dbUser).Error
eaa.Assert.NilError(err)

eaa.Assert.Equal(dbUser.Tag, userTagsBeforeDeletion)
},
},
).Close()
}

func TestDeleteUserTagRemovesTagFromUser(t *testing.T) {
tagUUIDs, appAssert := CreateSetOfTags(h.InitTest(t))
appAssert.Assert.Assert(len(tagUUIDs) > 1)

tagID := tagUUIDs[0]

appAssert.TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodPost,
Path: "/api/v1/users/:userID/tags/",
Body: SampleTagIDsFactory(&tagUUIDs),
Role: &models.Student,
TestUserIDReplaces: h.StringToPointer(":userID"),
},
h.TesterWithStatus{
Status: fiber.StatusCreated,
Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var dbUser models.User

err := eaa.App.Conn.Where("id = ?", eaa.App.TestUser.UUID).Preload("Tag").First(&dbUser)
eaa.Assert.NilError(err)

eaa.Assert.Equal(len(dbUser.Tag), len(tagUUIDs))

var dbTag models.Tag

err = eaa.App.Conn.Where("id = ?", tagID).Preload("User").First(&dbTag)
eaa.Assert.NilError(err)

eaa.Assert.Equal(len(dbTag.User), 1)
},
},
).TestOnStatusAndTester(
h.TestRequest{
Method: fiber.MethodDelete,
Path: fmt.Sprintf("/api/v1/users/:userID/tags/%s/", tagID),
Role: &models.Student,
TestUserIDReplaces: h.StringToPointer(":userID"),
},
h.TesterWithStatus{
Status: fiber.StatusNoContent,
Tester: func(eaa h.ExistingAppAssert, resp *http.Response) {
var dbUser models.User

err := eaa.App.Conn.Where("id = ?", eaa.App.TestUser.UUID).Preload("Tag").First(&dbUser)
eaa.Assert.NilError(err)

eaa.Assert.Equal(len(dbUser.Tag), len(tagUUIDs)-1)

var dbTag models.Tag

err = eaa.App.Conn.Where("id = ?", tagID).Preload("User").First(&dbTag)
eaa.Assert.NilError(err)

eaa.Assert.Equal(len(dbTag.User), 0)
},
},
)
}
Loading