Substitute an interface for another one - go

Recently, I stumbled upon an interesting article regarding the organization of Go code.
I was highly interested in the third approach: 'Independent Packages'.
It's said packages should NOT depend on each other, but defines their own interface to interact with
other packages.
Now, I face a problem with the approach.
I'm building a CLI applications that's able to execute various commands, one after each other.
It's important that these commands can share data.
First, I have built a contain in which I can register commands.
This resides in the package: commands.
import (
"sync"
)
// PRIVATE: Defines the API for sharing data between different commands.
type context interface {
SetAPIKey(string)
}
// Command defines the API that a command should satisfy to become eligible for being registered in the container
type Command interface {
GetName() string
SetProperty(prop, val string) error
Execute(context) error
}
// Container exposes the API for registration and retrieving of services.
type Container struct {
mutex sync.RWMutex
registeredCommands map[string]Command
}
// Add adds the command `cmd` to the container.
func (c *Container) Add(cmd Command) {
c.withLock(func() {
if c.registeredCommands == nil {
c.registeredCommands = make(map[string]Command)
}
c.registeredCommands[cmd.GetName()] = cmd
})
}
// Get retrieves the command who's name is equal to `name`.
func (c *Container) Get(name string) Command {
return c.withRLock(func() Command {
if c.registeredCommands == nil {
return nil
}
if cmd, ok := c.registeredCommands[name]; ok {
return cmd
}
return nil
})
}
// PRIVATE: Create a lock and execute the function `handler`.
// The lock is being released when the function `handler` is executed.
func (c *Container) withLock(handler func()) {
c.mutex.Lock()
defer c.mutex.Unlock()
handler()
}
// PRIVATE: Create a "read" lock and execute the function `handler`.
// The lock is being released when the function `handler` is executed.
func (c *Container) withRLock(handler func() Command) Command {
c.mutex.RLock()
defer c.mutex.RUnlock()
return handler()
}
Next I have command which implements the Command interface.
NOTE: This is stored in another package: rest.
import (
"bytes"
"encoding/json"
"encoding/xml"
"errors"
"fmt"
"io/ioutil"
"net/http"
)
// PRIVATE: Defines the API for sharing data between different commands.
type context interface {
SetAPIKey(string)
}
// GetAPIKey is the command that allows the retrieval of a API key using the REST API.
type GetAPIKey struct {
}
// GetName returns the name of the command `cmd`.
func (GetAPIKey) GetName() string {
return "API.REST.getAPIKey"
}
// SetProperty assigns value `val` to property `prop`.
func (cmd *GetAPIKey) SetProperty(prop, val string) error {
return nil
}
// Execute runs the command.
func (cmd GetAPIKey) Execute(ctx context) error {
ctx.SetAPIKey(xmlResponse.Key)
return nil
}
The MAIN entry point of the application is defined as follows:
// PRIVATE: Defines the container in which all the available commands are registered.
var container commands.Container
// PRIVATE: Defines an API for sharing data between different commands.
type context struct {
apiKey string
}
func (ctx *context) SetAPIKey(value string) {
ctx.apiKey = value
}
// PRIVATE: Represents the 'MAIN' entry point for the application.
// This method is executed when the application is started.
func main() {
ctx := &context{}
container := new(commands.Container)
container.Add(&rest.GetAPIKey{})
command := container.Get("API.REST.getAPIKey")
command.SetProperty("baseURI", "")
command.SetProperty("environmentNameOrURL", "")
command.SetProperty("userName", "")
command.SetProperty("password", "")
err := command.Execute(ctx)
if err != nil {
fmt.Println(err)
}
}
However this code does NOT compile.
It yields the following error:
.\app.go:53:16: cannot use &rest.GetAPIKey literal (type *rest.GetAPIKey) as type commands.Command in argument to container.Add:
*rest.GetAPIKey does not implement commands.Command (wrong type for Execute method)
have Execute(rest.context) error
want Execute(commands.context) error
Is there any way to make this code work?
So the context interface is defined in 2 places.

This will not work because the function signatures don't match. You can do:
Make Context a shared interface, so every package can use it. This does not necessarily introduce dependencies between packages using it.
Use context.Context, and expect to see the key in there. This is essentially the same as #1 but using a known predefined type
Use interface{} instead of context, and use type assertion to convert it to the local context type.

Related

task.Task undefined (type entities.Task has no field or method Task)

