Golang Pointer Understanding - go

I am learning go and i need to understand something. I am getting few errors. I have created a Product struct and attached a func with it. I also got a product lists as a slice. Actually I am following one example. I was just trying add different endpoints to it.
I have added question in comment in code. Please explain. I need to return the json single object as a response to user. Please guide me.
package data
type Product struct {
ID int `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Price float32 `json:"price"`
SKU string `json:"sku"`
CreatedOn string `json:"-"`
UpdatedOn string `json:"-"`
DeletedOn string `json:"-"`
}
type Products []*Product
func (p *Products) ToJSON(w io.Writer) error {
e := json.NewEncoder(w)
return e.Encode(p)
}
func (p *Product) FromJSON(r io.Reader) error {
d := json.NewDecoder(r)
return d.Decode(p)
}
var ErrProductNotFound = fmt.Errorf("Product not found")
func GetProduct(id int) (*Product, error) { // this is returning *Product & err. When I use this in GetProduct in handler func it is giving error
for _, p := range productList {
if p.ID == id {
fmt.Println(p)
return p, nil
}
}
return nil, ErrProductNotFound
}
var productList = []*Product{ **// Why in example the teacher doing it like this.** []*Product{&Product{}, &Product{}} **what it the reason? Please explain.
&Product{ // this gives warning : redundant type from array, slice, or map composite literal. need to understand why**
ID: 1,
Name: "Latte",
Description: "chai",
Price: 2.45,
SKU: "abc123",
CreatedOn: time.Now().UTC().String(),
UpdatedOn: time.Now().UTC().String(),
},
&Product{
ID: 2,
Name: "Tea",
Description: "chai",
Price: 1.45,
SKU: "abc1234",
CreatedOn: time.Now().UTC().String(),
UpdatedOn: time.Now().UTC().String(),
},
}
package handlers
func (p *Product) GetProduct(rw http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
id, _ := strconv.Atoi(vars["id"])
p, errr := data.GetProduct(id) **// cannot use data.GetProduct(id) (value of type *data.Product) as *Product value in assignment**
errr = p.ToJSON(rw) // **p.ToJSON undefined (type *Product has no field or method ToJSON)**
if errr != nil {
http.Error(rw, "could not locate the product", http.StatusBadGateway)
}
}

cannot use data.GetProduct(id) (value of type *data.Product) as *Product value in assignment
p.ToJSON undefined (type *Product has no field or method ToJSON)
The problem here is that inside the GetProduct handler the variable p already has a type (*handlers.Product) that is different from the one returned by the data.GetProduct function (*data.Product). So what you can do is to use a different name for the variable that will store the result of data.GetProduct.
Why in example the teacher doing it like this. []*Product{&Product{}, &Product{}} what it the reason? Please explain.
In general because that's one of the available methods for how to initialize a slice of structs, as per the language spec. Why the teacher used this method specifically? Unless the teacher confided the reason to someone, then no one would know, I certainly don't.
this gives warning : redundant type from array, slice, or map composite literal. need to understand why
Because it's true, it is redundant, as per the language spec, in a composite literal expression you can elide the types of the elements and keys.
For example a non-redundant version of the following composite literal:
[]*Product{&Product{}, &Product{}}
would look like this:
[]*Product{{}, {}}
and the result of these two expressions would be the same.

Related

Using validator to check if value is boolean

I'm new to Go, so this might be very easy, but I can't find it. I have an entity Page with these two properties:
type Page struct {
Title string `form:"title" binding:"required"`
Active bool
}
Now, if you don't send a title we get a (not very pretty, but acceptable*):
Key: 'Page.Title' Error:Field validation for 'Title' failed on the 'required' tag.
Now, if I send this to the endpoint:
{
"title": "I'm a valid title",
"active": "I'm not a boolean at all!"
}
We get this:
json: cannot unmarshal string into Go struct field Page.Active of type bool
Which, IMO, is giving way too much info. What is the standard practice in Go to validate user input?
I was first making a page-validor.go with some checks, then I found this, but I'm not sure what is good practice in Go.
How do I validate this properly? Should I find check what is provided and then try to move it into the struct and validate the actual contents?
I am using GinGonic
* I've found a way to unwrap the errors and make it nicer
Write custom JSON Unmarshaller method for the type Page and inside UnmarshalJSON(bytes []byte) method, you can unmarshal the JSON bytes to map[string]interface{} and then validate the types you need with the JSON field keys.
An example of the JSON Unmarshaller looks like below.
type Page struct {
Title string `form:"title" binding:"required"`
Active bool
}
func (p *Page) UnmarshalJSON(bytes []byte) error {
var data map[string]interface{}
err := json.Unmarshal(bytes, &data)
if err != nil {
return err
}
actv, _ := data["active"]
if reflect.TypeOf(actv).Kind() != reflect.Bool {
return errors.New("active field should be a boolean")
}
p.Active = actv.(bool)
return nil
}
See the full example here in Playground.
After some more research, I've implemented Go-map-schema.
var page Page
src := make(map[string]interface{})
json.Unmarshal(jsonData, &src)
results, _ := schema.CompareMapToStruct(page, src, nil)
fmt.Println(results.MissingFields)
fmt.Println(results.MismatchedFields)
This works simple with the standard notations for an struct:
type Page struct {
Title string `json:"title" validator:"required"`
Active bool `json:"metaRobotsFollow" validate:"required,bool"`
}
You should use validator v10 available with Go
validator documentation go
For your use case you can use boolean validator
https://pkg.go.dev/github.com/go-playground/validator/v10#hdr-Boolean
type Page struct {
Title string `json:"title" binding:"required"`
Active bool `json:"active" binding:"required,boolean"`
}
Below is sample Test case with one positive and negative
func TestPage(t *testing.T) {
tests := []struct {
testName string
input string
wantErr bool
}{
{
testName: "positive test",
input: `{
"title": "first book title",
"active": false
}`,
wantErr: false,
}, {
testName: "wrong boolean",
input: `{
"title": "second book title",
"active": falsee
}`,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
var p Page
b := []byte(tt.input)
err := json.Unmarshal(b, &p)
assert.Nil(t, err, "got error %v", err)
})
}

How to use golang create nest json array object

I try to query database and use query result to create json like these
[ {"TransID": "Transaction ID1",ProductID": ["ProID1","ProID2","ProID3","ProID4" ]},
{"TransID": "Transaction ID2","ProductID": ["ProID5","ProID6" ]} ]
so I create type struct from
type DataRecent []struct {
TransID string `json:"transID"`
ProductID []string `json:"productID"`}
and golang code is
var dataRecent DataRecent
var recent [5]string
for _, Trans := range recent {
if Trans != "" {
var TransID, ProductID string
selectTrans, err := db.Query("select transaction_id, product_id from detail where transaction_id = ?", Trans)
var arr []string
for selectTrans.Next() {
if err != nil {
panic(err.Error())
}
errTrans := selectTrans.Scan(&TransID, &ProductID)
if errTrans != nil {
panic(errTrans.Error())
}
arr = append(arr, ProductID)
}
}
dataRecent.TransID = Trans
dataRecent.ProductID = arr
}
c.JSON(http.StatusOK, gin.H{"status": "success", "message": "Find transactions success", "recent_trans": dataRecent})
defer db.Close()
but I can't build the code and got error
dataRecent.TransID undefined (type DataRecent has no field or method TransID)
dataRecent.ProductID undefined (type DataRecent has no field or method ProductID)
I don't know what to do and stuck with these for a week. I am new programmer for golang. Help me pleae, Thank you
Just remove the array when you create the struct
type DataRecent struct {
TransID string `json:"transID"`
ProductID []string `json:"productID"`
}
and do
var dataRecent []DataRecent
it will works for you.
looks like dataRecent isn't initialized. I suggest you use dataRecent := DataRecent{} instead of var dataRecent DataRecent.
some other insights:
I'm not sure if you've omitted the usage of make of the recent string array, or you're not aware you need to make() it. Anyway, arrays are values in Go, and if you're new to Go I strongly suggest you use slices instead. https://blog.golang.org/go-slices-usage-and-internals
Also, I'm not sure why you have to panic() in case you've found an error (in the words of Dave Cheney, panic means "game over man" - https://dave.cheney.net/tag/panic)

aws-sdk-go dynamodbattribute unmarshal list of maps not unmarshalling

I have a Dynamodb Table that has the following struct:
type StatusItem struct {
requestStatus string
timestamp string
RequestId string
}
I have the code as such:
items := []StatusItem{}
err = dynamodbattribute.UnmarshalListOfMaps(result.Items, &items)
if err != nil {
exitWithError(fmt.Errorf("failed to unmarshal Query result items, %v", err))
}
// Print out the items returned
for i, item := range items {
fmt.Println(i)
fmt.Printf(item.RequestId, item.requestStatus)
}
However, items is empty.
when i put a watcher in items, i can see the it unmarshals to the struct, but all the values are empty.
if i do:
log.println(result.items) i can see the values there (though a bit ugly)
%!(EXTRA string=)[map[timestamp:{
S: "2018-04-18 12:04:43.761"
} RequestId:{
S: "9"
} requestStatus:{
S: "REQUEST_RECEIVED"
}]
what am i doing wrong?
Thanks to #Peter for pointing me in the right direction.
The solution is simple (though rather dumb that that was the case).
as noted in : https://til.hashrocket.com/posts/3512417fb0-private-vs-public-struct-members
lowercase starting members are private, and Capital was public.
in order to get it working correctly, i needed to update my struct from:
type StatusItem struct {
requestStatus string
timestamp string
RequestId string
}
to:
type StatusItem struct {
RequestStatus string
Timestamp string
RequestId string
}

Convert interface to struct

type SipField interface {
Info() (id, name, defaultValue string, length int)
}
type Field string
func (f *Field) Get() string {
return string(*f)
}
func (f *Field) Set(s string) {
*f = Field(s)
}
type CommandID Field
func (cid *CommandID) Info() (id, name, defaultValue string, length int) {
return "", "command ID", "", 2
}
type Language Field
func (l *Language) Info() (id, name, defaultValue string, length int)
{
return "", "language", "019", 3
}
func InitField(f interface{}, val string) error {
sipField, ok := f.(SipField)
if !ok {
return errors.New("InitField: require a SipField")
}
_, _, defaultValue, length := sipField.Info()
field, ok := f.(*Field)
if !ok {
return errors.New("InitField: require a *Field")
}
return nil
}
How should I do for converting interface{} to Field(CommandID, Language...) in InitField() function? I try to directly type assert by
field, ok := f.(*Field)
but it not working.I have tried to use unsafe.Pointer but failed also.
Have a look at Type assertions chapter in Go reference. It states:
x.(T)
More precisely, if T is not an interface type, x.(T) asserts that the dynamic type of x is identical to the type T.
Types CommandID and Field are not identical as described in Type identity.
A defined type is always different from any other type.
Both types CommandId and Fields are defined as described in Type definitions.
A type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it.
TypeDef = identifier Type .
You can only do
field, ok := f.(*CommandID)
or
field, ok := f.(*Language)
As #mkopriva mentioned in the comment, you can do type conversion later to *Field but this it does not seem to be your goal.
Other solution is to introduce a Field interface with Set and Get methods. Then you will need to provide an implementation for every implementing type.

gorp PreUpdate method update involuntary columns

see below.
type User struct {
Id int64 `db:"id" json:"id"`
Name string `db:"name" json:"name"`
DateCreate int64 `db:"date_create"`
DateUpdate int64 `db:"date_update"`
}
func (u *User) PreInsert(s gorp.SqlExecutor) error {
u.DateCreate = time.Now().UnixNano()
u.DateUpdate = u.DateCreate
return nil
}
func (u *User) PreUpdate(s gorp.SqlExecutor) error {
u.DateUpdate = time.Now().UnixNano()
return nil
}
I executed INSERT.
user := model.User{
Name: "John",
}
err := dbMap.Insert(&user)
Result of INSERT. no problem
1,John,1444918337049394761,1444918337049394761
continue, I executed UPDATE.
user := model.User{
Id: 1,
Name: "John",
}
_, err := dbMap.Update(&user)
Result of UPDATE
1,John,0,1444918337049394900
Column DateCreate updated.
Of course, my expectation values are
1,John,1444918337049394761,1444918337049394900
That's because when initializing your user struct, you did not explicitely set user.DateCreate, which effectively sets it to 0.
gorp cannot guess which fields you mean to update or not, so it updates them all.
To do what you want, you have to choose between trade-offs
Get the struct from the ORM, with the field already set to the good value. That's one additional select query that will be executed.
Execute a custom query, losing the convenience of the ORM
I'm guessing you could have another struct type without this field. That seems messy, I do not recommend that.

Resources