Go Interface property is undefined - go

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

Related

Should I explicitly create a relation symmetrical to "Belongs To" or "Has Many"?

I am new to ORM (and GORM) so apologies if this is an obvious question, but it does not seem to be covered by the documentation.
I will be using the examples from the documentation as a base to my questions
Question 1: Belongs To
// `User` belongs to `Company`, `CompanyID` is the foreign key
type User struct {
gorm.Model
Name string
CompanyID int
Company Company
}
type Company struct {
ID int
Name string
}
A User belongs to one Company only → this is handled by the code
above
A Company has many User → is this implied by the code
above? Or should I add somehow a relation O2M in Company?
Question 2: Has Many
// User has many CreditCards, UserID is the foreign key
type User struct {
gorm.Model
CreditCards []CreditCard
}
type CreditCard struct {
gorm.Model
Number string
UserID uint
}
A User has 1+ CreditCard→ this is handled by the code
A CreditCard can belong to several users (say, a shared family CC) → is it implied? (if not: how to set up the O2M relationship).
Or is it, instead, a case where a CreditCard is explicitly configured to belong to only one user?
Q1: Based on how you defined your structs, you don't need an explicit O2M relationship in the Company struct, but when loading Company details, if you want to load all users that are assigned to that specific company, you need to add that field as well. It will need an additional function call like Preload or Joins, but you shouldn't need an explicit definition of this relationship.
type Company struct {
ID int
Name string
Users []User
}
Q2: The way the relationship is defined now, it is configured so that a CreditCard belongs to only one user. If you want a many2many relationship, you need to specify the relation table. There is more documentation on it here, but it should look something like this:
type User struct {
gorm.Model
CreditCards []CreditCard `gorm:"many2many:users_creditcards"`
}
type CreditCard struct {
gorm.Model
Number string
}

How to extract values from local context in gofiber

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)

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

Defining Belongs To relation for anonymous field using GORM

I wanted to create relationnal database structure in a using the go package Gorm.
I wanted to add foreign keys to my structure, it works perfectly on regular field, but it does not seem to work when I want to make foreign key for anonymous fields.
Here is some simplified code to demonstrate my case
type StructA struct {
gorm.Model
SimpleFieldID int // appears in database as foreign key
SimpleField StructB
TranslationID int // appears in database as a regular int
Translation
}
type StructB struct {
gorm.Model
SomeField string
SomeOtherField string
}
type Translation struct {
gorm.Model
En string
Fr string
}
I wanted to have anonymous field in the first place to be able to call some GetName() method on any object that has the Translation attribute.
I found this question, but the given anwser was for the has-one relation, where I want to use a belongs-to relation.

Apollo Graphql modify input data

In Apollo Server, one could use a schema directive to implement a resolver middleware like such:
adminGetUsers(getUsersPL: GetUsersPL!): [User] #hasRole(role: "ADMIN")
#hasRole(role: "ADMIN") serves as a middleware to prevent any non-admin user from using this mutation.
So how would one sanitize/transform input data? For example,
getUser(userId: String! #transform): [User]
#transform will take in userId as a hashed id (ie: xyfd), and transform it to a numbered ID (ie: 12). This syntax is not allowed of course. Basically I want a way to modify input data before it goes into resolver.
That actually is valid syntax. You can define a directive that's applied to argument definitions like this:
directive #test on ARGUMENT_DEFINITION
type Query {
foo(input: String #test): String
}
Schema directives are not middleware. They are just ways of altering individual definitions inside your schema. Most commonly they are used to alter field definitions, but you can alter other definitions like object types, input object types, enums, unions, etc. When using a directive with a field, you can wrap the existing resolve function inside another one (or replace it altogether) -- in doing so, we can create "middleware" for resolvers. However, that's not the purpose of schema directives.
That aside, you can't use an argument directive to alter the value the argument is passed. At best, you can change the type of the argument to something else (like a custom scalar). However, you can just use a field directive to do what you're trying to accomplish:
class ExampleDirective extends SchemaDirectiveVisitor {
public visitFieldDefinition(field) {
const { resolve = defaultFieldResolver } = field
field.resolve = async function (
source,
args,
context,
info,
) {
args.someArg = doSomething(args.someArg)
return resolve.call(this, source, args, context, info);
}
}
}

Resources