Update object in memory with values from req.Body - go

I am creating a simple in-memory server before doing things with a database. I have this update method:
type Nearby struct {
ID int `json:"id,omitempty"`
Me int `json:"me,omitempty"`
You int `json:"you,omitempty"`
ContactTime int64 `json:"contactTime,omitempty"`
}
func (h NearbyHandler) updateById(v NearbyInjection) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
params := mux.Vars(r)
decoder := json.NewDecoder(r.Body)
var t Nearby
err := decoder.Decode(&t)
if err != nil {
panic(err)
}
mtx.Lock()
item, ok := v.Nearby[params["id"]]
mtx.Unlock()
if !ok {
panic(errors.New("No item to update"))
}
// !! Ok, how can I overwrite the properties from t onto item
if ok {
json.NewEncoder(w).Encode(item)
} else {
io.WriteString(w, "null")
}
}
}
I am looking to take the key/values from t, and write them onto the item object. t gets decoded into a struct value (I assume), and item is a struct value found in a map. Both item and t have the same type (Nearby)
In JavaScript, all I am looking to do would be:
Object.assign(item, t);
Just trying to accomplish something similar with Go.
With Golang, I can do this:
item.ContactTime = t.ContactTime
but I only want to overwrite item.ContactTime if t.ContactTime is defined.

Just overwrite the item in your map:
v.Nearby[params["id"]] = t

I'd suggest to use https://github.com/imdario/mergo function Merge. I don't think there is any reason to reinvent the wheel in this one and go's lack of generics does not help with such operations. example:
src := Foo{
A: "one",
B: 2,
}
dest := Foo{
A: "two",
}
mergo.Merge(&dest, src)
fmt.Println(dest)
// Will print
// {two 2}
I also think you should make all Nearby's properties pointers so that you can always compare them against nil to make sure they were not set.

Related

What is the right way to conditionally assign multiple properties to a struct

I'm working on a resolver function for a GraphQL query for a BE I'm writing in Go. In the resolver, I have user data that I want to update, using an input value containing several possible update properties.
In JavaScript, this can be done quickly through destructuring (pseudo):
const mergedObj = {...oldProps, ...newProps}
For now, my resolver function looks like this (using gqlgen for GraphQL Go resolvers):
func (r *mutationResolver) ModifyUser(ctx context.Context, input *model.ModifyUserInput) (*model.User, error) {
id := input.ID
us, ok := r.Resolver.UserStore[id]
if !ok {
return nil, fmt.Errorf("not found")
}
if input.FirstName != nil {
us.FirstName = *input.FirstName
}
if input.LastName != nil {
us.LastName = *input.LastName
}
if input.ProfileImage != nil {
us.ProfileImage = input.ProfileImage
}
if input.Password != nil {
us.Password = *input.Password
}
if input.Email != nil {
us.Email = *input.Email
}
if input.InTomorrow != nil {
us.InTomorrow = input.InTomorrow
}
if input.DefaultDaysIn != nil {
us.DefaultDaysIn = input.DefaultDaysIn
}
r.Resolver.UserStore[id] = us
return &us, nil
}
This feels quite boilerplatey. Would it make sense in this situation to iterate through struct keys? Or is there another pattern I'm missing?
Use a function to reduce the boilerplate:
func mergef[T any](a, b *T) {
if b != nil {
*a = *b
}
}
...
mergef(&us.FirstName, input.FirstName)
mergef(&us.LastName, input.LastName)
...
Use the reflect package to reduce more boilerplate:
// merge sets fields in struct pointed to by d to
// dereferenced fields in struct pointed to by s.
//
// Argument s must point to a struct with pointer type
// fields.
// Argument d must point to a struct with fields that
// correspond to the fields in s: there must be a field
// in d with the same name as a field in s; the type of
// the field in s must be a pointer to the type of the field
// in d.
func merge(d, s any) {
sv := reflect.ValueOf(s).Elem()
dv := reflect.ValueOf(d).Elem()
for i := 0; i < sv.NumField(); i++ {
sf := sv.Field(i)
if sf.IsNil() {
continue
}
df := dv.FieldByName(sv.Type().Field(i).Name)
df.Set(sf.Elem())
}
}
Employ the function like this:
merge(us, input)

