Copy non empty struct values in Golang for updating Datastore - go

I'm writing a PATCH API and it is not necessary to send all the fields in request. I'd like to update only the new fields.
I'm using Datastore, and I think, I need to send entire struct that contains the final value I intend to save, as it replaces all the values.
I retrieve existing values from DB in one struct and I have the request in another struct.
type product struct {
TITLE string `json:"title"`
SUMMARY string `json:"summary"`
IMAGEURL string `json:"imageUrl"`
CATEGORY string `json:"category"`
TYPE int `json:"type"`
}
var dbVal product // Holds the existing DB values
var formVal product // Holds the values received from web form (api request)
formVal need not have all the values. It is empty if the field is not sent in the request.
I want the dbVal struct to be updated with the values in formVal ONLY if formVal field is not empty (!= "" for string or != 0 for int).
PS: I searched for more than a day and tried different examples and tried to use reflect from some of the other answers, but not able to find out how to assign values to another struct. If this is already answered, I'm sorry for reposting this - please share the link.

I would propose to slightly change the struct to use pointers:
type product struct {
TITLE *string `json:"title"`
SUMMARY *string `json:"summary"`
IMAGEURL *string `json:"imageUrl"`
CATEGORY *string `json:"category"`
TYPE *int `json:"type"`
}
And then you can iterate through the patch entity using reflect and replace non-nil values. Check example in Go Playground.

Consider writing a utility method on the product type that checks if it is empty and use that to determine whether to update the database values, e.g.:
func (p product) isEmpty() bool {
return (p.TITLE == "") && (p.SUMMARY == "") && (p.IMAGEURL == "") && (p.CATEGORY == "") && (p.TYPE == 0)
}
// ...
if !formVal.isEmpty() {
// Update database values...
}

Related

How to access the fields of this struct in Golang

I'm new to Golang and I need to know how to access the value from a struct of the format:
type CurrentSkuList struct {
SubscriptionNumber string `json:"subscriptionNumber`
Quantity int `json:"quantity"`
SubscriptionProducts []struct {
ID int `json:"id"`
ActiveStartDate int `json:"activeStartDate"`
ActiveEndDate int `json:"activeEndDate"`
Status string `json:"status"`
Sku string `json:"sku"`
ChildIDs []int `json:"childrenIds"`
} `json:"subscriptionProducts"`
}
For example, if I have a variable currentSkus of type CurrentSkuList and I need to access only Sku and Status values, is there a way to do something like:
currentSkus.SubscriptionProducts.Sku?
EDIT! When I try to access currentSkus.Quantity I get a compiler error undefined (type []util.CurrentSkuList has no field or method Quantity).
Yeah, there is a way. You can access by the . syntax you proposed. In this case, CurrentSkuList is returning an slice of SubscriptionProduct, you know that because of the [] struct part. Then you would have to access to the data this way:
currentSkus.SubscriptionProducts[index].Sku

How can I choose which struct member to update at runtime?

Say I have a struct in Go that looks like this:
LastUpdate struct {
Name string `yaml:"name"`
Address string `yaml:"address"`
Phone string `yaml:"phone"`
}
Now say I want to create a function that accepts the name of the field (eg. "Phone") and then updates that field to a value, like today's date.
How can I build the function in a way that it will accept the name of the field and update that field in the struct?
I know that I could do an IF clause for each scenario (if field == "Phone") {var.LastUpdate.Phone = time.Now().Date()}, but I'd like to build this function so that I don't have to go add an IF clause every time I add a new member to this struct in the future. Thoughts?
Use the reflect package to set a field by name.
// setFieldByName sets field with given name to specified value.
// The structPtr argument must be a pointer to a struct. The
// value argument must be assignable to the field.
func setFieldByName(structPtr interface{}, name string, value interface{}) error {
v := reflect.ValueOf(structPtr)
v = v.Elem() // deference pointer
v = v.FieldByName(name) // get field with specified name
if !v.IsValid() {
return errors.New("field not found")
}
v.Set(reflect.ValueOf(value))
return nil
}
Use it like this:
var lu LastUpdate
setFieldByName(&lu, "Name", "Russ Cox")
Run it on the Playground

Go struct separation best practices

