gogo-gormgo-echo

Partial update a structure with echo and gorm


I got a user interface, user model and user id structures

type UserId struct {
    Id int64 `param:"id" json:"id"`
}

type User struct {
    Email     string    `json:"email"`
    Password  string    `json:"password"`
    Username  string    `json:"username"`
    CreatedAt time.Time `json:"created_at"`
    UpdatedAt time.Time `json:"updated_at"`
}

type User struct {
    gorm.Model
    ID       int64  `gorm:"primary_key"`
    Email    string `gorm:"type:varchar(128);unique;not null"`
    Username string `gorm:"type:varchar(64);unique;not null"`
    Password string `gorm:"type:varchar(64);not null"`
}

I'v got a function to update a user

func updateUser(c echo.Context) error {
    cc := c.(*myproject.ConfigContext)

    userId := new(interfaces.UserId)
    err := cc.Bind(userId)
    if err != nil {
        return err
    }

    newInfoUser := new(interfaces.User)
    err = cc.Bind(newInfoUser)
    if err != nil {
        return err
    }

    db, err := cc.ConnectDB()
    if err != nil {
        return err
    }
    err = db.AutoMigrate(&models.User{})
    if err != nil {
        return err
    }

    dbUser := new(models.User)
    r := db.First(dbUser, userId.Id)
    if r.Error != nil {
        return cc.NoContent(http.StatusNotFound)
    }

    // the partial update

    return cc.JSON(200, "")
}

I could test if newInfoUser is empty for each fields and update if it not but that will be replicate code and I would like to do it in a general way.

behavior wanted:

got a user

{
    "username": "test", 
    "email": "test@gmail.com",
    "password": "password"
}

and call update with a body

{
    "username": "test2"
}

bind it to user structure will create

{
    "username": "test2",
    "email": "",
    "password": ""
}

and I would like user to be updated in

{
    "username": "test2",
    "email": "test@gmail.com",
    "password": "password"
}

Solution

  • I find https://willnorris.com/2014/05/go-rest-apis-and-pointers/ which show the omitempty tag

    (I realized I did a mistake, can't bind 2 times the same context)

    type UserId struct {
        Id int64 `param:"id" json:"id"`
    }
    
    type User struct {
        Email     string    `json:"email,omitempty"`
        Password  string    `json:"password,omitempty"`
        Username  string    `json:"username,omitempty"`
        // CreatedAt time.Time `json:"created_at,omitempty"`
        // UpdatedAt time.Time `json:"updated_at,omitempty"`
    }
    
    type User struct {
        gorm.Model
        ID       int64  `gorm:"primary_key"`
        Email    string `gorm:"type:varchar(128);unique;not null"`
        Username string `gorm:"type:varchar(64);unique;not null"`
        Password string `gorm:"type:varchar(64);not null"`
    }
    
    func updateUser(c echo.Context) error {
        cc := c.(*myproject.ConfigContext)
        var err error
    
        userId := new(interfaces.UserId)
        userId.Id, err = strconv.ParseInt(cc.Param("id"), 10, 64)
        if err != nil {
            return err
        }
    
        newInfoUser := new(interfaces.User)
        err = cc.Bind(newInfoUser)
        if err != nil {
            return err
        }
    
        db, err := cc.ConnectDB()
        if err != nil {
            return err
        }
        err = db.AutoMigrate(&models.User{})
        if err != nil {
            return err
        }
    
        r := db.Model(&models.User{}).Where("id = ?", userId.Id).Updates(newInfoUser)
        if r.Error != nil {
            return cc.NoContent(http.StatusNotFound)
        }
    
        return cc.JSON(200, "")
    }
    

    but I wonder why it is not working with createAt and updateAt fields