diff --git a/controllers/user/musicalinfo/entity.go b/controllers/user/musicalinfo/entity.go new file mode 100644 index 0000000..3e15a72 --- /dev/null +++ b/controllers/user/musicalinfo/entity.go @@ -0,0 +1,10 @@ +package musicalinfo + +type MusicalInfoInput struct { + SkillLevel string `json:"skill_level" validate:"required" updateValidation:"omitempty"` + PrimaryInstrument string `json:"primary_instrument" validate:"required" updateValidation:"omitempty"` + SecondaryInstruments []string `json:"secondary_instruments,omitempty" validate:"omitempty" updateValidation:"omitempty"` + Genres []string `json:"genres" validate:"omitempty" updateValidation:"omitempty"` + FavoriteArtists []string `json:"favorite_artists,omitempty" validate:"omitempty" updateValidation:"omitempty"` + LearningGoals []string `json:"learning_goals,omitempty" validate:"omitempty" updateValidation:"omitempty"` +} diff --git a/controllers/user/musicalinfo/input.go b/controllers/user/musicalinfo/input.go deleted file mode 100644 index 8da6e8e..0000000 --- a/controllers/user/musicalinfo/input.go +++ /dev/null @@ -1,10 +0,0 @@ -package musicalinfo - -type MusicalInfoInput struct { - SkillLevel string `json:"skill_level" updateValidation:"omitempty"` - PrimaryInstrument string `json:"primary_instrument" updateValidation:"omitempty"` - SecondaryInstruments []string `json:"secondary_instruments,omitempty" updateValidation:"omitempty"` - Genres []string `json:"genres" updateValidation:"omitempty"` - FavoriteArtists []string `json:"favorite_artists,omitempty" updateValidation:"omitempty"` - LearningGoals []string `json:"learning_goals,omitempty" updateValidation:"omitempty"` -} diff --git a/controllers/user/musicalinfo/patch-service.go b/controllers/user/musicalinfo/patch-service.go new file mode 100644 index 0000000..28ea775 --- /dev/null +++ b/controllers/user/musicalinfo/patch-service.go @@ -0,0 +1,22 @@ +package musicalinfo + +import ( + model "github.com/donaderoyan/talentgrowth-be/models" + "go.mongodb.org/mongo-driver/bson" +) + +func (s *service) UpdateMusicalInfoService(userID string, input *MusicalInfoInput) (*model.MusicalInfo, error) { + + fields := bson.M{ + "skillLevel": input.SkillLevel, + "primaryInstrument": input.PrimaryInstrument, + "secondaryInstruments": input.SecondaryInstruments, + "genres": input.Genres, + "favoriteArtists": input.FavoriteArtists, + "learningGoals": input.LearningGoals, + } + + result, err := s.repository.UpdateMusicalInfo(userID, fields) + + return result, err +} diff --git a/controllers/user/musicalinfo/post-service.go b/controllers/user/musicalinfo/post-service.go new file mode 100644 index 0000000..2d1a4a6 --- /dev/null +++ b/controllers/user/musicalinfo/post-service.go @@ -0,0 +1,22 @@ +package musicalinfo + +import ( + model "github.com/donaderoyan/talentgrowth-be/models" + "go.mongodb.org/mongo-driver/bson" +) + +func (s *service) CreateMusicalInfoService(userID string, input *MusicalInfoInput) (*model.MusicalInfo, error) { + + fields := bson.M{ + "skillLevel": input.SkillLevel, + "primaryInstrument": input.PrimaryInstrument, + "secondaryInstruments": input.SecondaryInstruments, + "genres": input.Genres, + "favoriteArtists": input.FavoriteArtists, + "learningGoals": input.LearningGoals, + } + + result, err := s.repository.CreateMusicalInfo(userID, fields) + + return result, err +} diff --git a/controllers/user/musicalinfo/repository.go b/controllers/user/musicalinfo/repository.go index 967fec5..d446ed6 100644 --- a/controllers/user/musicalinfo/repository.go +++ b/controllers/user/musicalinfo/repository.go @@ -13,6 +13,7 @@ import ( type Repository interface { UpdateMusicalInfo(userID string, updates bson.M) (*model.MusicalInfo, error) + CreateMusicalInfo(userID string, data bson.M) (*model.MusicalInfo, error) } type repository struct { @@ -28,10 +29,18 @@ type MusicalInfoUpdateError struct { } func (e *MusicalInfoUpdateError) Error() string { - return fmt.Sprintf("Profile update error: %s - %s", e.Code, e.Message) + return fmt.Sprintf("Musical Information update error: %s - %s", e.Code, e.Message) } -func (r *repository) UpdateMusicalInfo(userID string, updates bson.M) (*model.MusicalInfo, error) { +type MusicalInfoCreateError struct { + *util.BaseError +} + +func (e *MusicalInfoCreateError) Error() string { + return fmt.Sprintf("Musical Information update error: %s - %s", e.Code, e.Message) +} + +func (r *repository) CreateMusicalInfo(userID string, data bson.M) (*model.MusicalInfo, error) { // Start a session for transaction session, err := r.db.Client().StartSession() if err != nil { @@ -42,7 +51,7 @@ func (r *repository) UpdateMusicalInfo(userID string, updates bson.M) (*model.Mu // Convert userID to primitive.ObjectID userIDPrimitive, err := primitive.ObjectIDFromHex(userID) if err != nil { - return nil, &MusicalInfoUpdateError{util.NewBaseError("INVALID_USER_ID", "Invalid user ID format")} + return nil, &MusicalInfoCreateError{util.NewBaseError("INVALID_USER_ID", "Invalid user ID format")} } var updatedMusicalInfo model.MusicalInfo @@ -53,35 +62,89 @@ func (r *repository) UpdateMusicalInfo(userID string, updates bson.M) (*model.Mu err = r.db.Collection("users").FindOne(sc, bson.M{"_id": userIDPrimitive}).Decode(&existingUser) if err != nil { if err == mongo.ErrNoDocuments { - return &MusicalInfoUpdateError{util.NewBaseError("USER_NOT_FOUND", "User not found")} + return &MusicalInfoCreateError{util.NewBaseError("USER_NOT_FOUND", "User not found")} } return err } - updates["userID"] = userIDPrimitive + var existingMusicalInfo *model.MusicalInfo + err := r.db.Collection("musicalinfo").FindOne(sc, bson.M{"userID": userIDPrimitive}).Decode(&existingMusicalInfo) + if err == mongo.ErrNoDocuments { + // userID does not exist in musicalinfo, continue with creation + data["userID"] = userIDPrimitive + } else if err != nil { + return err + } else { + // Musical info already exists, return error + return &MusicalInfoCreateError{util.NewBaseError("MUSICAL_INFO_EXIST", "Musical information already exists")} + } + // Insert musical information - result, errInsert := r.db.Collection("musicalinfo").InsertOne(sc, updates) + result, errInsert := r.db.Collection("musicalinfo").InsertOne(sc, data) if errInsert != nil { - return &MusicalInfoUpdateError{util.NewBaseError("ADD_MUSICALINFO_ERROR", "Update musical information failed")} + return &MusicalInfoCreateError{util.NewBaseError("ADD_MUSICALINFO_ERROR", "Create musical information failed")} } // Update the User document with the MusicalInfoID filter := bson.M{"_id": userIDPrimitive} userUpdate := bson.M{ "$set": bson.M{ - "musical_info_id": result.InsertedID.(primitive.ObjectID), + "musicalInfoId": result.InsertedID.(primitive.ObjectID), }, } _, err = r.db.Collection("users").UpdateOne(sc, filter, userUpdate) if err != nil { - return &MusicalInfoUpdateError{util.NewBaseError("ADD_MUSICALINFO_ERROR", "Update musical information failed")} + return &MusicalInfoCreateError{util.NewBaseError("ADD_MUSICALINFO_ERROR", "Create musical information failed")} } // Retrieve the updated MusicalInfo err = r.db.Collection("musicalinfo").FindOne(context.Background(), bson.M{"userID": userIDPrimitive}).Decode(&updatedMusicalInfo) if err != nil { - return &MusicalInfoUpdateError{util.NewBaseError("MUSICAL_INFO_RETRIEVE_ERROR", "Failed to retrieve updated musical information")} + return &MusicalInfoCreateError{util.NewBaseError("MUSICAL_INFO_RETRIEVE_ERROR", "Failed to retrieve updated musical information")} + } + + return nil + }) + + if transactionErr != nil { + return nil, transactionErr + } + + return &updatedMusicalInfo, nil +} + +func (r *repository) UpdateMusicalInfo(userID string, updates bson.M) (*model.MusicalInfo, error) { + // Start a session for transaction + session, err := r.db.Client().StartSession() + if err != nil { + return nil, err + } + defer session.EndSession(context.Background()) + + // Convert userID to primitive.ObjectID + userIDPrimitive, err := primitive.ObjectIDFromHex(userID) + if err != nil { + return nil, &MusicalInfoUpdateError{util.NewBaseError("INVALID_USER_ID", "Invalid user ID format")} + } + + transactionErr := mongo.WithSession(context.Background(), session, func(sc mongo.SessionContext) error { + + // Check if user exists and retrieve current user data + var existingUser model.User + err = r.db.Collection("users").FindOne(sc, bson.M{"_id": userIDPrimitive}).Decode(&existingUser) + if err != nil { + if err == mongo.ErrNoDocuments { + return &MusicalInfoUpdateError{util.NewBaseError("USER_NOT_FOUND", "User not found")} + } + return err + } + + // Insert musical information + _, errInsert := r.db.Collection("musicalinfo").UpdateOne(sc, bson.M{"userID": userIDPrimitive}, bson.M{"$set": updates}) + if errInsert != nil { + fmt.Printf("INSERT MUSICAL INFORMATION ERRORR >>>>>>>>>>> %v", errInsert) + return &MusicalInfoUpdateError{util.NewBaseError("ADD_MUSICALINFO_ERROR", "Update musical information failed")} } return nil @@ -91,5 +154,12 @@ func (r *repository) UpdateMusicalInfo(userID string, updates bson.M) (*model.Mu return nil, transactionErr } + // Retrieve the updated MusicalInfo + var updatedMusicalInfo model.MusicalInfo + err = r.db.Collection("musicalinfo").FindOne(context.Background(), bson.M{"userID": userIDPrimitive}).Decode(&updatedMusicalInfo) + if err != nil { + return nil, &MusicalInfoUpdateError{util.NewBaseError("MUSICAL_INFO_RETRIEVE_ERROR", "Failed to retrieve updated musical information")} + } + return &updatedMusicalInfo, nil } diff --git a/controllers/user/musicalinfo/service.go b/controllers/user/musicalinfo/service.go index 06d4f80..e20c58a 100644 --- a/controllers/user/musicalinfo/service.go +++ b/controllers/user/musicalinfo/service.go @@ -2,11 +2,11 @@ package musicalinfo import ( model "github.com/donaderoyan/talentgrowth-be/models" - "go.mongodb.org/mongo-driver/bson" ) type Service interface { UpdateMusicalInfoService(userID string, input *MusicalInfoInput) (*model.MusicalInfo, error) + CreateMusicalInfoService(userID string, input *MusicalInfoInput) (*model.MusicalInfo, error) } type service struct { @@ -16,19 +16,3 @@ type service struct { func NewMusicalInfoService(repository Repository) *service { return &service{repository: repository} } - -func (s *service) UpdateMusicalInfoService(userID string, input *MusicalInfoInput) (*model.MusicalInfo, error) { - - fields := bson.M{ - "skillLevel": input.SkillLevel, - "primaryInstrument": input.PrimaryInstrument, - "secondaryInstruments": input.SecondaryInstruments, - "genres": input.Genres, - "favoriteArtists": input.FavoriteArtists, - "learningGoals": input.LearningGoals, - } - - result, err := s.repository.UpdateMusicalInfo(userID, fields) - - return result, err -} diff --git a/handlers/user/musicalinfo/handler.go b/handlers/user/musicalinfo/handler.go new file mode 100644 index 0000000..5065267 --- /dev/null +++ b/handlers/user/musicalinfo/handler.go @@ -0,0 +1,11 @@ +package musicalinfohandler + +import musicalinfo "github.com/donaderoyan/talentgrowth-be/controllers/user/musicalinfo" + +type handler struct { + service musicalinfo.Service +} + +func NewMusicalInfohandler(service musicalinfo.Service) *handler { + return &handler{service: service} +} diff --git a/handlers/user/musicalinfo/patch.go b/handlers/user/musicalinfo/patch.go index 04860ce..c6451fe 100644 --- a/handlers/user/musicalinfo/patch.go +++ b/handlers/user/musicalinfo/patch.go @@ -8,14 +8,6 @@ import ( "github.com/gin-gonic/gin" ) -type handler struct { - service musicalinfo.Service -} - -func NewMusicalInfohandler(service musicalinfo.Service) *handler { - return &handler{service: service} -} - // Swagger documentation for UpdateMusicalInfoHandler // @Summary Update musical information (partial update) // @Description Update musical information for a user diff --git a/handlers/user/musicalinfo/post.go b/handlers/user/musicalinfo/post.go new file mode 100644 index 0000000..7cd07db --- /dev/null +++ b/handlers/user/musicalinfo/post.go @@ -0,0 +1,38 @@ +package musicalinfohandler + +import ( + "net/http" + + musicalinfo "github.com/donaderoyan/talentgrowth-be/controllers/user/musicalinfo" + util "github.com/donaderoyan/talentgrowth-be/utils" + "github.com/gin-gonic/gin" +) + +func (h *handler) CreateMusicalInfoHandler(ctx *gin.Context) { + userID := ctx.Param("id") + var input musicalinfo.MusicalInfoInput + + if err := ctx.ShouldBindJSON(&input); err != nil { + util.ErrorResponse(ctx, "Create musical information failed", http.StatusBadRequest, http.MethodPost, err.Error()) + } + + if errValidator := util.Validator(input, "validate"); errValidator != nil { + util.ErrorResponse(ctx, "The input value is invalid", http.StatusBadRequest, http.MethodPost, errValidator.Error()) + return + } + + updateMusicalInfo, errUpdate := h.service.CreateMusicalInfoService(userID, &input) + if errUpdate != nil { + switch errUpdate.(type) { + case *musicalinfo.MusicalInfoCreateError: + util.ErrorResponse(ctx, "Create musical information failed", http.StatusBadRequest, http.MethodPost, errUpdate.Error()) + return + default: + // Handle other unexpected errors + util.ErrorResponse(ctx, "Internal server error", http.StatusInternalServerError, http.MethodPost, errUpdate.Error()) + return + } + } + + util.APIResponse(ctx, "Musical information created successfully", http.StatusOK, http.MethodPost, updateMusicalInfo) +} diff --git a/handlers/user/profile/get.go b/handlers/user/profile/get.go index 4980520..cb60d38 100644 --- a/handlers/user/profile/get.go +++ b/handlers/user/profile/get.go @@ -22,7 +22,7 @@ import ( func (h *handler) GetProfileHandler(ctx *gin.Context) { userID := ctx.Param("id") if userID == "" { - util.ErrorResponse(ctx, "Update profile failed", http.StatusBadRequest, http.MethodPut, "ID is required") + util.ErrorResponse(ctx, "Update profile failed", http.StatusBadRequest, http.MethodGet, "ID is required") return } @@ -30,11 +30,11 @@ func (h *handler) GetProfileHandler(ctx *gin.Context) { if errData != nil { switch errData.(type) { case *profile.GetUserProfileError: - util.ErrorResponse(ctx, "Get user profile failed", http.StatusBadRequest, http.MethodPatch, errData.Error()) + util.ErrorResponse(ctx, "Get user profile failed", http.StatusBadRequest, http.MethodGet, errData.Error()) return default: // Handle other unexpected errors - util.ErrorResponse(ctx, "Internal server error", http.StatusInternalServerError, http.MethodPatch, nil) + util.ErrorResponse(ctx, "Internal server error", http.StatusInternalServerError, http.MethodGet, nil) return } } @@ -51,5 +51,5 @@ func (h *handler) GetProfileHandler(ctx *gin.Context) { "bio": dataUser.Bio, } - util.APIResponse(ctx, "Get user profile successfully", http.StatusOK, http.MethodPatch, responseData) + util.APIResponse(ctx, "Get user profile successfully", http.StatusOK, http.MethodGet, responseData) } diff --git a/routes/user.go b/routes/user.go index e60cebf..62f919a 100644 --- a/routes/user.go +++ b/routes/user.go @@ -29,4 +29,5 @@ func InitUserRoutes(db *mongo.Database, route *gin.Engine) { userGroup.GET("/profile/:id", profileHandler.GetProfileHandler) // musical information userGroup.PATCH("/musicalinfo/:id", musicalInfoHandler.UpdateMusicalInfoHandler) + userGroup.POST("/musicalinfo/:id", musicalInfoHandler.CreateMusicalInfoHandler) }