Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support ReceiveFunc for client. #73 #205

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions clientoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"fmt"

"github.com/cenkalti/backoff/v4"
"reflect"
"strings"
)

// WithConnection sets the Connection of the Client
Expand Down Expand Up @@ -94,16 +96,46 @@ func WithHttpConnection(ctx context.Context, address string, options ...func(*ht
func WithReceiver(receiver interface{}) func(Party) error {
return func(party Party) error {
if client, ok := party.(*client); ok {
if client.receiver != nil {
return errors.New("client already has a receiver")
}
client.receiver = receiver
if receiver, ok := receiver.(ReceiverInterface); ok {
receiver.Init(client)

hubType := reflect.TypeOf(receiver)
hubValue := reflect.ValueOf(receiver)

for i := 0; i < hubType.NumMethod(); i++ {
methodName := strings.ToLower(hubType.Method(i).Name)
receiver.SetFunc(methodName, hubValue.Method(i))
}
}
return nil
}
return errors.New("option WithReceiver is client only")
}
}

// ReceiveFunc set the handler function, and it will be added to the receiver. a default one will be used, If there is no receiver.
func ReceiveFunc(name string, fn interface{}) func(Party) error {
return func(party Party) error {
if client, ok := party.(*client); ok {
if client.receiver == nil {
receiver := &Receiver{}
receiver.Init(client)
client.receiver = receiver
}
if receiver, ok := client.receiver.(ReceiverInterface); ok {
receiver.SetFunc(name, fn)
return nil
}
return errors.New("existing receiver does not support adding methods")
}
return errors.New("option ReceiveFunc is client only")
}
}

// WithBackoff sets the backoff.BackOff used for repeated connection attempts in the client.
// See https://pkg.go.dev/github.com/cenkalti/backoff for configuration options.
// If the option is not set, backoff.NewExponentialBackOff() without any further configuration will be used.
Expand Down
11 changes: 9 additions & 2 deletions loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,13 +369,20 @@ func buildMethodArguments(method reflect.Value, invocation invocationMessage,
}

func getMethod(target interface{}, name string) (reflect.Value, bool) {
if receiver, ok := target.(ReceiverInterface); ok {
if method := receiver.GetFunc(name); method != nil {
return reflect.ValueOf(method), true
}
}

hubType := reflect.TypeOf(target)
if hubType != nil {
hubValue := reflect.ValueOf(target)
name = strings.ToLower(name)
lowCaseName := strings.ToLower(name)

for i := 0; i < hubType.NumMethod(); i++ {
// Search in public methods
if m := hubType.Method(i); strings.ToLower(m.Name) == name {
if m := hubType.Method(i); strings.ToLower(m.Name) == lowCaseName {
return hubValue.Method(i), true
}
}
Expand Down
43 changes: 40 additions & 3 deletions receiver.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,35 @@
package signalr

// ReceiverInterface allows receivers to interact with the server directly from the receiver methods
// Init(Client)
//
// Init(Client)
//
// Init is used by the Client to connect the receiver to the server.
// Server() Client
//
// Server() Client
//
// Server can be used inside receiver methods to call Client methods,
// e.g. Client.Send, Client.Invoke, Client.PullStream and Client.PushStreams
//
// GetFunc(string) interface{}
//
// GetFunc is used to get the handler function by handler name.
//
// SetFunc(string, interface{})
//
// SetFunc is used to set the mapping between the name and the handler function.
type ReceiverInterface interface {
Init(Client)
Server() Client
GetFunc(string) interface{}
SetFunc(string, interface{})
}

// Receiver is a base class for receivers in the client.
// It implements ReceiverInterface
type Receiver struct {
client Client
methodMap map[string]interface{}
client Client
}

// Init is used by the Client to connect the receiver to the server.
Expand All @@ -26,3 +41,25 @@ func (ch *Receiver) Init(client Client) {
func (ch *Receiver) Server() Client {
return ch.client
}

// GetFunc is used to get the handler function by handler name.
func (ch *Receiver) GetFunc(name string) interface{} {
return ch.methodMap[name]
}

// SetFunc is used to set the mapping between the name and the handler function.
func (ch *Receiver) SetFunc(name string, fn interface{}) {
if ch.methodMap == nil {
ch.methodMap = make(map[string]interface{})
}
if _, ok := ch.methodMap[name]; ok {
info, _ := ch.client.loggers()
_ = info.Log(evt, "set function", "warn", "method has been overridden", "method", name)
}
ch.methodMap[name] = fn
}

// On is an alias for SetFunc
func (ch *Receiver) On(name string, fn interface{}) {
ch.SetFunc(name, fn)
}