From 498a888d89195a5a8f919641b52c8cd031874d44 Mon Sep 17 00:00:00 2001
From: Alder Whiteford <alderwhiteford@Alders-MacBook-Pro-2.local>
Date: Fri, 26 Jan 2024 00:51:30 -0500
Subject: [PATCH] SAC-19 User Tag CRUD

---
 backend/src/controllers/user.go  | 22 ++++++++++++++++++++++
 backend/src/models/user.go       |  8 +++++++-
 backend/src/server/server.go     |  5 +++++
 backend/src/services/user.go     | 25 +++++++++++++++++++++++++
 backend/src/transactions/tag.go  | 12 ++++++++++++
 backend/src/transactions/user.go | 27 +++++++++++++++++++++++++++
 6 files changed, 98 insertions(+), 1 deletion(-)

diff --git a/backend/src/controllers/user.go b/backend/src/controllers/user.go
index 1b509bf3d..c6e7c71eb 100644
--- a/backend/src/controllers/user.go
+++ b/backend/src/controllers/user.go
@@ -137,3 +137,25 @@ 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.Tags)
+	if err != nil {
+		return err.FiberError(c)
+	}
+	
+	return c.Status(fiber.StatusCreated).JSON(&tags)
+}
diff --git a/backend/src/models/user.go b/backend/src/models/user.go
index 1f6b3aa7e..52f04308e 100644
--- a/backend/src/models/user.go
+++ b/backend/src/models/user.go
@@ -1,6 +1,8 @@
 package models
 
-import "github.com/GenerateNU/sac/backend/src/types"
+import (
+	"github.com/GenerateNU/sac/backend/src/types"
+)
 
 type UserRole string
 
@@ -76,3 +78,7 @@ type UpdateUserRequestBody struct {
 	College   College `json:"college" validate:"omitempty,oneof=CAMD DMSB KCCS CE BCHS SL CPS CS CSSH"`
 	Year      Year    `json:"year" validate:"omitempty,min=1,max=6"`
 }
+
+type CreateUserTagsBody struct {
+	Tags      []uint  `json:"tags" validate:"required"`
+}
diff --git a/backend/src/server/server.go b/backend/src/server/server.go
index 4d622d646..82c980ff7 100644
--- a/backend/src/server/server.go
+++ b/backend/src/server/server.go
@@ -73,6 +73,11 @@ func userRoutes(router fiber.Router, userService services.UserServiceInterface)
 	users.Get("/:id", userController.GetUser)
 	users.Patch("/:id", userController.UpdateUser)
 	users.Delete("/:id", userController.DeleteUser)
+
+	userTags := users.Group("/:uid/tags")
+	
+	userTags.Post("/", userController.CreateUserTags)
+	userTags.Get("/", userController.GetUserTags)
 }
 
 func categoryRoutes(router fiber.Router, categoryService services.CategoryServiceInterface) {
diff --git a/backend/src/services/user.go b/backend/src/services/user.go
index 324e37d56..e427c40e4 100644
--- a/backend/src/services/user.go
+++ b/backend/src/services/user.go
@@ -19,6 +19,8 @@ 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 []uint) ([]models.Tag, *errors.Error)
 }
 
 type UserService struct {
@@ -107,3 +109,26 @@ func (u *UserService) DeleteUser(id string) *errors.Error {
 
 	return transactions.DeleteUser(u.DB, *idAsInt)
 }
+
+func (u *UserService) GetUserTags(id string) ([]models.Tag, *errors.Error) {
+	idAsInt, err := utilities.ValidateID(id)
+	if err != nil {
+		return nil, err
+	}
+
+	return transactions.GetUserTags(u.DB, *idAsInt)
+}
+
+func (u *UserService) CreateUserTags(id string, tagIDs []uint) ([]models.Tag, *errors.Error) {
+	// Validate the id:
+	idAsInt, err := utilities.ValidateID(id)
+	if err != nil {
+		return nil, err
+	}
+
+	// Retrieve a list of valid tags from the ids:
+	tags, err := transactions.GetTagsByIDs(u.DB, tagIDs)
+
+	// Update the user to reflect the new tags:
+	return transactions.CreateUserTags(u.DB, *idAsInt, tags)
+}
diff --git a/backend/src/transactions/tag.go b/backend/src/transactions/tag.go
index 9214ca683..da14ccdd0 100644
--- a/backend/src/transactions/tag.go
+++ b/backend/src/transactions/tag.go
@@ -55,3 +55,15 @@ func DeleteTag(db *gorm.DB, id uint) *errors.Error {
 
 	return nil
 }
+
+func GetTagsByIDs(db *gorm.DB, selectedTagIDs []uint) ([]models.Tag, *errors.Error) {
+	if len(selectedTagIDs) != 0 {
+		var tags []models.Tag
+		if err := db.Model(models.Tag{}).Where("id IN ?", selectedTagIDs).Find(&tags).Error; err != nil {
+			return nil, &errors.FailedToGetTag
+		}
+		
+		return tags, nil
+	}
+	return []models.Tag{}, nil
+}
diff --git a/backend/src/transactions/user.go b/backend/src/transactions/user.go
index 9fb868ee6..dc666b18b 100644
--- a/backend/src/transactions/user.go
+++ b/backend/src/transactions/user.go
@@ -74,3 +74,30 @@ func DeleteUser(db *gorm.DB, id uint) *errors.Error {
 	}
 	return nil
 }
+
+func GetUserTags(db *gorm.DB, id uint) ([]models.Tag, *errors.Error) {
+	var tags []models.Tag
+
+	user, err := GetUser(db, id)
+	if err != nil {
+		return nil, &errors.UserNotFound
+	}
+
+	if err := db.Model(&user).Association("Tag").Find(&tags) ; err != nil {
+		return nil, &errors.FailedToGetTag
+	}
+	return tags, nil
+}
+
+func CreateUserTags(db *gorm.DB, id uint, tags []models.Tag) ([]models.Tag, *errors.Error) {
+	user, err := GetUser(db, id)
+	if err != nil {
+		return nil, &errors.UserNotFound
+	}
+
+	if err := db.Model(&user).Association("Tag").Replace(tags); err != nil {
+		return nil, &errors.FailedToUpdateUser
+	}
+
+	return tags, nil
+}