Golang: Unmarshal function that can handle 2 different structs

looking for help.
Say I have the following structs..
type phoneStruct struct {
PhoneID string `json:"id"`
Carrier string `json:"carrier"`
}
type carStruct struct {
CarID string `json:"id"`
Model string `json:"model"`
}
Two very different structs.. but predictable.
Now in my code what I'm doing is doing a POST request and then parsing the body of what I get back so that I can do some test assertions on it. But for simplicity lets say I have this JSON
var jsonPhone = `{
"id": "123",
"carrier": "Rogers"
}`
var jsonCar = `{
"id": "foobar-fewf-434-fewf",
"model": "Civic"
}`
And I want to associate that json with my structs, so I have the following calls:
p, err := parsePhone(jsonPhone)
c, err := parseCar(jsonCar)
And the functions looks something like
func parsePhone(body []byte) (*phoneStruct, error) {
var p = new(phoneStruct)
err := json.Unmarshal(body, p)
return p, err
}
func parseCar(body []byte) (*carStruct, error) {
var c = new(carStruct)
err := json.Unmarshal(body, c)
return c, err
}
.. I am wondering if there is a way to just have one struct. I know there is if there is some commonality between two or more structs .. or how to parse unknown structs.. but haven't been able to find a resource on if I know the structures.. how can I have a function that looks something like..
func parseAnything(body []byte (<pointer to the struct>, error)
var c = new(someStruct)
err := json.Unmarshal(body, c)
return c, err
}
Note that I will always know what I am calling so I could do a conditional switch statement in my parse function that says 'if car.. if phone..' but I'm not sure how to define the return.
Thank you!
Write the function to take a pointer to the result:
func parseAnything(body []byte, c interface{}) error {
return json.Unmarshal(body, c)
}
Use it like this:
var p phoneStruct
if err := parseAnything(jsonPhone, &p); err != nil {
// handle error
}
// p has unmarshaled phone
var c carStruct
if err := parseAnything(jsonCar, &c); err != nil {
// handle error
}
// c has unmarshaled car
I don't know exactly what are you trying to build, but will try to give some insight based on what I understood.
If you are trying to use the same parser for both structs, they probably have something in common. Probably they are being used together for some use case of you application.
So, if they are being use together, they should implement some interface that represents the set of features that these structs have in common (with may or maybe not be related with just the parsing of data).
And, as you said, you know beforehand of what type of struct you need, so this should come easy with something like this:
//The interface that represents what the structs have in common.
//I named it "Parser" because it's the info I have. It probably should have another (better) name
type Parser interface {
Parse([]byte) (Parser, error)
}
type Phone struct {
PhoneID string `json:"id"`
Carrier string `json:"carrier"`
}
type Car struct {
CarID string `json:"id"`
Model string `json:"model"`
}
func (p *Phone) Parse (body []byte) (Parser, error) {
err := json.Unmarshal(body, p)
return p, err
}
func (c *Car) Parse (body []byte) (Parser, error) {
err := json.Unmarshal(body, c)
return c, err
}
func main() {
c := Car{}
var jsonCar = `{
"id": "123",
"model": "Civic"
}`
res, _ := c.Parse([]byte(jsonCar))
fmt.Println(res)
p := Phone{}
var jsonPhone = `{
"id": "123",
"carrier": "Rogers"
}`
res, _ = p.Parse([]byte(jsonPhone))
fmt.Println(res)
}

Create type inside if condition