I am trying to figure out a decent approach toward dealing with multiple uses for a struct. Let me explain the scenario.
I have a struct that represents the Model in gorm. In the current implementation, I have validation bound to this struct so when a request hits the endpoint I would validate against the model's struct. This works fine for most cases. But then there are some instances where I want to have more control over the request and the response.
This is possible by introducing a few additional internal structs that will parse the request and response. And I can decouple the validation from the model into the request specific struct. I am trying to figure out what the best practice is around these patterns. Pretty sure a lot of peeps would have faced a similar situation.
// Transaction holds the transaction details.
type Transaction struct {
Program Program
ProgramID uuid.UUID
Type string
Value float64
Reference string
}
// TransactionRequest for the endpoint.
type TransactionRequest struct {
ProgramKey string `json:"program_key" validator:"required"`
Type string `json:"type" validator:"required,oneof=credit debit"`
Value float64 `json:"value" validator:"required,numeric"`
Reference string `json:"reference" validator:"required"`
}
Update:
I managed to find a balance by introducing additional tags for update requests, I wrote about how I achieved it here
I faced similar problem and for solving that, defined a method for validating and didn't use tags. I had to, because I follow DDD and we should validate in service layer not API layer.
here is a sample of my apporach:
type Station struct {
types.GormCol
CompanyID types.RowID `gorm:"not null;unique" json:"company_id,omitempty"`
CompanyName string `gorm:"not null;unique" json:"company_name,omitempty"`
NodeCode uint64 `json:"node_code,omitempty"`
NodeName string `json:"node_name,omitempty"`
Key string `gorm:"type:text" json:"key,omitempty"`
MachineID string `json:"machine_id,omitempty"`
Detail string `json:"detail,omitempty"`
Error error `sql:"-" json:"user_error,omitempty"`
Extra map[string]interface{} `sql:"-" json:"extra_station,omitempty"`
}
// Validate check the type of
func (p *Station) Validate(act action.Action) error {
fieldError := core.NewFieldError(term.Error_in_companys_form)
switch act {
case action.Save:
if p.CompanyName == "" {
fieldError.Add(term.V_is_required, "Company Name", "company_name")
}
if p.CompanyID == 0 {
fieldError.Add(term.V_is_required, "Company ID", "company_id")
}
}
if fieldError.HasError() {
return fieldError
}
return nil
}
file's address: https://github.com/syronz/sigma-mono/blob/master/model/station.model.go

Better way of decoding json values

Assume a JSON object with the general format
"accounts": [
{
"id": "<ACCOUNT>",
"tags": []
}
]
}
I can create a struct with corresponding json tags to decode it like so
type AccountProperties struct {
ID AccountID `json:"id"`
MT4AccountID int `json:"mt4AccountID,omitempty"`
Tags []string `json:"tags"`
}
type Accounts struct {
Accounts []AccountProperties `json:"accounts"`
}
But the last struct with just one element seems incorrect to me. Is there a way I could simply say type Accounts []AccountProperties `json:"accounts"` instead of creating an entire new struct just to decode this object?
You need somewhere to store the json string accounts. Using a:
var m map[string][]AccountProperties
suffices, though of course you then need to know to use the string literal accounts to access the (single) map entry thus created:
type AccountProperties struct {
ID string `json:"id"`
MT4AccountID int `json:"mt4AccountID,omitempty"`
Tags []string `json:"tags"`
}
func main() {
var m map[string][]AccountProperties
err := json.Unmarshal([]byte(data), &m)
fmt.Println(err, m["accounts"])
}
See complete Go Playground example (I had to change the type of ID to string and fix the missing { in the json).
As Dave C points out in comments, this is no shorter than just using an anonymous struct type:
var a struct{ Accounts []AccountProperties }
in terms of the Unmarshall call (and when done this way it's more convenient to use). Should you want to use an anonymous struct like this in a json.Marshall call, you'll need to tag its single element to get a lowercase encoding: without a tag it will be called "Accounts" rather than "accounts".
(I don't claim the map method to be better, just an alternative.)

How can I add a new boolean property to a Golang struct and set the default value to true?

I have a user struct that corresponds to an entity. How can I add a new property active and set the default value to true?
Can I also set the value of that property to true for all existing entities by some easy method?
type User struct {
Id int64 `json:"id"`
Name string `json:"name"`
}
Bonus questions: I don't quite understand the syntax in the struct. What do the three columns represent? What do the JSON strings have ``around them?
//You can't change declared type.
type User struct {
Id int64 `json:"id"`
Name string `json:"name"`
}
//Instead you construct a new one embedding existent
type ActiveUser struct {
User
Active bool
}
//you instantiate type literally
user := User{1, "John"}
//and you can provide constructor for your type
func MakeUserActive(u User) ActiveUser {
auser := ActiveUser{u, true}
return auser
}
activeuser := MakeUserActive(user)
You can see it works https://play.golang.org/p/UU7RAn5RVK
You have to set the default value as true at the moment when you are passing the struct type to a variable, but this means you need to extend that struct with a new Active field.
type User struct {
Id int64 `json:"id"`
Name string `json:"name"`
Active bool
}
user := User{1, "John", true}
json:"id" means that you are mapping the json decoded object field to the field id in your struct type. Practically you are deserialize the json string into object fields which later you can map to their specific field inside the struct.

Resources