package entities
type Task struct {
Id int64
task string
assignee string
deadline string
action string
}
package taskcontroller
import (
"fmt"
"html/template"
"net/http"
"github.com/jeypc/go-crud/config/entities"
)
func Index(response http.ResponseWriter, request *http.Request) {
temp, err := template.ParseFiles("config/views/task/index.html")
if err != nil {
panic(err)
}
temp.Execute(response, nil)
}
func Add(response http.ResponseWriter, request *http.Request) {
if request.Method == http.MethodGet {
temp, err := template.ParseFiles("config/views/task/add.html")
if err != nil {
panic(err)
}
temp.Execute(response, nil)
} else if request.Method == http.MethodPost {
request.ParseForm()
var task entities.Task
task.Task = request.Form.Get("task")
task.Assignee = request.Form.Get("assignee")
task.Deadline = request.Form.Get("deadline")
fmt.Println(task)
}
}
OK, so this is probably going to get closed, as this is a simple typo, or just a case of RT(f)M. I'd recommend you walk through the golang tour. Exported names is something explained on page 3 of the basics, so pretty much at the start of the tour. If you haven't looked at the tour at all, the tour starts here
Still, I'll answer your question using your specific code:
In golang, capitalisation matters. In your case, you have a package called entities, in which you define the type Task, with a capital T. Because you're using a capital as the first character of the name, the type is exported. When you import the entities package in your taskcontroller package, you'll be able to use entities.Task. Had you written:
package entities
type task struct {}
you would not be able to write t := entities.task{}. To put it in conventional OOP terms: any name beginning with a lower-case character is private. If they start with an upper-case character, they are public.
Now looking at your type:
type Task struct {
Id int64
task string
assignee string
deadline string
action string
}
You have a public type called Task, with 5 fields. One field (Id) is public, and can be accessed outside of the entities package. The other four fields are private, and can only be accessed from inside the entities package.
Now, you're trying to set fields like assignee, task, and so on in the taskcontroller package. You can't do that, because they are private. To be able to access these fields, the simplest way is to change the entities.Task type and make the fields public:
type Task struct {
Id int64
Task string
Assignee string
Deadline string
Action string
}
Then, you'll be able to access all fields in other packages:
package taskcontroller
import "entities"
func someFunc() {
task := entities.Task{
Id: 123,
Task: "foo",
}
// or
task.Assignee = "bar"
}
Sometimes, though, you may want to limit/control access to fields like this, in which case you can use getters/setters. Again, the same rule applies WRT capitalisation:
package entities
// Task as you have it now, with unexported fields
type Task struct {
Id int64
task string
assignee string
deadline string
action string
}
// SetAssignee starts with upper-case S, so can be called from other packages
// this method can be used for validation/normalisation/...
// you'll need to add SetX for each field
func (t *Task) SetAssignee(assignee string) error {
if len(assignee) > 255 {
return errors.New("assignee too long, max length is 255")
}
// assignee is not exported, but using this function its value can be set
t.assignee = assignee
return nil
}
// GetAssignee is exported, can be called everywhere
// again, you'll need to implement a GetX for each unexported field
// can be used to return default values, for example
func (t Task) GetAssignee() string {
if len(t.assignee) == 0 {
return "none"
}
return t.assignee
}
func (t *Task) ToMapAndClear() map[string]interface{} {
// create a map containing the data
r := map[string]interface{}{
"id": t.Id,
"task": t.task,
"assignee": t.assignee,
"deadline": t.deadline,
"action": t.action,
}
// clear all fields. We can call reset from inside the package
t.reset()
return r
}
// ExtractMap same function as above, but with defer so it's less verbose
func (t *Task) ExtractMap() map[string]interface{} {
defer t.reset()
return map[string]interface{
"id": t.Id,
"task": t.task,
"assignee": t.assignee,
"deadline": t.deadline,
"action": t.action,
}
}
// reset starts with lower-case s, so cannot be called outside of this package
func (t *Task) reset() {
*t = Task{}
}
With these methods, you can use the entities.Task type in other packages like so:
package main
import (
"fmt"
"entities"
)
func main() {
t := entities.Task{
Id: 123,
}
if err := t.SetAssignee("Benjamin"); err != nil {
fmt.Printf("Could not set assignee: %v\n", err)
return
}
fmt.Printf("Task assigned to %s\n", t.GetAssignee()) // works fine
// does not work
t.reset() // main package does not have access to reset method
// this does work - we get the values in a map, and reset gets called internally
data := t.ExtractMap()
}
As you can see, though I can't call reset directly outside of the main package, I can call exported methods that call reset internally.

implement a generic argument method