Requirement:
type A struct {//some code}
type B struct {//some code}
func getData(db string) interface{} {
if db == "dbA" { // problem with this if condition
type C A
} else {
type C B
}
var byteData []byte
query := //cassandra query object
iter := query.Iter()
for iter.Scan(&byteData) {
myObject := new(C)
err := proto.Unmarshal(byteData, myObject)
objects = append(objects, myObject)
}
return objects
}
Basically, I don't want to write the if condition inside the loop. But myObject is declared outside the scope of C.
Alternate way:
I have tried having the type of myObject as proto.Message, but that gives me error "invalid memory address or nil pointer dereference"
myObject := new(proto.Message)
err := proto.Unmarshal(byteData, myObject)
objects = append(objects, myObject)
Another alternate:
I am also not sure if using the same variable will work (and hence i am trying to create a new variable each time inside the loop)
PS: This does not require much Cassandra knowledge
Help appreciated. Thanks !
Edit 1:
What I'm trying to accomplish is fetch a few rows from the database. But since I have multiple tables which contain very much similar data, I want to do it in one function in a very much optimised manner. And the values I want to fetch are stored in bytes, which I am using proto to convert into Golang objects.
Edit 2:
proto.Unmarshal needs the second argument to be of type proto.Message. Hence I cant use an empty interface. (Both types A and B implement proto.Message)
Edit 3:
You can find proto at "github.com/golang/protobuf". I use it to convert objects into bytes and also back into objects.!
In Go's type system, what you want to achieve can not be done directly.
However, with help of interface{} and first-class function value, we can get it done, and rather gracefully.
To do this, instead of declaring a new type C, we declare a new constructer :
var newC func() interface{}
if true {
newC = func() interface{} {
c := new(A)
return c
}
} else {
newC = func() interface{} {
c := new(B)
return c
}
}
And then, in the loop, change myObject := new(C) to myObject := newC(). It is done.
Example: https://play.golang.org/p/dBKBYrLqi_P
Edit:
As you need the param being a proto.Message, which I guess is an interface, you can cast it into proto.Message.
So basically you can re-write you code into:
type A struct {//some code}
type B struct {//some code}
func getData(db string) interface{} {
var newC func() interface{}
if true {
newC = func() proto.Message {
c := new(A)
return c
}
} else {
newC = func() proto.Message {
c := new(B)
return c
}
}
var byteData []byte
query := //cassandra query object
iter := query.Iter()
for iter.Scan(&byteData) {
myObject := newC()
err := proto.Unmarshal(byteData, myObject)
objects = append(objects, myObject)
}
return objects
}

Golang remap interface go-cache

I've got a struct like below:
type Page struct {
title string
url string
}
and a map of structs:
var mostViewed = make(map[int]Page)
With go-cache, I store the map with a TTL time.
c.Set("data", mostViewed, 60*time.Minute)
But, once I recover "data" key, how could I assing it back to a map?
a, _ := c.Get("data")
fmt.Printf("%+v\n", a)
out: map[17:{title:xxx, url:yyy}]
I tried something like:
z := map[int]Page{a}
Any clue? It's like "remapping" a mapped string.
You get an interface{} type back, but you know what it is, so you need to use a type assertion to cast it back to a map[int]Page. Here is a quick external resource. https://newfivefour.com/golang-interface-type-assertions-switch.html
Here is an example
https://play.golang.org/p/lGseg88K1m
type Page struct {
title string
url string
}
func main() {
m := make(map[int]Page)
m[1] = Page{"hi", "there"}
iface := makeIface(m)
// use type assertion to cast it back to a map
if mapAgain, ok := iface.(map[int]Page); ok {
// it really is a map[int]Page
fmt.Printf("%+v\n", mapAgain)
} else {
// its not actually a map[int]Page
fmt.Println("oops")
}
// alternatively use a type-switch if it could be multiple types
switch v := iface.(type) {
case map[int]Page:
//yay
fmt.Printf("%+v\n", v)
case string:
// its a string
fmt.Println(v)
default:
// something we didn't account for
}
}
func makeIface(m map[int]Page) interface{} {
return m
}
Edit: as a side note, you probably want to make your map type map[int]*Page because if you did something like this:
page := m[1]
page.url = "different"
fmt.Println(page) // prints url="different"
fmt.Println(m[1].url) // remains unchanged
Because page would be a copy of what is in the map, not the Page in the map itself.

in golang, general function to load http form data into a struct

In Go, http form data (e.g. from a POST or PUT request) can be accessed as a map of the form map[string][]string. I'm having a hard time converting this to structs in a generalizable way.
For example, I want to load a map like:
m := map[string][]string {
"Age": []string{"20"},
"Name": []string{"John Smith"},
}
Into a model like:
type Person struct {
Age int
Name string
}
So I'm trying to write a function with the signature LoadModel(obj interface{}, m map[string][]string) []error that will load the form data into an interface{} that I can type cast back to a Person. Using reflection so that I can use it on any struct type with any fields, not just a Person, and so that I can convert the string from the http data to an int, boolean, etc as necessary.
Using the answer to this question in golang, using reflect, how do you set the value of a struct field? I can set the value of a person using reflect, e.g.:
p := Person{25, "John"}
reflect.ValueOf(&p).Elem().Field(1).SetString("Dave")
But then I'd have to copy the load function for every type of struct I have. When I try it for an interface{} it doesn't work.
pi := (interface{})(p)
reflect.ValueOf(&pi).Elem().Field(1).SetString("Dave")
// panic: reflect: call of reflect.Value.Field on interface Value
How can I do this in the general case? Or even better, is there a more idiomatic Go way to accomplish what I'm trying to do?
You need to make switches for the general case, and load the different field types accordingly. This is basic part.
It gets harder when you have slices in the struct (then you have to load them up to the number of elements in the form field), or you have nested structs.
I have written a package that does this. Please see:
http://www.gorillatoolkit.org/pkg/schema
For fun, I tried it out. Note that I cheated a little bit (see comments), but you should get the picture. There is usually a cost to use reflection vs statically typed assignments (like nemo's answer), so be sure to weigh that in your decision (I haven't benchmarked it though).
Also, obvious disclaimer, I haven't tested all edge cases, etc, etc. Don't just copy paste this in production code :)
So here goes:
package main
import (
"fmt"
"reflect"
"strconv"
)
type Person struct {
Age int
Name string
Salary float64
}
// I cheated a little bit, made the map's value a string instead of a slice.
// Could've used just the index 0 instead, or fill an array of structs (obj).
// Either way, this shows the reflection steps.
//
// Note: no error returned from this example, I just log to stdout. Could definitely
// return an array of errors, and should catch a panic since this is possible
// with the reflect package.
func LoadModel(obj interface{}, m map[string]string) {
defer func() {
if e := recover(); e != nil {
fmt.Printf("Panic! %v\n", e)
}
}()
val := reflect.ValueOf(obj)
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
// Loop over map, try to match the key to a field
for k, v := range m {
if f := val.FieldByName(k); f.IsValid() {
// Is it assignable?
if f.CanSet() {
// Assign the map's value to this field, converting to the right data type.
switch f.Type().Kind() {
// Only a few kinds, just to show the basic idea...
case reflect.Int:
if i, e := strconv.ParseInt(v, 0, 0); e == nil {
f.SetInt(i)
} else {
fmt.Printf("Could not set int value of %s: %s\n", k, e)
}
case reflect.Float64:
if fl, e := strconv.ParseFloat(v, 0); e == nil {
f.SetFloat(fl)
} else {
fmt.Printf("Could not set float64 value of %s: %s\n", k, e)
}
case reflect.String:
f.SetString(v)
default:
fmt.Printf("Unsupported format %v for field %s\n", f.Type().Kind(), k)
}
} else {
fmt.Printf("Key '%s' cannot be set\n", k)
}
} else {
// Key does not map to a field in obj
fmt.Printf("Key '%s' does not have a corresponding field in obj %+v\n", k, obj)
}
}
}
func main() {
m := map[string]string{
"Age": "36",
"Name": "Johnny",
"Salary": "1400.33",
"Ignored": "True",
}
p := new(Person)
LoadModel(p, m)
fmt.Printf("After LoadModel: Person=%+v\n", p)
}
I'd propose to use a specific interface instead of interface{} in your LoadModel
which your type has to implement in order to be loaded.
For example:
type Loadable interface{
LoadValue(name string, value []string)
}
func LoadModel(loadable Loadable, data map[string][]string) {
for key, value := range data {
loadable.LoadValue(key, value)
}
}
And your Person implements Loadable by implementing LoadModel like this:
type Person struct {
Age int
Name string
}
func (p *Person) LoadValue(name string, value []string) {
switch name {
case "Age":
p.Age, err = strconv.Atoi(value[0])
// etc.
}
}
This is the way, the encoding/binary package or the encoding/json package work, for example.

Resources