From 36f8356ba6cdc532c27a5abcccab22c0dd498ba1 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Thu, 22 Feb 2024 14:05:06 +0100 Subject: [PATCH 1/7] add skill and skillGroup CRUD --- common/skill.go | 33 ++++++++++++ common/user.go | 1 + handlers/skill.go | 129 ++++++++++++++++++++++++++++++++++++++++++++++ managers/skill.go | 116 +++++++++++++++++++++++++++++++++++++++++ models/ranking.go | 19 +++++++ models/skill.go | 24 +++++++++ storage/db.go | 7 ++- 7 files changed, 328 insertions(+), 1 deletion(-) create mode 100644 common/skill.go create mode 100644 handlers/skill.go create mode 100644 managers/skill.go create mode 100644 models/ranking.go create mode 100644 models/skill.go diff --git a/common/skill.go b/common/skill.go new file mode 100644 index 0000000..0cd7df8 --- /dev/null +++ b/common/skill.go @@ -0,0 +1,33 @@ +package common + +type SkillCreationInput struct { + Name string `json:"name"` +} + +type SkillUpdateInput struct { + Name string `json:"name"` +} + +type SkillGroupCreationInput struct { + Name string `json:"name"` +} + +type SkillGroupUpdateInput struct { + Name string `json:"name"` +} + +func NewSkillCreationInput() *SkillCreationInput { + return &SkillCreationInput{} +} + +func NewSkillUpdateInput() *SkillUpdateInput { + return &SkillUpdateInput{} +} + +func NewSkillGroupCreationInput() *SkillGroupCreationInput { + return &SkillGroupCreationInput{} +} + +func NewSkillGroupUpdateInput() *SkillGroupUpdateInput { + return &SkillGroupUpdateInput{} +} diff --git a/common/user.go b/common/user.go index e28d3a7..4e73d47 100644 --- a/common/user.go +++ b/common/user.go @@ -4,6 +4,7 @@ type UserCreationInput struct { FullName string `json:"fullName"` Email string `json:"email"` } + type UserUpdateInput struct { FullName string `json:"fullName"` Email string `json:"email"` diff --git a/handlers/skill.go b/handlers/skill.go new file mode 100644 index 0000000..93dcbe3 --- /dev/null +++ b/handlers/skill.go @@ -0,0 +1,129 @@ +package handlers + +import ( + "fmt" + "net/http" + + "github.com/buildfromzero/skill-map/common" + "github.com/buildfromzero/skill-map/managers" + "github.com/gin-gonic/gin" +) + +type SkillHandler struct { + groupName string + skillManager managers.SkillManager +} + +func NewSkillHandlerFrom(skillManager managers.SkillManager) *SkillHandler { + return &SkillHandler{ + "api/", + skillManager, + } +} + +func (handler *SkillHandler) RegisterApis(r *gin.Engine) { + skillGroup := r.Group(handler.groupName) + + skillGroup.GET("skills/", handler.List) + skillGroup.POST("skills/", handler.Create) + skillGroup.GET("skills/:skillid/", handler.Detail) + skillGroup.DELETE("skills/:skillid/", handler.Delete) + skillGroup.PATCH("skills/:skillid/", handler.Update) +} + +func (handler *SkillHandler) Create(ctx *gin.Context) { + + inputData := common.NewSkillCreationInput() + + err := ctx.BindJSON(&inputData) + + if err != nil { + common.BadResponse(ctx, "Failed to bind data") + return + } + + newSkill, err := handler.skillManager.Create(inputData) + + if err != nil { + common.BadResponse(ctx, "failed to create user") + return + } + + ctx.JSON(http.StatusOK, newSkill) +} + +func (handler *SkillHandler) List(ctx *gin.Context) { + + allSkills, err := handler.skillManager.List() + + if err != nil { + common.BadResponse(ctx, "failed to get all skills") + return + } + + ctx.JSON(http.StatusOK, allSkills) +} + +func (handler *SkillHandler) Detail(ctx *gin.Context) { + + skillId, ok := ctx.Params.Get("skillid") + + if !ok { + fmt.Println("invalid skill id") + } + skill, err := handler.skillManager.Get(skillId) + + if skill.ID == 0 { + common.BadResponse(ctx, "no skill present") + return + } + + if err != nil { + common.BadResponse(ctx, "failed to get skill") + } + + ctx.JSON(http.StatusOK, skill) +} + +func (handler *SkillHandler) Delete(ctx *gin.Context) { + + skillId, ok := ctx.Params.Get("skillid") + + if !ok { + common.BadResponse(ctx, "invalid userid") + } + err := handler.skillManager.Delete(skillId) + + if err != nil { + common.BadResponse(ctx, "failed to delete skill") + } + + common.SuccessResponse(ctx, "deleted Skill") +} + +func (handler *SkillHandler) Update(ctx *gin.Context) { + + skillId, ok := ctx.Params.Get("skillid") + + if !ok { + common.BadResponse(ctx, "failed to delete skill") + } + + inputData := common.NewSkillUpdateInput() + + err := ctx.BindJSON(&inputData) + + if err != nil { + common.BadResponse(ctx, "failed to bind data") + return + } + + user, err := handler.skillManager.Update(skillId, inputData) + + if err != nil { + common.BadResponse(ctx, "failed to update user") + return + } + + ctx.JSON(http.StatusOK, user) +} diff --git a/managers/skill.go b/managers/skill.go new file mode 100644 index 0000000..fd80797 --- /dev/null +++ b/managers/skill.go @@ -0,0 +1,116 @@ +package managers + +import ( + "errors" + + "github.com/buildfromzero/skill-map/common" + "github.com/buildfromzero/skill-map/models" + "github.com/buildfromzero/skill-map/storage" +) + +type SkillManager interface { + Create(inputData *common.SkillCreationInput) (*models.Skill, error) + List() ([]models.Skill, error) + Get(id string) (models.Skill, error) + Update(id string, inputData *common.SkillUpdateInput) (*models.Skill, error) + Delete(id string) error +} + +type skillManager struct { + // DatabaseDriver + // dbClient +} + +func NewSkillManager() SkillManager { + return &skillManager{} +} + +func (skillMgr *skillManager) Create(inputData *common.SkillCreationInput) (*models.Skill, error) { + newSkillObj := &models.Skill{Name: inputData.Name} + storage.DB.Create(newSkillObj) + + if newSkillObj.ID == 0 { + return nil, errors.New("skill creation failed") + } + + return newSkillObj, nil +} + +func (skillMgr *skillManager) List() ([]models.Skill, error) { + skillObj := []models.Skill{} + + storage.DB.Find(&skillObj) + // TODO: handle errors + return skillObj, nil +} + +func (skillMgr *skillManager) Get(id string) (models.Skill, error) { + skillObj := models.Skill{} + + storage.DB.First(&skillObj, id) + + return skillObj, nil +} + +func (skillMgr *skillManager) Update(id string, inputData *common.SkillUpdateInput) (*models.Skill, error) { + skillObj := models.Skill{} + + storage.DB.First(&skillObj, id) + storage.DB.Model(&skillObj).Updates(models.Skill{Name: inputData.Name}) + // TODO: handle errors + return &skillObj, nil +} + +func (skillMgr *skillManager) Delete(id string) error { + skillObj := models.Skill{} + + storage.DB.First(&skillObj, id) + storage.DB.Delete(&skillObj) + // TODO: handle errors + return nil +} + +func (skillMgr *skillManager) CreateGroup(inputData *common.SkillGroupCreationInput) (*models.SkillGroup, error) { + newSkillGroupObj := &models.SkillGroup{Name: inputData.Name} + storage.DB.Create(newSkillGroupObj) + + if newSkillGroupObj.ID == 0 { + return nil, errors.New("skill group creation failed") + } + + return newSkillGroupObj, nil +} + +func (skillMgr *skillManager) ListGroup() ([]models.SkillGroup, error) { + skillGroupObj := []models.SkillGroup{} + + storage.DB.Find(&skillGroupObj) + // TODO: handle errors + return skillGroupObj, nil +} + +func (skillMgr *skillManager) GetGroup(id string) (models.SkillGroup, error) { + skillGroupObj := models.SkillGroup{} + + storage.DB.First(&skillGroupObj, id) + + return skillGroupObj, nil +} + +func (skillMgr *skillManager) UpdateGroup(id string, inputData *common.SkillGroupUpdateInput) (*models.SkillGroup, error) { + skillGroupObj := models.SkillGroup{} + + storage.DB.First(&skillGroupObj, id) + storage.DB.Model(&skillGroupObj).Updates(models.Skill{Name: inputData.Name}) + // TODO: handle errors + return &skillGroupObj, nil +} + +func (skillMgr *skillManager) DeleteGroup(id string) error { + skillGroupObj := models.SkillGroup{} + + storage.DB.First(&skillGroupObj, id) + storage.DB.Delete(&skillGroupObj) + // TODO: handle errors + return nil +} diff --git a/models/ranking.go b/models/ranking.go new file mode 100644 index 0000000..a1cedf2 --- /dev/null +++ b/models/ranking.go @@ -0,0 +1,19 @@ +package models + +import ( + "time" + + "gorm.io/gorm" +) + +type UserSkillRank struct { + ID uint `gorm:"primarykey" json:"id"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` + UserID int + User User + SkillID int + Skill Skill + Rank uint `json:"rank"` +} diff --git a/models/skill.go b/models/skill.go new file mode 100644 index 0000000..24c05a1 --- /dev/null +++ b/models/skill.go @@ -0,0 +1,24 @@ +package models + +import ( + "time" + + "gorm.io/gorm" +) + +type Skill struct { + ID uint `gorm:"primarykey" json:"id"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` + Name string `json:"name"` +} + +type SkillGroup struct { + ID uint `gorm:"primarykey" json:"id"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` + Name string `json:"name"` + Skills []Skill `gorm:"many2many:skillgroup_skills;" json:"skills"` +} diff --git a/storage/db.go b/storage/db.go index bb63b7b..995238f 100644 --- a/storage/db.go +++ b/storage/db.go @@ -15,5 +15,10 @@ func InitializeDatabase() { panic("failed to connect database") } - DB.AutoMigrate(&models.User{}) + DB.AutoMigrate( + &models.User{}, + &models.Skill{}, + &models.SkillGroup{}, + &models.UserSkillRank{}, + ) } From 8115396b121dff424a43e572c8b08176bb10a045 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Thu, 22 Feb 2024 14:39:58 +0100 Subject: [PATCH 2/7] SKILL-19: add CRUD for Skill Group --- handlers/skill.go | 133 ++++++++++++++++++++++++++++++++++++++++------ handlers/user.go | 22 ++++---- managers/skill.go | 7 +++ 3 files changed, 136 insertions(+), 26 deletions(-) diff --git a/handlers/skill.go b/handlers/skill.go index 93dcbe3..584dc4e 100644 --- a/handlers/skill.go +++ b/handlers/skill.go @@ -23,15 +23,21 @@ func NewSkillHandlerFrom(skillManager managers.SkillManager) *SkillHandler { func (handler *SkillHandler) RegisterApis(r *gin.Engine) { skillGroup := r.Group(handler.groupName) - - skillGroup.GET("skills/", handler.List) - skillGroup.POST("skills/", handler.Create) - skillGroup.GET("skills/:skillid/", handler.Detail) - skillGroup.DELETE("skills/:skillid/", handler.Delete) - skillGroup.PATCH("skills/:skillid/", handler.Update) + // skill apis + skillGroup.GET("skills/", handler.ListSkills) + skillGroup.POST("skills/", handler.CreateSkill) + skillGroup.GET("skills/:skillid/", handler.SkillDetail) + skillGroup.DELETE("skills/:skillid/", handler.DeleteSkill) + skillGroup.PATCH("skills/:skillid/", handler.UpdateSkill) + // skill group apis + skillGroup.GET("skill-groups/", handler.ListSkillGroups) + skillGroup.POST("skill-groups/", handler.CreateSkillGroup) + skillGroup.GET("skill-groups/:groupid/", handler.SkillGroupDetail) + skillGroup.DELETE("skill-groups/:groupid/", handler.DeleteSkillGroup) + skillGroup.PATCH("skill-groups/:groupid/", handler.UpdateSkillGroup) } -func (handler *SkillHandler) Create(ctx *gin.Context) { +func (handler *SkillHandler) CreateSkill(ctx *gin.Context) { inputData := common.NewSkillCreationInput() @@ -45,14 +51,14 @@ func (handler *SkillHandler) Create(ctx *gin.Context) { newSkill, err := handler.skillManager.Create(inputData) if err != nil { - common.BadResponse(ctx, "failed to create user") + common.BadResponse(ctx, "failed to create skill") return } ctx.JSON(http.StatusOK, newSkill) } -func (handler *SkillHandler) List(ctx *gin.Context) { +func (handler *SkillHandler) ListSkills(ctx *gin.Context) { allSkills, err := handler.skillManager.List() @@ -64,7 +70,7 @@ func (handler *SkillHandler) List(ctx *gin.Context) { ctx.JSON(http.StatusOK, allSkills) } -func (handler *SkillHandler) Detail(ctx *gin.Context) { +func (handler *SkillHandler) SkillDetail(ctx *gin.Context) { skillId, ok := ctx.Params.Get("skillid") @@ -85,7 +91,7 @@ func (handler *SkillHandler) Detail(ctx *gin.Context) { ctx.JSON(http.StatusOK, skill) } -func (handler *SkillHandler) Delete(ctx *gin.Context) { +func (handler *SkillHandler) DeleteSkill(ctx *gin.Context) { skillId, ok := ctx.Params.Get("skillid") @@ -101,7 +107,7 @@ func (handler *SkillHandler) Delete(ctx *gin.Context) { common.SuccessResponse(ctx, "deleted Skill") } -func (handler *SkillHandler) Update(ctx *gin.Context) { +func (handler *SkillHandler) UpdateSkill(ctx *gin.Context) { skillId, ok := ctx.Params.Get("skillid") @@ -118,12 +124,109 @@ func (handler *SkillHandler) Update(ctx *gin.Context) { return } - user, err := handler.skillManager.Update(skillId, inputData) + skill, err := handler.skillManager.Update(skillId, inputData) + + if err != nil { + common.BadResponse(ctx, "failed to update skill") + return + } + + ctx.JSON(http.StatusOK, skill) +} + +func (handler *SkillHandler) CreateSkillGroup(ctx *gin.Context) { + + inputData := common.NewSkillGroupCreationInput() + + err := ctx.BindJSON(&inputData) + + if err != nil { + common.BadResponse(ctx, "failed to bind data") + return + } + + newSkillGroup, err := handler.skillManager.CreateGroup(inputData) + + if err != nil { + common.BadResponse(ctx, "failed to create Skill Group") + return + } + + ctx.JSON(http.StatusOK, newSkillGroup) +} + +func (handler *SkillHandler) ListSkillGroups(ctx *gin.Context) { + + skillGroups, err := handler.skillManager.ListGroup() + + if err != nil { + common.BadResponse(ctx, "failed to get all Skill Groups") + return + } + + ctx.JSON(http.StatusOK, skillGroups) +} + +func (handler *SkillHandler) SkillGroupDetail(ctx *gin.Context) { + + groupId, ok := ctx.Params.Get("groupid") + + if !ok { + fmt.Println("invalid group id") + } + skillGroup, err := handler.skillManager.GetGroup(groupId) + + if skillGroup.ID == 0 { + common.BadResponse(ctx, "no group present with given id") + return + } + + if err != nil { + common.BadResponse(ctx, "failed to get Skill Group") + } + + ctx.JSON(http.StatusOK, skillGroup) +} + +func (handler *SkillHandler) DeleteSkillGroup(ctx *gin.Context) { + + groupId, ok := ctx.Params.Get("groupid") + + if !ok { + common.BadResponse(ctx, "invalid Group id") + } + err := handler.skillManager.DeleteGroup(groupId) + + if err != nil { + common.BadResponse(ctx, "failed to delete Skill Group") + } + + common.SuccessResponse(ctx, "deleted Skill Group") +} + +func (handler *SkillHandler) UpdateSkillGroup(ctx *gin.Context) { + + groupId, ok := ctx.Params.Get("groupid") + + if !ok { + common.BadResponse(ctx, "failed to get group id") + } + + inputData := common.NewSkillGroupUpdateInput() + + err := ctx.BindJSON(&inputData) + + if err != nil { + common.BadResponse(ctx, "failed to bind data") + return + } + + skillGroup, err := handler.skillManager.UpdateGroup(groupId, inputData) if err != nil { - common.BadResponse(ctx, "failed to update user") + common.BadResponse(ctx, "failed to update Skill Group") return } - ctx.JSON(http.StatusOK, user) + ctx.JSON(http.StatusOK, skillGroup) } diff --git a/handlers/user.go b/handlers/user.go index 287e6db..a083207 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -24,21 +24,21 @@ func NewUserHandlerFrom(userManager managers.UserManager) *UserHandler { func (handler *UserHandler) RegisterUserApis(r *gin.Engine) { userGroup := r.Group(handler.groupName) - userGroup.GET("", handler.List) - userGroup.POST("", handler.Create) - userGroup.GET(":userid/", handler.Detail) - userGroup.DELETE(":userid/", handler.Delete) - userGroup.PATCH(":userid/", handler.Update) + userGroup.GET("", handler.ListUser) + userGroup.POST("", handler.CreateUser) + userGroup.GET(":userid/", handler.UserDetail) + userGroup.DELETE(":userid/", handler.DeleteUser) + userGroup.PATCH(":userid/", handler.UpdateUser) } -func (handler *UserHandler) Create(ctx *gin.Context) { +func (handler *UserHandler) CreateUser(ctx *gin.Context) { userData := common.NewUserCreationInput() err := ctx.BindJSON(&userData) if err != nil { - common.BadResponse(ctx, "Failed to bind data") + common.BadResponse(ctx, "failed to bind data") return } @@ -52,7 +52,7 @@ func (handler *UserHandler) Create(ctx *gin.Context) { ctx.JSON(http.StatusOK, newUser) } -func (handler *UserHandler) List(ctx *gin.Context) { +func (handler *UserHandler) ListUser(ctx *gin.Context) { allUsers, err := handler.userManager.List() @@ -64,7 +64,7 @@ func (handler *UserHandler) List(ctx *gin.Context) { ctx.JSON(http.StatusOK, allUsers) } -func (handler *UserHandler) Detail(ctx *gin.Context) { +func (handler *UserHandler) UserDetail(ctx *gin.Context) { userId, ok := ctx.Params.Get("userid") @@ -85,7 +85,7 @@ func (handler *UserHandler) Detail(ctx *gin.Context) { ctx.JSON(http.StatusOK, user) } -func (handler *UserHandler) Delete(ctx *gin.Context) { +func (handler *UserHandler) DeleteUser(ctx *gin.Context) { userId, ok := ctx.Params.Get("userid") @@ -101,7 +101,7 @@ func (handler *UserHandler) Delete(ctx *gin.Context) { common.SuccessResponse(ctx, "Deleted user") } -func (handler *UserHandler) Update(ctx *gin.Context) { +func (handler *UserHandler) UpdateUser(ctx *gin.Context) { userId, ok := ctx.Params.Get("userid") diff --git a/managers/skill.go b/managers/skill.go index fd80797..9dc9156 100644 --- a/managers/skill.go +++ b/managers/skill.go @@ -9,11 +9,18 @@ import ( ) type SkillManager interface { + // Skill Create(inputData *common.SkillCreationInput) (*models.Skill, error) List() ([]models.Skill, error) Get(id string) (models.Skill, error) Update(id string, inputData *common.SkillUpdateInput) (*models.Skill, error) Delete(id string) error + // Skill Group + CreateGroup(inputData *common.SkillGroupCreationInput) (*models.SkillGroup, error) + ListGroup() ([]models.SkillGroup, error) + GetGroup(id string) (models.SkillGroup, error) + UpdateGroup(id string, inputData *common.SkillGroupUpdateInput) (*models.SkillGroup, error) + DeleteGroup(id string) error } type skillManager struct { From 0be4754077780b9233cba218f9611a5edde79f58 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Thu, 22 Feb 2024 14:46:49 +0100 Subject: [PATCH 3/7] SKILL-19: register Skill manager Endpoints --- handlers/skill.go | 2 +- handlers/user.go | 2 +- main.go | 10 +++++++--- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/handlers/skill.go b/handlers/skill.go index 584dc4e..87aee37 100644 --- a/handlers/skill.go +++ b/handlers/skill.go @@ -21,7 +21,7 @@ func NewSkillHandlerFrom(skillManager managers.SkillManager) *SkillHandler { } } -func (handler *SkillHandler) RegisterApis(r *gin.Engine) { +func (handler *SkillHandler) RegisterEndpoints(r *gin.Engine) { skillGroup := r.Group(handler.groupName) // skill apis skillGroup.GET("skills/", handler.ListSkills) diff --git a/handlers/user.go b/handlers/user.go index a083207..5daa453 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -21,7 +21,7 @@ func NewUserHandlerFrom(userManager managers.UserManager) *UserHandler { } } -func (handler *UserHandler) RegisterUserApis(r *gin.Engine) { +func (handler *UserHandler) RegisterEndpoints(r *gin.Engine) { userGroup := r.Group(handler.groupName) userGroup.GET("", handler.ListUser) diff --git a/main.go b/main.go index 8ba9c0b..ea98089 100644 --- a/main.go +++ b/main.go @@ -15,15 +15,19 @@ func init() { } func main() { - fmt.Println("Skill Map") + fmt.Println("Stating Skill Map...") router := gin.Default() - router.Use(cors.Default()) userManger := managers.NewUserManager() + skillManger := managers.NewSkillManager() + userHandler := handlers.NewUserHandlerFrom(userManger) - userHandler.RegisterUserApis(router) + userHandler.RegisterEndpoints(router) + + skillHandler := handlers.NewSkillHandlerFrom(skillManger) + skillHandler.RegisterEndpoints(router) router.Run() // listen and serve on 0.0.0.0:8080 } From 0be87c22ba9f22d25ccc0e8f0287db5230c698a9 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Thu, 22 Feb 2024 15:05:25 +0100 Subject: [PATCH 4/7] SKILL-19: handle not found error for skill manager --- handlers/skill.go | 10 +++++++--- managers/skill.go | 43 ++++++++++++++++++++++++++++++++----------- 2 files changed, 39 insertions(+), 14 deletions(-) diff --git a/handlers/skill.go b/handlers/skill.go index 87aee37..269c14d 100644 --- a/handlers/skill.go +++ b/handlers/skill.go @@ -76,6 +76,7 @@ func (handler *SkillHandler) SkillDetail(ctx *gin.Context) { if !ok { fmt.Println("invalid skill id") + return } skill, err := handler.skillManager.Get(skillId) @@ -97,11 +98,13 @@ func (handler *SkillHandler) DeleteSkill(ctx *gin.Context) { if !ok { common.BadResponse(ctx, "invalid userid") + return } err := handler.skillManager.Delete(skillId) if err != nil { - common.BadResponse(ctx, "failed to delete skill") + common.BadResponse(ctx, err.Error()) + return } common.SuccessResponse(ctx, "deleted Skill") @@ -198,7 +201,8 @@ func (handler *SkillHandler) DeleteSkillGroup(ctx *gin.Context) { err := handler.skillManager.DeleteGroup(groupId) if err != nil { - common.BadResponse(ctx, "failed to delete Skill Group") + common.BadResponse(ctx, err.Error()) + return } common.SuccessResponse(ctx, "deleted Skill Group") @@ -217,7 +221,7 @@ func (handler *SkillHandler) UpdateSkillGroup(ctx *gin.Context) { err := ctx.BindJSON(&inputData) if err != nil { - common.BadResponse(ctx, "failed to bind data") + common.BadResponse(ctx, err.Error()) return } diff --git a/managers/skill.go b/managers/skill.go index 9dc9156..ae3a13c 100644 --- a/managers/skill.go +++ b/managers/skill.go @@ -18,7 +18,7 @@ type SkillManager interface { // Skill Group CreateGroup(inputData *common.SkillGroupCreationInput) (*models.SkillGroup, error) ListGroup() ([]models.SkillGroup, error) - GetGroup(id string) (models.SkillGroup, error) + GetGroup(id string) (*models.SkillGroup, error) UpdateGroup(id string, inputData *common.SkillGroupUpdateInput) (*models.SkillGroup, error) DeleteGroup(id string) error } @@ -60,18 +60,26 @@ func (skillMgr *skillManager) Get(id string) (models.Skill, error) { } func (skillMgr *skillManager) Update(id string, inputData *common.SkillUpdateInput) (*models.Skill, error) { - skillObj := models.Skill{} + skillObj := &models.Skill{} - storage.DB.First(&skillObj, id) - storage.DB.Model(&skillObj).Updates(models.Skill{Name: inputData.Name}) + storage.DB.First(skillObj, id) + + if skillObj.ID == 0 { + return nil, errors.New("item does not exist") + } + + storage.DB.Model(skillObj).Updates(models.Skill{Name: inputData.Name}) // TODO: handle errors - return &skillObj, nil + return skillObj, nil } func (skillMgr *skillManager) Delete(id string) error { skillObj := models.Skill{} storage.DB.First(&skillObj, id) + if skillObj.ID == 0 { + return errors.New("item does not exist") + } storage.DB.Delete(&skillObj) // TODO: handle errors return nil @@ -96,27 +104,40 @@ func (skillMgr *skillManager) ListGroup() ([]models.SkillGroup, error) { return skillGroupObj, nil } -func (skillMgr *skillManager) GetGroup(id string) (models.SkillGroup, error) { - skillGroupObj := models.SkillGroup{} +func (skillMgr *skillManager) GetGroup(id string) (*models.SkillGroup, error) { + skillGroupObj := &models.SkillGroup{} - storage.DB.First(&skillGroupObj, id) + storage.DB.First(skillGroupObj, id) + + if skillGroupObj.ID == 0 { + return nil, errors.New("item does not exist") + } return skillGroupObj, nil } func (skillMgr *skillManager) UpdateGroup(id string, inputData *common.SkillGroupUpdateInput) (*models.SkillGroup, error) { - skillGroupObj := models.SkillGroup{} + skillGroupObj := &models.SkillGroup{} + + storage.DB.First(skillGroupObj, id) + + if skillGroupObj.ID == 0 { + return nil, errors.New("item does not exist") + } - storage.DB.First(&skillGroupObj, id) storage.DB.Model(&skillGroupObj).Updates(models.Skill{Name: inputData.Name}) // TODO: handle errors - return &skillGroupObj, nil + return skillGroupObj, nil } func (skillMgr *skillManager) DeleteGroup(id string) error { skillGroupObj := models.SkillGroup{} storage.DB.First(&skillGroupObj, id) + + if skillGroupObj.ID == 0 { + return errors.New("item does not exist") + } storage.DB.Delete(&skillGroupObj) // TODO: handle errors return nil From 92218d40da105a3f1dac8b23711787c7be404d71 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Thu, 22 Feb 2024 15:11:32 +0100 Subject: [PATCH 5/7] SKILL-19: more error handling --- handlers/user.go | 11 +++-------- managers/user.go | 19 ++++++++++++++++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/handlers/user.go b/handlers/user.go index 5daa453..f6bb2f2 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -73,13 +73,8 @@ func (handler *UserHandler) UserDetail(ctx *gin.Context) { } user, err := handler.userManager.Get(userId) - if user.ID == 0 { - common.BadResponse(ctx, "no user present") - return - } - if err != nil { - common.BadResponse(ctx, "failed to get user") + common.BadResponse(ctx, err.Error()) } ctx.JSON(http.StatusOK, user) @@ -95,7 +90,7 @@ func (handler *UserHandler) DeleteUser(ctx *gin.Context) { err := handler.userManager.Delete(userId) if err != nil { - common.BadResponse(ctx, "failed to delete user") + common.BadResponse(ctx, err.Error()) } common.SuccessResponse(ctx, "Deleted user") @@ -121,7 +116,7 @@ func (handler *UserHandler) UpdateUser(ctx *gin.Context) { user, err := handler.userManager.Update(userId, userData) if err != nil { - common.BadResponse(ctx, "failed to update user") + common.BadResponse(ctx, err.Error()) return } diff --git a/managers/user.go b/managers/user.go index 1a0a34a..6f38496 100644 --- a/managers/user.go +++ b/managers/user.go @@ -11,7 +11,7 @@ import ( type UserManager interface { Create(userData *common.UserCreationInput) (*models.User, error) List() ([]models.User, error) - Get(id string) (models.User, error) + Get(id string) (*models.User, error) Update(userId string, userData *common.UserUpdateInput) (*models.User, error) Delete(id string) error } @@ -42,10 +42,13 @@ func (userMgr *userManager) List() ([]models.User, error) { return users, nil } -func (userMgr *userManager) Get(id string) (models.User, error) { - user := models.User{} +func (userMgr *userManager) Get(id string) (*models.User, error) { + user := &models.User{} storage.DB.First(&user, id) + if user.ID == 0 { + return nil, errors.New("no user found") + } return user, nil } @@ -55,6 +58,11 @@ func (userMgr *userManager) Update(userId string, userData *common.UserUpdateInp user := models.User{} storage.DB.First(&user, userId) + + if user.ID == 0 { + return nil, errors.New("no user found") + } + storage.DB.Model(&user).Updates(models.User{FullName: userData.FullName, Email: userData.Email}) return &user, nil @@ -64,6 +72,11 @@ func (userMgr *userManager) Delete(id string) error { user := models.User{} storage.DB.First(&user, id) + + if user.ID == 0 { + return errors.New("no user found") + } + storage.DB.Delete(&user) return nil } From 64b5c40e8aa400e60f9cfeaf0669572c00f44098 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Thu, 22 Feb 2024 15:25:35 +0100 Subject: [PATCH 6/7] SKILL-19: error handling and added missing return statements --- handlers/skill.go | 16 ++++++---------- handlers/user.go | 5 +++++ managers/skill.go | 10 +++++++--- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/handlers/skill.go b/handlers/skill.go index 269c14d..87fbfca 100644 --- a/handlers/skill.go +++ b/handlers/skill.go @@ -80,13 +80,9 @@ func (handler *SkillHandler) SkillDetail(ctx *gin.Context) { } skill, err := handler.skillManager.Get(skillId) - if skill.ID == 0 { - common.BadResponse(ctx, "no skill present") - return - } - if err != nil { common.BadResponse(ctx, "failed to get skill") + return } ctx.JSON(http.StatusOK, skill) @@ -116,6 +112,7 @@ func (handler *SkillHandler) UpdateSkill(ctx *gin.Context) { if !ok { common.BadResponse(ctx, "failed to delete skill") + return } inputData := common.NewSkillUpdateInput() @@ -176,16 +173,13 @@ func (handler *SkillHandler) SkillGroupDetail(ctx *gin.Context) { if !ok { fmt.Println("invalid group id") - } - skillGroup, err := handler.skillManager.GetGroup(groupId) - - if skillGroup.ID == 0 { - common.BadResponse(ctx, "no group present with given id") return } + skillGroup, err := handler.skillManager.GetGroup(groupId) if err != nil { common.BadResponse(ctx, "failed to get Skill Group") + return } ctx.JSON(http.StatusOK, skillGroup) @@ -197,6 +191,7 @@ func (handler *SkillHandler) DeleteSkillGroup(ctx *gin.Context) { if !ok { common.BadResponse(ctx, "invalid Group id") + return } err := handler.skillManager.DeleteGroup(groupId) @@ -214,6 +209,7 @@ func (handler *SkillHandler) UpdateSkillGroup(ctx *gin.Context) { if !ok { common.BadResponse(ctx, "failed to get group id") + return } inputData := common.NewSkillGroupUpdateInput() diff --git a/handlers/user.go b/handlers/user.go index f6bb2f2..b0b9626 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -70,11 +70,13 @@ func (handler *UserHandler) UserDetail(ctx *gin.Context) { if !ok { fmt.Println("invalid userid") + return } user, err := handler.userManager.Get(userId) if err != nil { common.BadResponse(ctx, err.Error()) + return } ctx.JSON(http.StatusOK, user) @@ -86,11 +88,13 @@ func (handler *UserHandler) DeleteUser(ctx *gin.Context) { if !ok { common.BadResponse(ctx, "invalid userid") + return } err := handler.userManager.Delete(userId) if err != nil { common.BadResponse(ctx, err.Error()) + return } common.SuccessResponse(ctx, "Deleted user") @@ -102,6 +106,7 @@ func (handler *UserHandler) UpdateUser(ctx *gin.Context) { if !ok { common.BadResponse(ctx, "failed to delete user") + return } userData := common.NewUserUpdateInput() diff --git a/managers/skill.go b/managers/skill.go index ae3a13c..ea406dc 100644 --- a/managers/skill.go +++ b/managers/skill.go @@ -12,7 +12,7 @@ type SkillManager interface { // Skill Create(inputData *common.SkillCreationInput) (*models.Skill, error) List() ([]models.Skill, error) - Get(id string) (models.Skill, error) + Get(id string) (*models.Skill, error) Update(id string, inputData *common.SkillUpdateInput) (*models.Skill, error) Delete(id string) error // Skill Group @@ -51,11 +51,15 @@ func (skillMgr *skillManager) List() ([]models.Skill, error) { return skillObj, nil } -func (skillMgr *skillManager) Get(id string) (models.Skill, error) { - skillObj := models.Skill{} +func (skillMgr *skillManager) Get(id string) (*models.Skill, error) { + skillObj := &models.Skill{} storage.DB.First(&skillObj, id) + if skillObj.ID == 0 { + return nil, errors.New("no skill found") + } + return skillObj, nil } From 6b4a0808247692ba3f2816b77ad544b8c01c160e Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Thu, 22 Feb 2024 16:32:42 +0100 Subject: [PATCH 7/7] SKILL-19: fix many to many patching and listing --- common/skill.go | 3 ++- managers/skill.go | 50 +++++++++++++++++++++++++++++++++++------------ models/skill.go | 8 ++++++++ 3 files changed, 48 insertions(+), 13 deletions(-) diff --git a/common/skill.go b/common/skill.go index 0cd7df8..65d67b7 100644 --- a/common/skill.go +++ b/common/skill.go @@ -13,7 +13,8 @@ type SkillGroupCreationInput struct { } type SkillGroupUpdateInput struct { - Name string `json:"name"` + Name string `json:"name"` + Skills []int `json:"skills"` } func NewSkillCreationInput() *SkillCreationInput { diff --git a/managers/skill.go b/managers/skill.go index ea406dc..86bb131 100644 --- a/managers/skill.go +++ b/managers/skill.go @@ -2,6 +2,7 @@ package managers import ( "errors" + "fmt" "github.com/buildfromzero/skill-map/common" "github.com/buildfromzero/skill-map/models" @@ -52,9 +53,9 @@ func (skillMgr *skillManager) List() ([]models.Skill, error) { } func (skillMgr *skillManager) Get(id string) (*models.Skill, error) { - skillObj := &models.Skill{} + skillObj := models.NewSkill() - storage.DB.First(&skillObj, id) + storage.DB.First(skillObj, id) if skillObj.ID == 0 { return nil, errors.New("no skill found") @@ -101,17 +102,18 @@ func (skillMgr *skillManager) CreateGroup(inputData *common.SkillGroupCreationIn } func (skillMgr *skillManager) ListGroup() ([]models.SkillGroup, error) { - skillGroupObj := []models.SkillGroup{} + skillGroups := []models.SkillGroup{} - storage.DB.Find(&skillGroupObj) + // storage.DB.Find(&skillGroupObj) + storage.DB.Model(&skillGroups).Preload("Skills").Find(&skillGroups) // TODO: handle errors - return skillGroupObj, nil + return skillGroups, nil } func (skillMgr *skillManager) GetGroup(id string) (*models.SkillGroup, error) { - skillGroupObj := &models.SkillGroup{} + skillGroupObj := models.NewSkillGroup() - storage.DB.First(skillGroupObj, id) + storage.DB.Model(skillGroupObj).Preload("Skills").Find(skillGroupObj) if skillGroupObj.ID == 0 { return nil, errors.New("item does not exist") @@ -121,7 +123,8 @@ func (skillMgr *skillManager) GetGroup(id string) (*models.SkillGroup, error) { } func (skillMgr *skillManager) UpdateGroup(id string, inputData *common.SkillGroupUpdateInput) (*models.SkillGroup, error) { - skillGroupObj := &models.SkillGroup{} + + skillGroupObj := models.NewSkillGroup() storage.DB.First(skillGroupObj, id) @@ -129,20 +132,43 @@ func (skillMgr *skillManager) UpdateGroup(id string, inputData *common.SkillGrou return nil, errors.New("item does not exist") } - storage.DB.Model(&skillGroupObj).Updates(models.Skill{Name: inputData.Name}) + skillMapping, err := skillMgr.getSkillList(inputData.Skills) + + if err != nil { + return nil, err + } + + storage.DB.Model(skillGroupObj).Updates(models.Skill{Name: inputData.Name}) + storage.DB.Model(skillGroupObj).Association("Skills").Replace(skillMapping) // TODO: handle errors return skillGroupObj, nil } +func (skillMgr *skillManager) getSkillList(inputList []int) ([]*models.Skill, error) { + + skills := []*models.Skill{} + + for _, id := range inputList { + skill := models.NewSkill() + storage.DB.First(skill, id) + if skill.ID == 0 { + return nil, fmt.Errorf("skill with id %v not exists", id) + } + skills = append(skills, skill) + } + + return skills, nil +} + func (skillMgr *skillManager) DeleteGroup(id string) error { - skillGroupObj := models.SkillGroup{} + skillGroupObj := models.NewSkillGroup() - storage.DB.First(&skillGroupObj, id) + storage.DB.First(skillGroupObj, id) if skillGroupObj.ID == 0 { return errors.New("item does not exist") } - storage.DB.Delete(&skillGroupObj) + storage.DB.Delete(skillGroupObj) // TODO: handle errors return nil } diff --git a/models/skill.go b/models/skill.go index 24c05a1..a0506b8 100644 --- a/models/skill.go +++ b/models/skill.go @@ -22,3 +22,11 @@ type SkillGroup struct { Name string `json:"name"` Skills []Skill `gorm:"many2many:skillgroup_skills;" json:"skills"` } + +func NewSkill() *Skill { + return &Skill{} +} + +func NewSkillGroup() *SkillGroup { + return &SkillGroup{} +}