Type assertions with interface embedding in Go - go

I have an interface and it is embedded into multiple structs that
form a decorator pattern.
Now I have added code to typecast Validator and APIService to the API interface. So that if I add and a function to the interface then I should be able to add it at Validator and APIService.
But this code seems to compile.
I should have got a compiler error as I have not implemented Get on Validator
If I remove Get from APIService I get an error which says I cant typecast to *APIService to API as get is not implemented.
package main
import "fmt"
type API interface {
Get()
}
var _ API = (*Validator)(nil)
var _ API = (*APIService)(nil)
type APIService struct {
id string
}
type Validator struct {
API
}
type APIRouter struct {
API
}
func(a *APIService) Get(){
fmt.Println("Calling Get api on APIService")
}
func NewAPIRouter() *APIRouter{
return &APIRouter{
API: &Validator{
API: &APIService{
},
},
}
}
func main() {
var api API = NewAPIRouter()
api.Get()
}
If I compile the above code:
./main
Calling Get api on APIService
But if I comment Get function on APIService i get the below error which is valid:
go build
# main
./main.go:9:5: cannot use (*APIService)(nil) (type *APIService) as type API in assignment:
*APIService does not implement API (missing Get method)
I should have got a compiler error before as well as I have not implemented Get on Validator
If I remove Get from APIService I get an error which says I cant typecast to *APIService to API as get is not implemented.

The type APIService is a struct with a method Get, so it implements API interface.
The type Validator is a struct with an embedded interface API. This means, a Validator instance has an embedded member that implements the API interface, and since it is embedded, Validator also implemented the API interface. However, note that you have to initialize Validator.API to an implementation first. Something like this should work:
v:=Validator{API:&APIService{}}
With this, v.Get() will call the embedded APIService.Get.

Related

How to use a type parameter in an interface method?

I am trying to write a sample program to try and implementing the a data structure using Go generics.
As part of this I want to define a iterator interface. I have the following code:
package collection
type Iterator interface {
ForEachRemaining(action func[T any](T) error) error
// other methods
}
It keeps giving me following error
function type cannot have type parameters
Moving the type parameter to the method also doesn't work:
type Iterator interface {
ForEachRemaining[T any](action func(T) error) error
// other methods
}
Gives error:
methods cannot have type parameters
Is there any way to define generic interface
As the error suggests, methods cannot have type parameters of their own as per the latest design. However, they can use the generics from the interface or struct they belong to.
What you need is to specify the type parameter on the interface type as follows:
type Iterator[T any] interface {
// ...
}
and then use the T as any other type parameter in methods within the interface body. For example:
package main
import "fmt"
type Iterator[T any] interface {
ForEachRemaining(action func(T) error) error
// other methods
}
func main() {
fmt.Println("This program compiles")
}
Try it on Go playground.

How to mock nested third party dependencies?

Example third party API usage:
fund doSomething(api *api.Client) {
...
result, err := api.Logical().Write(val1, val2)
...
}
I can handle the initial call to Logical() with my own interface:
type API interface {
Logical() *api.Logical
}
...
doSomething(&MockAPI{}) // Assuming MockAPI implements API
However, this now brings the problem into sight: the Logical() method has to have the above function signature, otherwise, I couldn't substitute the real api object in for my interface. Since the *api.Logical type is nested within the third party API library, I cannot simply mock it out with another interface:
type Writer interface {
Write(string, string) Result, error
}
type API interface {
Logical() *Writer
}
...
doSomething(&api.Client{}) // Doesn't implement Logical() *Writer
How would I mock this API call out so I can return custom data and not hit a live service? If it helps, this is based on an actual API.
I think the best answer here is to just avoid the chained function call altogether.
type Writer interface {
Write(string, string) Result, error
}
func doSomething(writer Writer) {
...
result, err := writer.Write(val1, val2)
...
}
...
doSomething(api.Client.Logical())
Now I can implement my own Writer and use it for mocking.

Interface with method that has interface{} parameter not working

I'm trying to implement some interfaces in Go.
I have InterfaceA:
type InterfaceA interface{
read(interface{}) string
}
I then have InterfaceB:
type InterfaceB interface{
fetch()
}
I have a function:
func Read(a InterfaceA) {}
I have StructA, which satisfies InterfaceA via it's methods, but instead of having a variable "interface{}", it is passed InterfaceB as such:
type StructA struct {}
func (a *StructA) read(b InterfaceB) string {
resp := b.fetch()
return resp
}
This seems to work for my unit tests, but when I import the package I get an error stating that InterfaceA is not implemented correctly, because it is expecting "read(interface{})" not "read(InterfaceB)". Error:
StructA does not implement InterfaceA (wrong type for read method)
have read(InterfaceB) string
want read(interface {}) string
The reason I'm trying to pass an interface to the read() function is so that I can mock out the "i.fetch()" method, but still test the rest of the read() function.
I also can't pass the read method of StructA an interface parameter (e.g. read(b interface{}), because then b won't have the fetch() method.
Am I doing this wrong? I thought that this was working as my unit tests work. I've only encountered the issue when importing the package.
thanks for the comments. I've managed to get this to work via type type assertions. More details can be found here: golang.org/ref/spec#Type_assertions

