Call function of specific type in Go - go

I'm a complete Go newbie, so sorry for the question in advance.
I'm trying to work with a so-defined interface to connect to a message broker:
// Broker is an interface used for asynchronous messaging.
type Broker interface {
Options() Options
Address() string
Connect() error
Disconnect() error
Init(...Option) error
Publish(string, *Message, ...PublishOption) error
Subscribe(string, Handler, ...SubscribeOption) (Subscriber, error)
String() string
}
// Handler is used to process messages via a subscription of a topic.
// The handler is passed a publication interface which contains the
// message and optional Ack method to acknowledge receipt of the message.
type Handler func(Publication) error
// Publication is given to a subscription handler for processing
type Publication interface {
Topic() string
Message() *Message
Ack() error
}
I'm trying to use the Subscribe-function to subscribe to a channel and thats the point where I'm struggeling right now.
My current approach is the following one:
natsBroker.Subscribe(
"QueueName",
func(p broker.Publication) {
fmt.Printf(p.Message)
},
)
The error output is cannot use func literal (type func(broker.Publication)) as type broker.Handler in argument to natsBroker.Subscribe.
But how do I ensure that the function type actually is a broker.Handler?
Thx for your time in advance!
Update
In case anybody is interested, the error return type was missing which caused the error, so it should look similar to that:
natsBroker.Subscribe(
"QueueName",
broker.Handler(func(p broker.Publication) error {
fmt.Printf(p.Topic())
return nil
}),
)

As the error indicates, the parameter and what you're passing don't match:
type Handler func(Publication) error
func(p broker.Publication)
You have no return value. If you add a return value (even if you always return nil), it will work fine.

If your signature of your anonymous function matched that of the handler type declaration (Adrian correctly points out you're missing the error return), you should be able to just do a type conversion:
package main
import "fmt"
type Handler func(int) error
var a Handler
func main() {
a = Handler(func(i int) error {
return nil
})
fmt.Println(isHandler(a))
}
func isHandler(h Handler) bool {
return true
}
Since the the compiler knows at compiler-time that the types match, there's no need to do additional checking, like you might in the case of, say, a type assertion.

Related

How to serialize and deserialize errors string in go

In the context of two go services sharing the same types, one client post a json to a server:
type Message struct {
Error string `json:"error"`
}
The client should serialize an error into a string.
The server should deserialize that string into an error on which I can use errors.As or errors.Is to check for wrapped errors and such.
How may I serialize nested errors ?
Let me share with you some thoughts that maybe can guide you in the right direction.
errors.Is and errors.As
These two operators deal with the error type. The common scenario they are used when you've to deal with an error returned by a function and, based on which type of error it is, take some actions. If you're returning just a string from a function, it's almost impossible to invoke errors.Is and errors.As unless you do some extra logic to handle the string.
A simple workaround
If you wanna preserve a hierarchical structure for your errors without having to flatten them, you could follow this approach:
package main
import (
"encoding/json"
"fmt"
)
type RecursiveErr struct {
Message string `json:"Message"`
Err error `json:"error"`
}
func (r RecursiveErr) Error() string {
return r.Message
}
func main() {
// recursive - marshal
childErr := RecursiveErr{Message: "leaf-child error"}
parentErr := RecursiveErr{Message: "root error", Err: &childErr}
data, _ := json.MarshalIndent(&parentErr, "", "\t")
fmt.Println(string(data))
// recursive - unmarshal
var parsedParentErr RecursiveErr
json.Unmarshal(data, &parsedParentErr)
fmt.Println(parsedParentErr.Message)
}
This solution has the following benefits:
You can preserve the hierarchical structure of your errors (parent-child relationship)
You can invoke the errors.Is and errors.As on the Err field.
As it's an uncommon scenario for me, I hope to not be off-topic with your request, let me know!

How to report custom Go error types to Sentry?

I want my custom error to show up in Sentry, but it just grabs the underlying errorString type.
Is there a way to show operationTimeoutError instead?
Here's what I do:
type operationTimeoutError error
var errOperationTimeout operationTimeoutError = errors.New("TIMEOUT")
func foo() {
sentry.CaptureException(errOperationTimeout)
}
sentry will call the Error() method in the error you provided, which in return prints the error message of the underlying error. you can either override the Error method for the custom error type or map it to a new error with the message you intend to see in sentry. Since its likely that you are using the error for other purposes like logging, I think mapping is the better choice.
func mapError(err error)error{
switch err.(type){
case operationTimeoutError:
return errors.New("operationTimeoutError")
default:
return err
}
}
sentry.CaptureException(mapError(errOperationTimeout))

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.
}

