How to extract values from local context in gofiber - go

I have managed to use the local context to set database query results using a custom middleware. I am trying to see how I can authenticate a user then pull their details from the database and inject it to the context. This has been done.
The local data on the final method on the route is actually an interface and I would like to extract fields from the data I set from the previous auth middleware. How can I work with this interface type to some form like a struct or json so that I can get the fields and values for doing some logic?
user := c.Locals("user")
fmt.Println("checking for locals",user)
From above user is of struct user
{
Name string `json:"name"`
Emain string `json:"email"`
ID string `json:"id"`
Password string `json:"password"`
}
How can I get the name and email ?

So after digging into the fiber docs and reading about interfaces and especially empty interfaces I think I have a solution but stand to be corrected
I saw one can cast an interface to a concrete type. In my case I would take the c.Locals("user") of type var user interface{}
then cast it to a struct passing the pointer to the user model as follows
user := c.Locals("user")
details, ok :=user.(*models.User)
fmt.Println("checking for locals -----------",details.Name)

Related

Do struct tags throw error to the API user?

type Person struct {
Id string
Roll_no int
Email string `json:"email" validate:"required"`
}
The email here is required. If the user does not provide that in the request body of the API call, will Go handle the name validation automatically and throw the error if this field is not provided? Or would i neeI to validate it manually in my code and throw the error in some form of a http status code?
Do struct tags throw error to the API user?
No. For a lot of reasons.
Some struct tags (e.g. the json ones) are just used by package encoding/json to map between JSON object elements and struct fields).
Other struct tags (like validate) are used by certain 3rd party packages and if you do not use that package nothing at all will happen.
Go (the language and its stdlib) is "free from magic". Nothing magically happens. If you need validation on your API endpoints you might use validation packages which inspect struct tags but this won't happen magically: You have to call these functions and return an API error yourself. And no, I won't recommend a framework here.
To work struct tags have to be valid, none of yours are. Run go vet to find this type of problem. (should be json:"emal" validate:"required" and probably even "email")
Errors in Go are not "thrown" at all. Especially HTTP responses with a status code of 400 Bad Request are not "thrown". It always helps to get the terminology correct).
If the JSON sent over API does not contain Email then encoding/json json.Unmarshal will treat it as if Email was an empty string.
Option A: If empty string is not a valid Email, you can check for an empty string after json.Unmarshal
var person Person
err := json.Unmarshal(jsonBlob, &person)
if err != nil{
return err
}
if person.Email == "" {
return errors.New("email cannon be empty"
}
Option B: If empty string is valid, you can create a custom MarshalJSON to parse JSON as a string and look for the Email key.
https://pkg.go.dev/encoding/json#example-package-CustomMarshalJSON

How do I use custom preload in gorm

I have these 2 structs
type User struct {
gorm.Model
Name string
CompanyID int
Company Company
}
type Company struct {
gorm.Model
Name string
Adress string
}
I want to get users and preload their companies but I don't want to get the Adress field I tried the custom preload like bellow and I tested it in postman . the query returned all the fields but for adress I get an empty string , the reason why this is happening is that when storing the result in the user struct golang automatically initialize all the fields and the field adress get returned with its initial value which is an empty sting
var user []User
db.Table("users").Preload("Company",func(db *gorm.DB) *gorm.DB {
return db.Select("ID" ,"Name")
}).Find(&user)
what should I do to only have the name and id but not the adress
If your server exports the result as json you could add the json tag omitempty as such:
type Company struct {
gorm.Model
Name string
Adress string `json:"omitempty"`
}
That way the address field will not be included if it is an empty string.

GORM AutoMigrate Has One & Has Many:

I want to create a model User and Social where the User model has many Socials. Ideally a Social type would also have has one relation to simpilify querying from either side. Here is a code sample:
database type is MySQL 8.0
type base struct {
ID string `json:"id" gorm:"type:char(36);primaryKey;"`
Created time.Time `json:"created" gorm:"autoCreateTime"`
Updated time.Time `json:"updated" gorm:"autoUpdateTime"`
}
type User struct {
base
Friends []*User `json:"friends" gorm:"many2many:friends"`
Socials []*Social `json:"socials"`
}
type Social struct {
base
Provider string `json:"provider" gorm:"type:varchar(32);index"`
Identifier string `json:"identifier" gorm:"type:varchar(32);index"`
User *User `json:"user" gorm:"foreignKey:ID"`
Token string `json:"token"`
Link string `json:"link" gorm:"type:varchar(128)"`
}
Im getting the following error when using db.AutoMigrate(&User{}, &Social{}):
model.Social's field User, need to define a valid foreign key for relations or it need to implement the Valuer/Scanner interface
runtime error: invalid memory address or nil pointer dereference
I have tried:
adding gorm:"foreignKey:ID" to User.Socials tags
not using pointers (eg in User struct Socials []Social instead of Socials []*Social)
but the issue remains
According to documentation (https://gorm.io/docs/has_many.html#Has-Many),
you need to use objects, not references
type User struct {
base
Friends []User `json:"friends" gorm:"many2many:friends"`
Socials []Social `json:"socials"`
}
no * here
Also you can add UserID field to Social
type Social struct {
base
UserID string
Provider string `json:"provider" gorm:"type:varchar(32);index"`
Identifier string `json:"identifier" gorm:"type:varchar(32);index"`
User *User `json:"user" gorm:"foreignKey:ID"`
Token string `json:"token"`
Link string `json:"link" gorm:"type:varchar(128)"`
}
and add
type User struct {
base
FriendOf string `gorm:""`
Friends []*User `json:"friends" gorm:"many2many:friends,foreignKey:FriendOf"`
Socials []*Social `json:"socials"`
}
Issue was here:
type base struct {
...
}
type User {
base
...
}
type Social {
base
...
}
Since I thought base would just be package local definition I messed up the capitalization and had a private primary key.
Another issue was touched on by #vodolaz095, but (imo) not sufficiently clarified for any new go-gorm user.
It does not seem to be possible to use a has one relation like User User be the foreign key for a has many relation like Socials []Social gorm:"foreignKey:User". It is required to be split up as #vodolaz095 displayed in his 2nd code block

Go Interface property is undefined

I have a specific problem where I need a variable that can take the value of one or another structure. I create a userResp structure with a user field which has the value of an interface, but if I add a function that returns a sub property of user called Password, returns error. The value of user will be a struct that have the Password property.
The struct:
type userResp struct {
user interface{}
}
the function
func (ur *userResp) password() string {
return ur.user.Password
}
I get ur.user.Password undefined (type interface {} is interface with no methods) but the interface user can be a Admin or User struct with a Password field.
Any idea how do this, I need to work with an struct of either the user or admin and return it.
I can't use 2 functions because the logic is the same in both cases. I need the entire struct of User or Admin
There are a couple of problems here:
You can't call a method on an empty interface (interface{}) because the empty interface has no methods defined.
You can't reference fields through an interface type, only methods.
You need to determine what behavior something you set as the value of user should have and define an interface to reflect that. For example, if the only thing you need a user to be able to do is provide a password, the interface would be
type User interface {
Password() string
}
Your struct would then be
type userResp struct {
user User
}
You would then return the password like this:
return ur.user.Password()
Whatever types you want to store in the user field would have to have the Password method defined on them so they satisfy the User interface

GORM Not ignoring field with `gorm:"-"`

Using Jinzhu's GORM Package which is fantastic btw, I currently have this struct:
type User struct {
gorm.Model
// The Users username
Username string `gorm:"size:255;unique;not null"`
// The Users email address
Email string `gorm:"size:255;unique;not null"`
// The Users hashed password
Password string `gorm:"size:255;not null"`
// The Users password confirmation (only for forms)
PasswordC string `gorm:"-"`
// The Users FULL NAME (e.g. Burt Reynolds)
Fullname string `gorm:"size:255; not null"`
// The Users Karma level
Karma int
// Is the user banned?
Banned bool
}
But I also use Gorilla's Schema package so any form values populate the struct, but I don't want the PasswordC to be saved into the database because it will be plain text as the normal Password field gets bcrypt'd so any information on how to make GORM not save the PasswordC field.
The docs say gorm:"-", but the code indicates sql:"-" is the correct syntax.
My testing verifies this.
This is no longer correct. The code has been fixed, still use gorm:"-".

Resources