Sobes.tech
Junior — Senior
79

Ревью и оптимизация реализации пользовательского API

任务条件

Необходимо проанализировать представленный фрагмент кода API, отвечающего за работу с пользователями, выявить потенциальные проблемы и предложить улучшения в реализации.

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()
}