Issue implementing interface method that returns another interface

I am having a question about how to implement interfaces correctly in Go when it comes to third-party packages that use chained methods. I have compiled an example project below for you so that you can understand the problem.

package main

import (
	myAPI "github.com/hashicorp/vault/api"
)

var myClient *myAPI.Client

type MyProvider interface {
	GetClient() MyAPIClient
}

type MyAPIClient interface {
	// I want to do this but it does not work
	Logical() MyAPILogical
	// This works though
	// Logical() *myAPI.Logical
}

type MyAPILogical interface {
	Write(path string, data map[string]interface{}) (*myAPI.Secret, error)
}

type Provider struct {}

func PublicFunctionIWantToTest(provider MyProvider) {
	client := provider.GetClient()
	// We normally do something here with the 'client' variable, but important
	// is that we forward it later on
	privateFunctionThatIsUsedInTheTest(client)
}

func privateFunctionThatIsUsedInTheTest(client MyAPIClient) (*myAPI.Secret, error) {
	return client.Logical().Write(
		"/here/goes/some/path",
		map[string]interface{}{
			"key": "value",
		},
	)
}

func NewProvider() MyProvider {
	return Provider{}
}

func (p Provider) GetClient() MyAPIClient {
	return myClient
}

// Empty function just so that this example can be built
func main() {}

As you can see, the package has a chained method Logical().Write() . Since I want to create tests for PublicFunctionIWantToTest, I want to pass down all the functionality as interface so that I can use mockery to create mocks for it.

Unfortunately, I am hitting an issue with my MyAPIClient and the MyAPILogical interface. Since I can see in the package’s documentation (api package - github.com/hashicorp/vault/api - Go Packages) that the Logical() method returns a Logical instance, I want to make it so that interface method returns the other interface MyAPILogical (line 15). This does not work though, there is an error on line 47 in the GetClient() method saying that I would not implement the interface correctly. How could I do that?

cannot use myClient (variable of type *api.Client) as MyAPIClient value in return statement: *api.Client does not implement MyAPIClient (wrong type for method Logical)
		have Logical() *api.Logical
		want Logical() 

Thank you kindly

I’m not familiar with that package and I don’t yet see the “big picture” of what you’re trying to do, but you’re getting that error because, like you said, (*api.Client).Logical returns *api.Logical, not MyAPILogical, and even though the interface is implemented by *api.Logical, the MyAPIClient interface must be matched exactly.

One workaround could be to wrap the *api.Client type into a struct and implement your Logical function through that:

type myAPIClient struct{ *myAPI.Client }

func (c myAPIClient) Logical() MyAPILogical {
	return c.Client.Logical()
}

var myClient myAPIClient
1 Like

Thank you for the help @skillian !

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.