From c0041e8fa42378a28a4728e9d0c6ef4dae1c0ae8 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Sat, 24 Feb 2024 11:18:42 +0100 Subject: [PATCH 1/4] fixed user competence --- common/user.go | 9 +++++++++ handlers/user.go | 30 ++++++++++++++++++++++++++++++ managers/skill.go | 2 +- managers/user.go | 47 ++++++++++++++++++++++++++++++++++++++++------- models/ranking.go | 16 ++++++++-------- models/user.go | 13 +++++++------ storage/db.go | 2 +- 7 files changed, 96 insertions(+), 23 deletions(-) diff --git a/common/user.go b/common/user.go index 4e73d47..5d69bb5 100644 --- a/common/user.go +++ b/common/user.go @@ -10,6 +10,11 @@ type UserUpdateInput struct { Email string `json:"email"` } +type CompetenceInput struct { + Skill int `json:"skill" binding:"required"` + Rank int `json:"rank" binding:"required"` +} + func NewUserCreationInput() *UserCreationInput { return &UserCreationInput{} } @@ -17,3 +22,7 @@ func NewUserCreationInput() *UserCreationInput { func NewUserUpdateInput() *UserUpdateInput { return &UserUpdateInput{} } + +func NewCompetenceInput() *CompetenceInput { + return &CompetenceInput{} +} diff --git a/handlers/user.go b/handlers/user.go index b0b9626..72b51bf 100644 --- a/handlers/user.go +++ b/handlers/user.go @@ -26,9 +26,11 @@ func (handler *UserHandler) RegisterEndpoints(r *gin.Engine) { userGroup.GET("", handler.ListUser) userGroup.POST("", handler.CreateUser) + userGroup.POST(":userid/skills", handler.AddSkill) userGroup.GET(":userid/", handler.UserDetail) userGroup.DELETE(":userid/", handler.DeleteUser) userGroup.PATCH(":userid/", handler.UpdateUser) + } func (handler *UserHandler) CreateUser(ctx *gin.Context) { @@ -127,3 +129,31 @@ func (handler *UserHandler) UpdateUser(ctx *gin.Context) { ctx.JSON(http.StatusOK, user) } + +func (handler *UserHandler) AddSkill(ctx *gin.Context) { + + userId, ok := ctx.Params.Get("userid") + + if !ok { + common.BadResponse(ctx, "failed to delete user") + return + } + + userData := common.NewCompetenceInput() + + err := ctx.BindJSON(&userData) + + if err != nil { + common.BadResponse(ctx, "failed to bind data") + return + } + + user, err := handler.userManager.AddNewSkill(userId, userData) + + if err != nil { + common.BadResponse(ctx, err.Error()) + return + } + + ctx.JSON(http.StatusOK, user) +} diff --git a/managers/skill.go b/managers/skill.go index 86bb131..d0ca0cc 100644 --- a/managers/skill.go +++ b/managers/skill.go @@ -104,7 +104,6 @@ func (skillMgr *skillManager) CreateGroup(inputData *common.SkillGroupCreationIn func (skillMgr *skillManager) ListGroup() ([]models.SkillGroup, error) { skillGroups := []models.SkillGroup{} - // storage.DB.Find(&skillGroupObj) storage.DB.Model(&skillGroups).Preload("Skills").Find(&skillGroups) // TODO: handle errors return skillGroups, nil @@ -113,6 +112,7 @@ func (skillMgr *skillManager) ListGroup() ([]models.SkillGroup, error) { func (skillMgr *skillManager) GetGroup(id string) (*models.SkillGroup, error) { skillGroupObj := models.NewSkillGroup() + storage.DB.First(skillGroupObj, id) storage.DB.Model(skillGroupObj).Preload("Skills").Find(skillGroupObj) if skillGroupObj.ID == 0 { diff --git a/managers/user.go b/managers/user.go index d9821eb..43046f7 100644 --- a/managers/user.go +++ b/managers/user.go @@ -9,11 +9,12 @@ import ( ) type UserManager interface { - Create(userData *common.UserCreationInput) (*models.User, error) + Create(inputData *common.UserCreationInput) (*models.User, error) List() ([]models.User, error) Get(id string) (*models.User, error) - Update(userId string, userData *common.UserUpdateInput) (*models.User, error) + Update(userId string, inputData *common.UserUpdateInput) (*models.User, error) Delete(id string) error + AddNewSkill(userId string, inputData *common.CompetenceInput) (*models.User, error) } type userManager struct { @@ -25,20 +26,21 @@ func NewUserManager() UserManager { return &userManager{} } -func (userMgr *userManager) Create(userData *common.UserCreationInput) (*models.User, error) { - newUser := &models.User{FullName: userData.FullName, Email: userData.Email} +func (userMgr *userManager) Create(inputData *common.UserCreationInput) (*models.User, error) { + newUser := &models.User{FullName: inputData.FullName, Email: inputData.Email} storage.DB.Create(newUser) if newUser.ID == 0 { return nil, errors.New("user creation failed") } + userMgr.prefetchUser(newUser) return newUser, nil } func (userMgr *userManager) List() ([]models.User, error) { users := []models.User{} - storage.DB.Find(&users) + storage.DB.Preload("Competence").Preload("Competence.Skill").Find(&users) return users, nil } @@ -50,10 +52,12 @@ func (userMgr *userManager) Get(id string) (*models.User, error) { return nil, errors.New("no user found") } + userMgr.prefetchUser(user) + return user, nil } -func (userMgr *userManager) Update(userId string, userData *common.UserUpdateInput) (*models.User, error) { +func (userMgr *userManager) Update(userId string, inputData *common.UserUpdateInput) (*models.User, error) { user := models.NewUser() @@ -63,7 +67,9 @@ func (userMgr *userManager) Update(userId string, userData *common.UserUpdateInp return nil, errors.New("no user found") } - storage.DB.Model(&user).Updates(models.User{FullName: userData.FullName, Email: userData.Email}) + storage.DB.Model(&user).Updates(models.User{FullName: inputData.FullName, Email: inputData.Email}) + + userMgr.prefetchUser(user) return user, nil } @@ -80,3 +86,30 @@ func (userMgr *userManager) Delete(id string) error { storage.DB.Delete(user) return nil } + +func (userMgr *userManager) AddNewSkill(userId string, inputData *common.CompetenceInput) (*models.User, error) { + user := models.NewUser() + + storage.DB.First(user, userId) + + if user.ID == 0 { + return nil, errors.New("no user found") + } + + skill := models.NewSkill() + + storage.DB.First(skill, inputData.Skill) + + competenceObj := models.NewCompetence() + competenceObj.User = *user + competenceObj.Skill = *skill + competenceObj.Rank = inputData.Rank + storage.DB.Create(competenceObj) + userMgr.prefetchUser(user) + + return user, nil +} + +func (userMgr *userManager) prefetchUser(user *models.User) { + storage.DB.Model(user).Preload("Competence").Preload("Competence.Skill").Find(user) +} diff --git a/models/ranking.go b/models/ranking.go index 1bd339b..6bf3dfe 100644 --- a/models/ranking.go +++ b/models/ranking.go @@ -6,18 +6,18 @@ import ( "gorm.io/gorm" ) -type UserSkillRank struct { +type Competence 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"` + UserID int `json:"-"` + User User `json:"-"` + SkillID int `json:"-"` + Skill Skill `json:"skill"` + Rank int `json:"rank"` } -func NewUserSkillRank() *UserSkillRank { - return &UserSkillRank{} +func NewCompetence() *Competence { + return &Competence{} } diff --git a/models/user.go b/models/user.go index aa56f4f..d8b0b8f 100644 --- a/models/user.go +++ b/models/user.go @@ -8,12 +8,13 @@ import ( type User struct { // gorm.Model - ID uint `gorm:"primarykey" json:"id"` - CreatedAt time.Time `json:"createdAt"` - UpdatedAt time.Time `json:"updatedAt"` - DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` - FullName string `json:"fullName"` - Email string `json:"email"` + ID uint `gorm:"primarykey" json:"id"` + CreatedAt time.Time `json:"createdAt"` + UpdatedAt time.Time `json:"updatedAt"` + DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` + FullName string `json:"fullName"` + Email string `json:"email"` + Competence []Competence `gorm:"foreignKey:UserID" json:"competence"` } func NewUser() *User { diff --git a/storage/db.go b/storage/db.go index 995238f..e666af9 100644 --- a/storage/db.go +++ b/storage/db.go @@ -19,6 +19,6 @@ func InitializeDatabase() { &models.User{}, &models.Skill{}, &models.SkillGroup{}, - &models.UserSkillRank{}, + &models.Competence{}, ) } From 1de15fcf70a3a6b4fd20a11e356251e1a97a7565 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Sun, 25 Feb 2024 16:46:44 +0100 Subject: [PATCH 2/4] fk update --- common/skill.go | 3 ++- managers/skill.go | 43 ++++++++++++++++++++++++------------------- models/skill.go | 28 +++++++++++++++------------- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/common/skill.go b/common/skill.go index 65d67b7..3b0bafe 100644 --- a/common/skill.go +++ b/common/skill.go @@ -1,7 +1,8 @@ package common type SkillCreationInput struct { - Name string `json:"name"` + Name string `json:"name"` + Group string `json:"group"` } type SkillUpdateInput struct { diff --git a/managers/skill.go b/managers/skill.go index d0ca0cc..5df296a 100644 --- a/managers/skill.go +++ b/managers/skill.go @@ -2,7 +2,6 @@ package managers import ( "errors" - "fmt" "github.com/buildfromzero/skill-map/common" "github.com/buildfromzero/skill-map/models" @@ -35,6 +34,12 @@ func NewSkillManager() SkillManager { func (skillMgr *skillManager) Create(inputData *common.SkillCreationInput) (*models.Skill, error) { newSkillObj := &models.Skill{Name: inputData.Name} + + skillGroup := models.NewSkillGroup() + + storage.DB.First(skillGroup, inputData.Group) + newSkillObj.SkillGroup = *skillGroup + storage.DB.Create(newSkillObj) if newSkillObj.ID == 0 { @@ -113,7 +118,7 @@ func (skillMgr *skillManager) GetGroup(id string) (*models.SkillGroup, error) { skillGroupObj := models.NewSkillGroup() storage.DB.First(skillGroupObj, id) - storage.DB.Model(skillGroupObj).Preload("Skills").Find(skillGroupObj) + // storage.DB.Model(skillGroupObj).Preload("Skills").Find(skillGroupObj) if skillGroupObj.ID == 0 { return nil, errors.New("item does not exist") @@ -132,33 +137,33 @@ func (skillMgr *skillManager) UpdateGroup(id string, inputData *common.SkillGrou return nil, errors.New("item does not exist") } - skillMapping, err := skillMgr.getSkillList(inputData.Skills) + // skillMapping, err := skillMgr.getSkillList(inputData.Skills) - if err != nil { - return nil, err - } + // if err != nil { + // return nil, err + // } storage.DB.Model(skillGroupObj).Updates(models.Skill{Name: inputData.Name}) - storage.DB.Model(skillGroupObj).Association("Skills").Replace(skillMapping) + // storage.DB.Model(skillGroupObj).Association("Skills").Replace(skillMapping) // TODO: handle errors return skillGroupObj, nil } -func (skillMgr *skillManager) getSkillList(inputList []int) ([]*models.Skill, error) { +// func (skillMgr *skillManager) getSkillList(inputList []int) ([]*models.Skill, error) { - skills := []*models.Skill{} +// 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) - } +// 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 -} +// return skills, nil +// } func (skillMgr *skillManager) DeleteGroup(id string) error { skillGroupObj := models.NewSkillGroup() diff --git a/models/skill.go b/models/skill.go index a0506b8..f80f365 100644 --- a/models/skill.go +++ b/models/skill.go @@ -6,12 +6,12 @@ import ( "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"` +func NewSkill() *Skill { + return &Skill{} +} + +func NewSkillGroup() *SkillGroup { + return &SkillGroup{} } type SkillGroup struct { @@ -20,13 +20,15 @@ type SkillGroup struct { UpdatedAt time.Time `json:"updatedAt"` DeletedAt gorm.DeletedAt `gorm:"index" json:"-"` Name string `json:"name"` - Skills []Skill `gorm:"many2many:skillgroup_skills;" json:"skills"` + // Skills []Skill `gorm:"many2many:skillgroup_skills;" json:"skills"` } -func NewSkill() *Skill { - return &Skill{} -} - -func NewSkillGroup() *SkillGroup { - return &SkillGroup{} +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"` + SkillGroupID uint `json:"-"` + SkillGroup SkillGroup `json:"skillGroup"` } From d16978ae4663f18b83c104fbd204145e79ff2e11 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Sun, 25 Feb 2024 22:03:00 +0100 Subject: [PATCH 3/4] fix skill-group updates --- common/skill.go | 3 ++- managers/skill.go | 46 +++++++++++++++++----------------------------- 2 files changed, 19 insertions(+), 30 deletions(-) diff --git a/common/skill.go b/common/skill.go index 3b0bafe..c4da193 100644 --- a/common/skill.go +++ b/common/skill.go @@ -6,7 +6,8 @@ type SkillCreationInput struct { } type SkillUpdateInput struct { - Name string `json:"name"` + Name string `json:"name"` + Group string `json:"group"` } type SkillGroupCreationInput struct { diff --git a/managers/skill.go b/managers/skill.go index 5df296a..8f27807 100644 --- a/managers/skill.go +++ b/managers/skill.go @@ -50,11 +50,11 @@ func (skillMgr *skillManager) Create(inputData *common.SkillCreationInput) (*mod } func (skillMgr *skillManager) List() ([]models.Skill, error) { - skillObj := []models.Skill{} + skillList := []models.Skill{} - storage.DB.Find(&skillObj) + storage.DB.Model(&skillList).Preload("SkillGroup").Find(&skillList) // TODO: handle errors - return skillObj, nil + return skillList, nil } func (skillMgr *skillManager) Get(id string) (*models.Skill, error) { @@ -66,6 +66,8 @@ func (skillMgr *skillManager) Get(id string) (*models.Skill, error) { return nil, errors.New("no skill found") } + storage.DB.Model(skillObj).Preload("SkillGroup").Find(skillObj) + return skillObj, nil } @@ -78,7 +80,17 @@ func (skillMgr *skillManager) Update(id string, inputData *common.SkillUpdateInp return nil, errors.New("item does not exist") } - storage.DB.Model(skillObj).Updates(models.Skill{Name: inputData.Name}) + skillGroup := models.NewSkillGroup() + storage.DB.First(skillGroup, inputData.Group) + + skillObj.SkillGroup = *skillGroup + skillObj.Name = inputData.Name + + storage.DB.Save(skillObj) + + // storage.DB.Model(skillObj).Updates(models.Skill{Name: inputData.Name}) + + storage.DB.Model(skillObj).Preload("SkillGroup").Find(skillObj) // TODO: handle errors return skillObj, nil } @@ -109,7 +121,7 @@ func (skillMgr *skillManager) CreateGroup(inputData *common.SkillGroupCreationIn func (skillMgr *skillManager) ListGroup() ([]models.SkillGroup, error) { skillGroups := []models.SkillGroup{} - storage.DB.Model(&skillGroups).Preload("Skills").Find(&skillGroups) + storage.DB.Model(&skillGroups).Find(&skillGroups) // TODO: handle errors return skillGroups, nil } @@ -118,7 +130,6 @@ func (skillMgr *skillManager) GetGroup(id string) (*models.SkillGroup, error) { 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") @@ -137,34 +148,11 @@ func (skillMgr *skillManager) UpdateGroup(id string, inputData *common.SkillGrou return nil, errors.New("item does not exist") } - // 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.NewSkillGroup() From 4080d4d2d7a78b016d5e773499b41c969b2f6b64 Mon Sep 17 00:00:00 2001 From: Tom Victor Date: Mon, 26 Feb 2024 15:57:21 +0100 Subject: [PATCH 4/4] minor prefetch --- managers/skill.go | 3 --- managers/user.go | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/managers/skill.go b/managers/skill.go index 8f27807..fd128c4 100644 --- a/managers/skill.go +++ b/managers/skill.go @@ -87,9 +87,6 @@ func (skillMgr *skillManager) Update(id string, inputData *common.SkillUpdateInp skillObj.Name = inputData.Name storage.DB.Save(skillObj) - - // storage.DB.Model(skillObj).Updates(models.Skill{Name: inputData.Name}) - storage.DB.Model(skillObj).Preload("SkillGroup").Find(skillObj) // TODO: handle errors return skillObj, nil diff --git a/managers/user.go b/managers/user.go index 43046f7..a1e9d39 100644 --- a/managers/user.go +++ b/managers/user.go @@ -111,5 +111,5 @@ func (userMgr *userManager) AddNewSkill(userId string, inputData *common.Compete } func (userMgr *userManager) prefetchUser(user *models.User) { - storage.DB.Model(user).Preload("Competence").Preload("Competence.Skill").Find(user) + storage.DB.Model(user).Preload("Competence").Preload("Competence.Skill").Preload("Competence.Skill.SkillGroup").Find(user) }