Junior — Senior
Review and optimization of user API implementation
livecode
Task condition
It is necessary to analyze the provided API code fragment responsible for user management, identify potential issues, and suggest improvements in the implementation.
func isValidName(name string) error {
if len(name) > 50 {
return errors.New("invalid name length")
}
return nil
}
func isValidPhone(phone string) error {
if len(phone) != 11 {
return errors.New("invalid phone format")
}
return nil
}
type User struct {
Id int `json:"id"`
Name string `json:"name"`
Phone string `json:"phone"`
IsAdmin bool `json:"is_admin"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
type UserRepository interface {
Find(sql string) *User
Save(user *User) *User
}
type UserApi struct {
userRepo UserRepository
logger *zap.Logger
}
func NewUserApi(userRepo UserRepository) *UserApi {
api := &UserApi{
userRepo: userRepo,
}
if os.Getenv("ENV") == "prod" {
api.logger, _ = zap.NewProduction()
} else {
api.logger, _ = zap.NewDevelopment()
}
return api
}
func (api *UserApi) ProcessRequest(c *gin.Context) {
authUserId := c.GetInt("auth_user_id")
authUser := api.userRepo.Find(fmt.Sprintf("SELECT * FROM user WHERE id = %d", authUserId))
id := c.Query("id")
user := api.userRepo.Find(fmt.Sprintf("SELECT * FROM user WHERE id = %s", id))
if authUser.IsAdmin || authUser.Id == user.Id {
if c.Request.Method == "POST" {
bytes, _ := io.ReadAll(c.Request.Body)
var body map[string]string
json.Unmarshal(bytes, &body)
var errs map[string]string
if err := isValidName(body["name"]); err != nil {
errs["name"] = err.Error()
} else {
user.Name = body["name"]
}
if err := isValidPhone(body["phone"]); err != nil {
errs["phone"] = err.Error()
} else {
user.Phone = body["phone"]
}
if len(errs) > 0 {
c.JSON(http.StatusUnprocessableEntity, errs)
return
}
user.UpdatedAt = time.Now()
api.userRepo.Save(user)
api.logger.Debug("user saved", zap.Int("user_id", user.Id))
c.JSON(http.StatusOK, user)
return
}
}
c.Status(http.StatusForbidden)
}
func main() {
// It's implemented somehow
api := NewUserApi(...)
r := gin.Default()
r.POST("/users", api.ProcessRequest)
r.Run()
}