I have code in Go, which includes many execution methods.
Each method receives it own struct of parameters.
I want a dispatcher to call each method with its related struct.
The dispatcher receives the name of the execution method, and a JSON of the parameters struct.
Then, it uses reflection to build the struct, and calls the method.
The problem is I get compilation error unless I create the execution methods using empty interface.
In the example below I have 2 execution methods: api1 is compiling, but is using the empty interface and explicit casting. api2 is what I want to do, but it is failing with compile error:
cannot use api2 (type func(Api2Parameters)) as type Api in assignment
How can I make api2 usage compile?
import (
"encoding/json"
"log"
"reflect"
)
type Api func(arguments interface{})
type ApiDetails struct {
Executor *Api
ParametersType reflect.Type
}
var Apis map[string]*ApiDetails
func RunApi(apiName string, data string) {
api := Apis[apiName]
parameters := reflect.New(api.ParametersType).Interface().(interface{})
_ = json.Unmarshal([]byte(data), parameters)
(*api.Executor)(parameters)
}
type Api1Parameters struct {
Count1 int
Id1 string
}
func api1(arguments interface{}) {
parameters, _ := arguments.(*Api1Parameters)
log.Printf("api1 parameters(%+v)", parameters)
}
type Api2Parameters struct {
Count2 int
Id2 string
}
func api2(arguments Api2Parameters) {
log.Printf("api2 parameters(%+v)", arguments)
}
func Test() {
// this assignment works fine
var api_1 Api = api1
Apis["api1"] = &ApiDetails{
Executor: &api_1,
ParametersType: reflect.TypeOf(Api1Parameters{}),
}
// this assignment produce compile error
var api_2 Api = api2
Apis["api2"] = &ApiDetails{
Executor: &api_2,
ParametersType: reflect.TypeOf(Api2Parameters{}),
}
RunApi("api1", `{"Count1":19, "Id1":"a"}`)
RunApi("api2", `{"Count2":34, "Id2":"b"}`)
}
Create a value using the argument type, unmarshal to that value and invoke the function:
var Apis = map[string]interface{}{
"api1": api1,
"api2": api2,
}
func RunApi(apiName string, data string) {
fv := reflect.ValueOf(Apis[apiName])
ft := fv.Type()
pin := reflect.New(ft.In(0))
_ = json.Unmarshal([]byte(data), pin.Interface())
fv.Call([]reflect.Value{pin.Elem()})
}
Run it on the playground.
If you want to keep this type-safe, you can push the JSON-to-args conversion a little deeper:
type Api func(string)
type ApiDetails struct {
// Don't use *Api, that's a function pointer-pointer
Executor Api
}
Then, use a closure to do the JSON translation:
Apis["api1"] = &ApiDetails{
Executor: func(data string) {
var args ArgsForAPI1
json.Unmarshal([]byte(string),&args)
api1Implem(args)
}
}
With this, you can do:
APIs["api1"].Executor( `{"Count1":19, "Id1":"a"}`)
You can change the RunApi to do something like this.
A second option is to do this using reflection:
type ApiDetails struct {
// Executor can accept any function signature
// Assume a function with a single parameter
Executor interface{}
ParametersType reflect.Type
}
Then, the RunApi function can use reflection to construct a struct and call Executor:
parameters := reflect.New(api.ParametersType)
_ = json.Unmarshal([]byte(data), parameters.Interface{})
reflect.ValueOf(api.Executor).Call([]reflect.Value{parameters})
This should work, but you'll only get runtime errors if you mess up.

go wire, wiring up the dependencies

I am using Google wire to wire up the dependencies in one of my Go projects. So far all the dependencies were wired up successfully.
I have following container:
type Container struct {
Logger logger.StructuredLogger
IDGenerator idgenerator.IDGenerator
Arango arangodb.Arango
}
Here StructuredLogger, IDGenerator and Arango are interface.
The container set is defined as below:
var mockContainerSet = wire.NewSet(
Container{},
ResolveStructuredLogger,
ResolveIDGenerator,
ResolveArangoDB,
)
Functions:
func ResolveStructuredLogger() logger.StructuredLogger {
panic(wire.Build(wire.InterfaceValue(new(logger.StructuredLogger), logrusprovider.NewLogrusLogger(nil))))
}
func ResolveIDGenerator() idgenerator.IDGenerator {
panic(wire.Build(wire.InterfaceValue(new(idgenerator.IDGenerator), examples.NewKSUIDGenerator())))
}
Now the issue is when I want to create a provider for Arango. The factory method for Arango provider is taking IDGenerator as an argument.
func NewMockedArango(generator idgenerator.IDGenerator) (*MockedArango, error){
if generator == nil {
return nil , errors.New("mock arangodb expects a valid IDGenerator")
}
return &MockedArango{generator:generator}, nil
}
All the functions from arangodb.Arango is implemented. And I tried the following to create a provider:
func ResolveMockArangoDB(idGenerator idgenerator.IDGenerator) (arangodb.Arango, error) {
panic(wire.Build(wire.InterfaceValue(new(arangodb.Arango), mockdb.NewMockedArango(idGenerator))))
}
But the wire is throwing errors, I tried a lot of ways and no success to set this up. Any help?
Error:
2-valued mockdb.NewMockedArango(idGenerator) (value of type
(*github.com/tejashwi/catalog-manager-svc/pkg/webservice/arangodb/mockdb.MockedArango,
error)) where single value is expected
Signature of wire.Interface is
InterfaceValue(typ interface{}, x interface{})
Here you need to pass two single-valued arguments. In the second argument, you are passing 2-valued argument since function mockdb.NewMockedArango is returning 2 values.
You can change function ResolveMockArangoDB to:
func ResolveMockArangoDB(idGenerator idgenerator.IDGenerator) (arangodb.Arango, error) {
mockedArango, err := mockdb.NewMockedArango(idGenerator)
<HANDLE err>
panic(wire.Build(wire.InterfaceValue(new(arangodb.Arango), mockedArango)))
}