Cannot use implementation of interface as argument to func that wants interface

I'm getting the following error:
./main.go:31: cannot use telegramService (type messaging.TelegramService) as type mypackage.MessagingService in argument to mypackage.RegisterMessagingService:
messaging.TelegramService does not implement mypackage.MessagingService (wrong type for HandleIncomingMessage method)
have HandleIncomingMessage(telegram.Message) error
want HandleIncomingMessage(mypackage.IncomingMessage) error
I have an interface that describes a messaging service like Telegram or WhatsApp, and an interface that describes an incoming message from one of those services:
// IncomingMessage is a message that comes in on a messaging service
type IncomingMessage interface {
Send() error
}
// MessagingService is a service on which messages can be send (like Telegram or FB Messenger)
type MessagingService interface {
Start()
HandleIncomingMessage(IncomingMessage) error
GetHTTPHandler() http.HandlerFunc
GetCommands() []MessagingCommand
}
The first implementation of MessagingService is for Telegram. The issue is the HandleIncomingMessage function, which currently doesn't really do anything and just looks like this:
// HandleIncomingMessage will take an incoming message and repond to it
func (s TelegramService) HandleIncomingMessage(msg *telegram.Message) error {
return nil
}
The issue is that this function accepts a telegram.Message, which the compiler says doesn't comply with the interface. The thing is, that telegram.Message is an implementation of IncomingMessage:
// Message is a Telegram message
type Message struct {
// Added the line below at some point, but it didn't work without it either
mypackage.IncomingMessage
MessageID uint64 `json:"message_id"`
FirstName string `json:"first_name"`
Username string `json:"username"`
Date uint64 `json:"date"`
Text string `json:"text"`
Chat Chat `json:"chat"`
From User `json:"from"`
}
// Send will take m and send it
func (m Message) Send() error {
// Do stuff
return nil
}
Initially IncomingMessage was an empty interface, which is where I first noticed the issue. I tried adding the function Send() which I was going to add anyway, as I thought maybe just giving it any struct wouldnt't work. However, I'm still getting this error.
I don't see any reason why telegram.Message doesn't implement the interface, it's pretty straight forward.
Can anyone explain why this doesn't work?
PS: My package isn't actually called mypackage, changed for clarity
HandleIncomingMessage must take an IncomingMessage argument since that's the way the interface is defined. You can't define an implementation of HandleIncomingMessage that takes some other type as the argument, even if that type implements IncomingMessage. You can define your function to take IncomingMessage and convert that to *telegram.Message using a type assertion:
func (s TelegramService) HandleIncomingMessage(im IncomingMessage) error {
msg := im.(*telegram.Message)
return nil
}
I'm assuming you actually want to be using a pointer to telegram.Message. If so, you need to change the definition of the Send method to take a pointer receiver.

Create custom error with stack trace

I'm trying to use go-errors to include stack traces with errors I generate. I have a custom HttpError type that I also want to include stack traces. My initial thought was to do this with an embed, but I can't seem to embed it since the name of the class (Error) is the same as the name of one of the methods.
package netutil
import (
"github.com/go-errors/errors"
)
type HttpError struct {
status int
*errors.Error
}
func (h *HttpError) Error() string {
return "Failed"
}
func NewHttpError(status int, message string) *HttpError {
return &HttpError{
status,
errors.New(message),
}
}
I receive the following error:
tmp_error.go:12: type HttpError has both field and method named Error
Any suggestions?
Why not just name this inner error with something appropriate like inner-error or stack-trace?
type HttpError struct {
status int
StackTace *errors.Error
}
Seems to be fairly common practice in classes used for error handling in other languages/frameworks like .NET and Java.
Another option would be to concatenate your custom message with the inner error using fmt.Sprintf at the time you create the error, keeping it all as one.
errors.New(fmt.Sprintf("This proprietary error happened! Stack trace: %s", message));
If you did that you wouldn't implement func (h *HttpError) Error() since you'd be relying on the embedded error for this.

Resources