Override interface's function callback arguments - go

I am using a package that has a Router interface, and I have created my own app-specific Router interface that wraps the third party package.
Everything is working well, however one of the methods is throwing a compilation error:
controllers/auth.go:52:17: cannot use func literal (type func(router.Router)) as type func(chi.Router) in argument to c.router.Group
This is the interface of the third party package (chi):
type Router interface {
// ...
// Group adds a new inline-Router along the current routing
// path, with a fresh middleware stack for the inline-Router.
Group(fn func(r Router)) Router
// ...
}
This is my wrapper interface:
type Router interface {
chi.Router
// Custom methods...
}
My usage of the Group function is like so:
type AuthController struct {
router router.Router
// ...
}
func (c *AuthController) SetRoutes() {
c.router.Group(func(r router.Router) {
r.Use(middleware.Anyone)
r.Post("/auth/register", c.Register)
r.Post("/auth/login", c.Authenticate)
r.Post("/auth/token/refresh", c.RefreshToken)
})
c.router.Group(func(r router.Router) {
r.Use(middleware.Authorized)
r.Get("/auth/ping", c.Ping)
r.Post("/auth/logout", c.Logout)
})
}
Why is it screaming at my function callbacks argument type? My wrapper router.Router implements the chi.Router interface, so it should work fine shouldn't it? Am I misunderstanding how Go works here?