Typecasting one function into another in golang

I have the following code:
package vault
type Client interface {
GetHealth() error
}
func (c DefaultClient) GetHealth () error {
resp := &VaultHealthResponse{}
err := c.get(resp, "/v1/sys/health")
if err != nil {
return err
}
return nil;
}
Now, I want to use this function as part of this struct:
type DependencyHealthFunction func() error
type Dependency struct {
Name string `json:"name"`
Required bool `json:"required"`
Healthy bool `json:"healthy"`
Error error `json:"error,omitempty"`
HealthFunction DependencyHealthFunction
}
Basically, set the value of HealthFunction to GetHealth. Now, when I do the following:
func (config *Config) GetDependencies() *health.Dependency {
vaultDependency := health.Dependency{
Name: "Vault",
Required: true,
Healthy: true,
HealthFunction: vault.Client.GetHealth,
}
temp1 := &vaultDependency
return temp1;
}
This gives me an error and it says cannot use vault.Client.GetHealth (type func(vault.Client) error) as type health.DependencyHealthFunction in field value. How can I do this?
Edit: How DependencyHealthFunction is used?
As its part of Dependency struct, it's simply used as following: d.HealthFunction() where d is a variable of type *Dependency.
This is abstract:
HealthFunction: vault.Client.GetHealth,
If we were to call HealthFunction(), what code do you expect to run? vault.Client.GetHealth is just a promise that such a function exists; it isn't a function itself. Client is just an interface.
You need to create something that conforms to Client and pass its GetHealth. For example, if you had a existing DefaultClient such as:
defaultClient := DefaultClient{}
Then you could pass its function:
HealthFunction: defaultClient.GetHealth,
Now when you later call HealthFunction() it will be the same as calling defaultClient.GetHealth().
https://play.golang.org/p/9Lw7uc0GaE
I believe the issue is related to understanding how interfaces are treated in Go.
An interface simply defines a method or set of methods that a particular type must satisfy to be considered as "implementing" the interface.
For example:
import "fmt"
type Greeter interface {
SayHello() string
}
type EnglishGreeter struct{}
// Satisfaction of SayHello method
func (eg *EnglishGreeter) SayHello() string {
return "Hello"
}
type SpanishGreeter struct{}
func (sg *SpanishGreeter) SayHello() string {
return "Ola"
}
func GreetPerson(g Greeter) {
fmt.Println(g.SayHello())
}
func main() {
eg := &EnglishGreeter{}
sg := &SpanishGreeter{}
// greet person in english
GreetPerson(eg)
// greet person in spanish
GreetPerson(sg)
}
You can add this behavior into a custom struct by simply having a Greeter field inside the struct. ie
type FrontEntrance struct {
EntranceGreeter Greeter
}
fe := &FrontEntrance { EntranceGreeter: &EnglishGreeter{} }
// then call the SayHello() method like this
fe.EntranceGreeter.SayHello()
Interfaces in golang are useful for composing common expected behavior for types based on the methods that they satisfy.
Hope this helps.

How to unite two different struct using interface?