Go mock interface with vendored imports

I'm using the Docker API and pass the Client to a function like this:
func DoStuffToDocker(ctx context.Context, c *client.Client, ID string) {
c.ContainerInspect(ctx, ID)
// do stuff
}
I create an interface that defines the functions I use so that I can pass a mock in my tests:
type dockerClient interface {
ContainerInspect(ctx context.Context, containerID string) (types.ContainerJSON, error)
}
... and then change the signature of my function to accept the interface instead of the client.
My problem is that the compiler complains that the client doesn't implement the interface. From what I can tell the reason is that the implementation of ContainerInspect uses the golang.org/x/net/context context which the API has vendored (so the import is actually github.com/docker/docker/vendor/golang.org/x/net/context).
How can I create the interface so that the client implements it?

What is the best way to have dependency injection in Golang

I'm a beginner in Golang and I'm working on a small library which need to get a DB connection at some point in the code for différent sub package / method call. I'm just wondering how I can manage this ?
Example, If I manage to have a webserver, it works with handler, so how can I get this connection inside this function ?
It could be used with another process, simple method call or MVC model ?
I don't want to use global because for me it's a bad practice except if it's very exceptional way (or tricky somehow).
I read a lot of write in different website, but still, I'm asking and learning from different opinion and experiences.
Thanks for your time !
Create a struct that represent the resource​, let's call Cart. Add get and post methods to this struct. These methods should be http handlers. In main create an instance of the struct with db interface. And in the route call Cart.get. Now in get method you have access to the db interface.
Not a working example, just to get the idea of injecting for testing.
type storage interface {
PrepareContext(context.Context, string) (*sql.Stmt, error)
}
func main() {
db, _ := sql.Open("mysql", `queryString`)
http.HandleFunc("/", Cart{db}.get)
http.ListenAndServe(":8080", nil)
}
type Cart struct {
storage
}
func (crt Cart) get(w http.ResponseWriter, r *http.Request) {
q, _ := crt.PrepareContext(context.Background(), `select *`)
fmt.Println(q.Exec())
}
/////////Test
type testDB struct{}
func (c testDB) PrepareContext(context.Context, string) (*sql.Stmt, error) {
return nil, nil
}
func TestGet(t *testing.T) {
db := testDB{}
_ = Cart{db}
//http test here
}
I would suggest Dargo which is an injection engine in the style of Java CDI and/or JSR-330. It uses struct annotations and Injection is performed using reflection or using creator functions. It supports different lifecycles for services including Singleton (created exactly once, lazily), PerLookup (created every time injected or looked up), Immediate (created exactly once, eagerly) and DargoContext (tied to the lifecycle of a context.Context)
You can also try Hiboot which is a web/cli application framework that support dependency injection out of box.
Docs
// HelloService is a simple service interface, with interface, we can mock a fake service in unit test
type HelloService interface {
SayHello(name string) string
}
type helloServiceImpl struct {
}
func init() {
// Register Rest Controller through constructor newHelloController
// Register Service through constructor newHelloService
app.Register(newHelloController, newHelloService)
}
// please note that the return type name of the constructor HelloService,
// hiboot will instantiate a instance named helloService for dependency injection
func newHelloService() HelloService {
return &helloServiceImpl{}
}
// SayHello is a service method implementation
func (s *helloServiceImpl) SayHello(name string) string {
return "Hello" + name
}
// PATH: /login
type helloController struct {
web.Controller
helloService HelloService
}
// newHelloController inject helloService through the argument helloService HelloService on constructor
func newHelloController(helloService HelloService) *helloController {
return &helloController{
helloService: helloService,
}
}
// Get /
// The first word of method name is the http method GET
func (c *helloController) Get(name string) string {
return c.helloService.SayHello(name)
}
I would suggest giving a try to https://github.com/facebookgo/inject. It allows to define an object graph and specify dependencies with struct annotations. Injection is performed using reflection.
For an IoC container, you can try this package:
https://github.com/golobby/container
Example of singleton binding:
container.Singleton(func() Database {
return &MySQL{}
})
Example of resolving:
var db Database
container.Make(&db)
As you can see it's so easy to work with.

Resources