I can see how this can be confusing so I will try to break it down. You have this method:
Group(fn func(r Router)) Router
This method takes a function as a parameter. That function must have a specific signature:
func(r Router)
That is, it takes a single argument of type chi.Router and has no return values. However, when you call it:
c.router.Group(func(r router.Router) { /***/ }
You're passing in a function of the wrong signature; your function signature is:
func(r router.Router)
That's not the signature required by the method you're calling, so it won't compile. It doesn't matter if router.Router implements chi.Router; the parameter (a func(router.Router)) passed is not of the expected type (a func(chi.Router)).
This may seem silly at first - after all, any router.Router must implement chi.Router. But, think about it: that method, Group, is expecting to receive a function, to which it can pass any chi.Router. That means it can pass a chi.Router which does not implement router.Router. If it were to accept your function, it would break type safety, and what in Go is meant to be a compile-time error (the error you're getting, in fact) would become a run-time error. Basically, by passing a function with a different (and more strict) argument type, you're expecting a guarantee which that method never offered.

The parameter types aren't the same, so the function type doesn't match what's expected, even though your interface includes the interface from the other package (the type has to match exactly). You need to have your functions take a chi.router and then use a type assertion, i.e., myRouter := r.(Router), to convert to your type.

Related

Golang: Autocomplete missing interface methods

I am currently working on a go project where I define an interface to use in another file. My workflow works but I'm confused at either why there doesn't seem to be any "auto-complete" for protocols or why I cannot find it, I am using VSCode.
First of all, I am not fluent in go at all so if I misuse the term "object" for example that's because I don't know any better.
Let's say the interface looks like that:
type TestInterface interface {
Function1()
}
and my function to pass an object of that interface to looks like that
func Start(obj TestInterface) {
...
Now, in order to define a type that I can construct an object of, I do
type TestInterfaceType int
and because I have to conform to the interface defined, I need to apply these functions to my "new" type
func (e TestInterfaceType) Function1() {
fmt.Println("Test")
}
I am then able to construct an object
var testInterfaceTypeObject TestInterface
testInterfaceTypeObject = TestInterfaceType(1)
and passing that object to my Start() function above which expects a type of that interface works fine since the object of my "new type" has the functions defined that are defined on the interface.
Everything until here works as I have expected, or at least it works for me.
Now if I add another function to the TestInterface but I do not apply that new function to my TestInterfaceType, I see an error coming up that is InvalidIfaceAssign:
InvalidIfaceAssign occurs when a value of type T is used as an
interface, but T does not implement a method of the expected
interface.
I understand why this is happening, I also have to do the func (e TestInterfaceType) ... for my new function but here is my question:
Is there a way to automatically generate empty mock functions for the missing functions? In Java for example, the IDE offers me to add the missing functions (without functionality of course, but at least they are there, ready for me to write logic into them)?
In other words, once I add Function2() to TestInterface, can I make it automatically do
func (e TestInterfaceType) Function2() {
}
wherever I use that interface?

Implementing interface type to function type in Golang

I have created few types including interface as:
// GetProfileHandlerFunc turns a function with the right signature into a get profile handler
type GetProfileHandlerFunc func(GetProfileParams, interface{}) middleware.Responder
// Handle executing the request and returning a response
func (fn GetProfileHandlerFunc) Handle(params GetProfileParams, principal interface{}) middleware.Responder {
return fn(params, principal)
}
// GetProfileHandler interface for that can handle valid get profile params
type GetProfileHandler interface {
Handle(GetProfileParams, interface{}) middleware.Responder
}
Now in my api implementation package. I am using a logic to handle the request parameters. I am trying to assign GetProfileHandlerFunc to another type since it implements GetProfileHandler interface as you can see above.
api.ProfileGetProfileHandler = profile.GetProfileHandlerFunc(func(params profile.GetProfileParams, principal *models.User) middleware.Responder {
// contains logic to handle the request
}
Now I think I can do above logic. But I am getting type mismatch error.
cannot convert func literal (type func(profile.GetProfileParams,
*"userproj/models".User)
middleware.Responder) to type profile.GetProfileHandlerFuncgo
the point is:
if you have a function like
func A(param interface{}) {}
you can pass anything to param when you make a call to function A. like
A(10)
A(true)
A(nil)
Because interface{} means everything. So your handle func definition:
type GetProfileHandlerFunc func(GetProfileParams, interface{}) middleware.Responder
means a function GetProfileHandlerFunc which takes two params, the first is of type GetProfileParams, the second is of type interface{}. That means second param canbe anything.
But
func(params profile.GetProfileParams, principal *models.User) middleware.Responder
means a function which takes two params, the first is of type GetProfileParams, the second is of type *models.User. So, do you think they are the same? No.
I need a function can take anything as second param,not a function who can only take User as second function.
When you call your handle
GetProfileHandlerFunc(params, 10) // this is ok
So is that ok for
func(params profile.GetProfileParams, principal *models.User) middleware.Responder
No.
The right way to do this is :
api.ProfileGetProfileHandler = profile.GetProfileHandlerFunc(func(params profile.GetProfileParams, principal interface) middleware.Responder {
user:=principal.(*model.User) // watch this.
}

Function as argument, access inner parameter

The package valyala/fasthttp implements the following function type:
type RequestHandler func(ctx *RequestCtx)
It is used in buaazp/fasthttprouter like this:
func (r *Router) Handle(method, path string, handle fasthttp.RequestHandler) {
//...
}
I am trying to wrap these like this (open for suggestions on implementation):
//myapp/router
type Request struct {
fasthttp.RequestCtx
}
type RequestHandler func(*Request)
func Handle(method string, path string, handler RequestHandler) {
//I need to access the fasthttp.RequestCtx stuff in here...
}
How can I achieve this? Or, if this is not the way to go at all, how can I achieve my goal as mentioned below for a router package?
BACKGROUND
Goal: My wish is to wrap tooling packages (sessions, database, routing, etc.) in order to make my app agnostic to the implementation of these packages. I wish to do this primarily for the purpose of being able to extend these with domain-specific functionality, and being able to switch one 3rd party lib for another, if I ever would need to do so. It also makes debugging and logging easier.
Method: I create native types and functions, which map to the functionality of the imported packages.
Problem: I am stuck on how to wrap a foreign (i.e. imported) function type properly.
At all your idea looks very good. Some things you could change:
//myapp/router
// Using a composition is idiomatic go code
// this should work. It can't get better.
type Request struct {
fasthttp.RequestCtx
}
// I would make the RequestHandler as a real Handler. In go it would be
// a interface
type RequestHandler interface{
Request(*Request)
}
// If you have a function, which needs to access parameters from `Request`
// you should take this as an input.
func Handle(method string, path string, req *Request) {
//Access Request via req.Request ...
}
Because if you pass a function or an interface into your function, which needs also Request as input the caller needs to create that before he calls your Handle function. Why not change that function just for the input you really need?

How pass different structures to function?

I have several different structures.
Here show two:
type AdsResponse struct {
Body struct {
Docs []struct {
ID int `json:"ID"`
// others
} `json:"docs"`
} `json:"response"`
Header `json:"responseHeader"`
}
type OtherResponse struct {
Body struct {
Docs []struct {
ID int `json:"ID"`
// others
} `json:"docs"`
} `json:"response"`
Header `json:"responseHeader"`
}
but i don't know how i can do for this method accepts and return both.
func Get(url string, response Response) (Response, bool) {
res, err := goreq.Request{
Uri: url,
}.Do()
// several validations
res.Body.FromJsonTo(&response)
return response, true
}
And use like this:
var struct1 AdsResponse
var struct2 OtherResponse
Get("someURL", struct1)
Get("someURL", struct2)
There are any form?
Your code example is somewhat confusing since both structs appear to be identical. I'll assume that they differ somewhere in "others".
First, I generally recommend creating a wrapper around these kinds of JSON deserializations. Working directly on the JSON structure is fragile. Most of your program should not be aware of the fact that the data comes down in JSON. So for instance, you can wrap this in an Ads struct that contains an AdsResponse, or just copies the pieces it cares about out of it. Doing that will also make some of the below slightly easier to implement and less fragile.
The most common solution is probably to create an interface:
type Response interface {
ID() int
}
You make both Ads and Others conform to Response. Then you can return Response. If necessary, you can type-switch later to figure out which one you have and unload other data.
switch response := response.(type) {
case Ads:
...
case Other:
...
}
I don't quite get why you have the reponse as a parameter and as a return. I think you dont need to return it. You should pass a pointer to the reponse and fill it with the data. Also, I'd return an Error instead of a boolean, but that is another topic.
Anyway, the solution is to use interface{} (empty interface).
You are lucky because the function you are using (FromJsonTo) accepts an empty interface as a parameter, so you can safely change your parameter type to interface{} and just pass it to FromJsonTo. Like this:
func Get(url string, response interface{}) bool {
res, err := goreq.Request{
Uri: url,
}.Do()
// several validations
res.Body.FromJsonTo(response)
return true
}
Warning: I did not compile the code.
Then you would call this function with the url and a pointer to one of the reponse structs like this:
var struct1 AdsResponse
var struct2 OtherResponse
Get("someURL", &struct1)
Get("someURL", &struct2)
The way to achieve this is through Go's interfaces.
Two options:
empty interface
Get(url string, response interface{}) (Response, bool)
This option allows any value to be given to this function.
custom interface
Creating a custom interface will allow you to narrow down the types that can be provided as arguments to your function.
In this case you'll have to create an interface that all your Response structs will need to abide by. Any struct really that abides by that interface will be able to be used as an argument of your function.
Something like this:
type MyResponse interface {
SomeFunction()
}
Then your function signature could look like
Get(url string, response MyResponse) (MyResponse, bool)
As long as AdsResponse and OtherResponse abide by the MyResponse interface, they will be allowed to be used as arguments to the function.
Follow the solution working at Go Playground
Go has no polymorphic or any other OO like behaviour, so, when you try to pass a AdsResponse or OtherResponse struct as an Response (or any interface{}), these values becomes an Response (or other param type specified), and is not possible to Go to infer the real type that originate these interface{} and correctly decode your json to these struct types as expected.
This kind of thing should works perfectly in OO languages, like Java, C# etc. There is no hierarchy generalization/specialization on structs/interfaces in Go.
You would need to do a type assertion in your Rest executor, or a switch case, but it seems that you need a generic REST executor, like a generic lib some thing like that. Would not reasonable create a switch case for each struct in your program. Maybe you have dozens or hundreds of structs soon.
I think that a reasonable solution is the rest client pass a lambda function to do the last step for your, that is just create a correct struct destination type and call json decode.
As i say above, the return type of executeRest() in my example will became an interface{}, but the rest client can securely do the type assertion of returned value after executeRest() call.

How to pass interfaces indirectly in golang

I have a package with a method:
func Route(router *mux.Router){
subrouter := router.PathPrefix(_API).Subrouter()
subrouter.Path(_FOO).HandlerFunc(foo)
subrouter.Path(_BAR).HandlerFunc(bar)
}
and I would like to remove the external dependency of mux by having a matching interface in my package that simple encompasses all the functionality used above, like so:
type Router interface{
Path(string) Path
PathPrefix(string) Path
}
type Path interface{
HandlerFunc(http.HandlerFunc)
Subrouter() Router
}
func Route(router Router){
subrouter := router.PathPrefix(_API).Subrouter()
subrouter.Path(_FOO).HandlerFunc(foo)
subrouter.Path(_BAR).HandlerFunc(bar)
}
but when I build this I get error:
*mux.Router does not implement api.Router (wrong type for Path method)
have Path(string) *mux.Route
want Path(string) api.Path
but I thought interfaces were implicitly used in golang so I thought that *mux.Route did implement my Path interface.
I thought interfaces were implicitly used in golang
Values are wrapped in interfaces implicitly, but only in specific cases like when passing an implementation to a function with an interface argument:
func X(api.Path) {}
X(&mux.Route{}) // works, implicitly converted to api.Path
or when returning an implementation from a function with an interface return type:
func Y() api.Path {
return &mux.Route{} // works, implicitly converted to api.Path
}
In your question's case the compiler wants a value that has a method with the signature:
Path(string) api.Path
But you're giving it a value with a method with signature:
Path(string) *mux.Route
As you might now, Go types are invariant. Formally:
type A interface { Path(string) *mux.Route }
is not a subtype of
type B interface { Path(string) api.Path }
Therefore this won't work.

Resources