I have the following code:
package main
import (
"log"
)
type Data struct {
Id int
Name string
}
type DataError struct {
Message string
ErrorCode string
}
func main() {
response := Data{Id: 100, Name: `Name`}
if true {
response = DataError{Message: `message`, ErrorCode: `code`}
}
log.Println(response)
}
This code returns me an error:
./start.go:20: cannot use DataError literal (type DataError) as type
Data in assignment
Seems to be that I could not assign to response var data with different type (in my case DataError). I heard that possible solution could be to unite Data and DataError structs via interface. Or maybe there is another better solution?
Could you please point me how to resolve this problem?
Thanks
It looks like you're trying to make a union type (what the ML-family of languages calls "enum"). I know of a couple of patterns for this:
0. Basic error handling (playground)
I suspect what you're doing is just basic error handling. In Go, we use multiple return values and check the result. This is almost certainly what you want to do:
package main
import (
"fmt"
"log"
)
type Data struct {
ID int
Name string
}
type DataError struct {
Message string
ErrorCode string
}
// Implement the `error` interface. `error` is an interface with
// a single `Error() string` method
func (err DataError) Error() string {
return fmt.Sprintf("%s: %s", err.ErrorCode, err.Message)
}
func SomeFunction(returnData bool) (Data, error) {
if returnData {
return Data{ID: 42, Name: "Answer"}, nil
}
return Data{}, DataError{
Message: "A thing happened",
ErrorCode: "Oops!",
}
}
func main() {
// this bool argument controls whether or not an error is returned
data, err := SomeFunction(false)
if err != nil {
log.Fatalln(err)
}
fmt.Println(data)
}
1. Interfaces (playground)
Again, if your options are good-data and error, you should probably use the first case (stick with the idiom/convention), but other times you might have multiple "good data" options. We can use interfaces to solve this problem. Here we're adding a dummy method to tell the compiler to constrain the possible types that can implement this interface to those that have an IsResult() method. The biggest downside to this is that sticking things into an interface can incur an allocation, which can be detrimental in a tight loop. This pattern isn't terribly common.
package main
import "fmt"
type Result interface {
// a dummy method to limit the possible types that can
// satisfy this interface
IsResult()
}
type Data struct {
ID int
Name string
}
func (d Data) IsResult() {}
type DataError struct {
Message string
ErrorCode string
}
func (err DataError) IsResult() {}
func SomeFunction(isGoodData bool) Result {
if isGoodData {
return Data{ID: 42, Name: "answer"}
}
return DataError{Message: "A thing happened", ErrorCode: "Oops!"}
}
func main() {
fmt.Println(SomeFunction(true))
}
2. Tagged Union (playground)
This case is similar to the previous case, except instead of using an interface, we're using a struct with a tag that tells us which type of data the struct contains (this is similar to a tagged union in C, except the size of the struct is the sum of its potential types instead of the size of its largest potential type). While this takes up more space, it can easily be stack-allocated, thus making it tight-loop friendly (I've used this technique to reduce allocs from O(n) to O(1)). In this case, our tag is a bool because we only have two possible types (Data and DataError), but you could also use a C-like enum.
package main
import (
"fmt"
)
type Data struct {
ID int
Name string
}
type DataError struct {
Message string
ErrorCode string
}
type Result struct {
IsGoodData bool
Data Data
Error DataError
}
// Implements the `fmt.Stringer` interface; this is automatically
// detected and invoked by fmt.Println() and friends
func (r Result) String() string {
if r.IsGoodData {
return fmt.Sprint(r.Data)
}
return fmt.Sprint(r.Error)
}
func SomeFunction(isGoodData bool) Result {
if isGoodData {
return Result{
IsGoodData: true,
Data: Data{ID: 42, Name: "Answer"},
}
}
return Result{
IsGoodData: false,
Error: DataError{
Message: "A thing happened",
ErrorCode: "Oops!",
},
}
}
func main() {
// this bool argument controls whether or not an error is returned
fmt.Println(SomeFunction(true))
}
You can't assign 2 different types that are not "assignable" to the same variable ... unless you use a specific interface signature or empty interface.
https://golang.org/ref/spec#Assignability
that code would compile :
func main() {
var response interface{} // empty interface AKA Object AKA void pointer
response = Data{Id: 100, Name: `Name`}
if true {
response = DataError{Message: `message`, ErrorCode: `code`}
}
log.Println(response)
}
since every type implements empty interface, but you want to do that only if there is no other options.
if 2 types share some methods use a specific interface, for instance (pseudo-code) :
type Responder interface {
Respond() string
}
type Data struct { /* code */
}
func (d Data) Respond() string { return "" }
type DataError struct { /* code */
}
func (d DataError) Respond() string { return "" }
func main() {
var response Responder // declared as interface
response = Data{}
response = DataError{}
fmt.Println(response)
}
Whenever you have doubts a quick scan of the go spec is useful, it is the only authority and pretty well written compared to most specs out there. For the most part the rules are crystal clear, and that's a strength of Go.

Resources