Skip to content

Commit e1e380b

Browse files
committed
ft: add a common name formatter
1 parent 7cff1cc commit e1e380b

File tree

4 files changed

+56
-119
lines changed

4 files changed

+56
-119
lines changed

README.md

Lines changed: 40 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
go-jsonrpc
2-
==================
1+
# go-jsonrpc
32

43
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/github.com/filecoin-project/go-jsonrpc)
54
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](https://protocol.ai)
@@ -24,41 +23,42 @@ func (h *SimpleServerHandler) AddGet(in int) int {
2423
func main() {
2524
// create a new server instance
2625
rpcServer := jsonrpc.NewServer()
27-
26+
2827
// create a handler instance and register it
2928
serverHandler := &SimpleServerHandler{}
3029
rpcServer.Register("SimpleServerHandler", serverHandler)
31-
30+
3231
// rpcServer is now http.Handler which will serve jsonrpc calls to SimpleServerHandler.AddGet
3332
// a method with a single int param, and an int response. The server supports both http and websockets.
34-
33+
3534
// serve the api
3635
testServ := httptest.NewServer(rpcServer)
3736
defer testServ.Close()
38-
37+
3938
fmt.Println("URL: ", "ws://"+testServ.Listener.Addr().String())
40-
39+
4140
[..do other app stuff / wait..]
4241
}
4342
```
4443

4544
### Client
45+
4646
```go
4747
func start() error {
4848
// Create a struct where each field is an exported function with signatures matching rpc calls
4949
var client struct {
5050
AddGet func(int) int
5151
}
52-
52+
5353
// Make jsonrp populate func fields in the struct with JSONRPC calls
5454
closer, err := jsonrpc.NewClient(context.Background(), rpcURL, "SimpleServerHandler", &client, nil)
5555
if err != nil {
5656
return err
5757
}
5858
defer closer()
59-
59+
6060
...
61-
61+
6262
n := client.AddGet(10)
6363
// if the server is the one from the example above, n = 10
6464

@@ -73,28 +73,28 @@ func start() error {
7373
type _ interface {
7474
// No Params / Return val
7575
Func1()
76-
76+
7777
// With Params
7878
// Note: If param types implement json.[Un]Marshaler, go-jsonrpc will use it
7979
Func2(param1 int, param2 string, param3 struct{A int})
80-
80+
8181
// Returning errors
8282
// * For some connection errors, go-jsonrpc will return jsonrpc.RPCConnectionError{}.
8383
// * RPC-returned errors will be constructed with basic errors.New(__"string message"__)
8484
// * JSON-RPC error codes can be mapped to typed errors with jsonrpc.Errors - https://pkg.go.dev/github.com/filecoin-project/go-jsonrpc#Errors
8585
// * For typed errors to work, server needs to be constructed with the `WithServerErrors`
8686
// option, and the client needs to be constructed with the `WithErrors` option
8787
Func3() error
88-
88+
8989
// Returning a value
9090
// Note: The value must be serializable with encoding/json.
9191
Func4() int
92-
92+
9393
// Returning a value and an error
9494
// Note: if the handler returns an error and a non-zero value, the value will not
9595
// be returned to the client - the client will see a zero value.
9696
Func4() (int, error)
97-
97+
9898
// With context
9999
// * Context isn't passed as JSONRPC param, instead it has a number of different uses
100100
// * When the context is cancelled on the client side, context cancellation should propagate to the server handler
@@ -103,9 +103,9 @@ type _ interface {
103103
// * If the context contains an opencensus trace span, it will be propagated to the server through a
104104
// `"Meta": {"SpanContext": base64.StdEncoding.EncodeToString(propagation.Binary(span.SpanContext()))}` field in
105105
// the jsonrpc request
106-
//
106+
//
107107
Func5(ctx context.Context, param1 string) error
108-
108+
109109
// With non-json-serializable (e.g. interface) params
110110
// * There are client and server options which make it possible to register transformers for types
111111
// to make them json-(de)serializable
@@ -115,7 +115,7 @@ type _ interface {
115115
// which will pass reader data through separate http streams on a different hanhler.
116116
// * Note: a similar mechanism for return value transformation isn't supported yet
117117
Func6(r io.Reader)
118-
118+
119119
// Returning a channel
120120
// * Only supported in websocket mode
121121
// * If no error is returned, the return value will be an int channelId
@@ -132,12 +132,13 @@ type _ interface {
132132
```
133133

134134
### Custom Transport Feature
135+
135136
The go-jsonrpc library supports creating clients with custom transport mechanisms (e.g. use for IPC). This allows for greater flexibility in how requests are sent and received, enabling the use of custom protocols, special handling of requests, or integration with other systems.
136137

137138
#### Example Usage of Custom Transport
138139

139140
Here is an example demonstrating how to create a custom client with a custom transport mechanism:
140-
141+
141142
```go
142143
// Setup server
143144
serverHandler := &SimpleServerHandler{} // some type with methods
@@ -175,6 +176,7 @@ fmt.Printf("Current value: %d\n", client.AddGet(5))
175176
```
176177

177178
### Reverse Calling Feature
179+
178180
The go-jsonrpc library also supports reverse calling, where the server can make calls to the client. This is useful in scenarios where the server needs to notify or request data from the client.
179181

180182
NOTE: Reverse calling only works in websocket mode
@@ -244,49 +246,31 @@ if err := client.Call(); err != nil {
244246
}
245247
```
246248

247-
## Options
249+
## Options
248250

251+
### Using `WithMethodNameFormatter`
249252

250-
### Using `WithNamespaceSeparator`
251-
```go
253+
```go
252254
func main() {
253-
// create a new server instance with a custom namespace separator
254-
rpcServer := jsonrpc.NewServer(jsonrpc.WithNamespaceSeparator("_"))
255-
256-
// create a handler instance and register it
257-
serverHandler := &SimpleServerHandler{}
258-
rpcServer.Register("SimpleServerHandler", serverHandler)
259-
260-
// serve the api
261-
testServ := httptest.NewServer(rpcServer)
262-
defer testServ.Close()
263-
264-
fmt.Println("URL: ", "ws://"+testServ.Listener.Addr().String())
255+
// create a new server instance with a custom separator
256+
rpcServer := jsonrpc.NewServer(jsonrpc.WithMethodNameFormatter(
257+
func(namespace, method string) string {
258+
return namespace + "_" + method
259+
}),
260+
)
265261

266-
// rpc method becomes SimpleServerHandler_AddGet
267-
268-
[..do other app stuff / wait..]
269-
}
270-
```
262+
// create a handler instance and register it
263+
serverHandler := &SimpleServerHandler{}
264+
rpcServer.Register("SimpleServerHandler", serverHandler)
271265

272-
### Using `WithMethodNameTransformer`
273-
```go
274-
func main() {
275-
// create a new server instance with a custom method transformer
276-
rpcServer := jsonrpc.NewServer(jsonrpc.WithMethodNameTransformer(toSnakeCase))
277-
278-
// create a handler instance and register it
279-
serverHandler := &SimpleServerHandler{}
280-
rpcServer.Register("SimpleServerHandler", serverHandler)
281-
282-
// serve the api
283-
testServ := httptest.NewServer(rpcServer)
284-
defer testServ.Close()
285-
286-
fmt.Println("URL: ", "ws://"+testServ.Listener.Addr().String())
266+
// serve the api
267+
testServ := httptest.NewServer(rpcServer)
268+
defer testServ.Close()
269+
270+
fmt.Println("URL: ", "ws://"+testServ.Listener.Addr().String())
271+
272+
// rpc method becomes SimpleServerHandler_AddGet
287273

288-
// rpc method becomes SimpleServerHandler.add_get
289-
290274
[..do other app stuff / wait..]
291275
}
292276
```

handler.go

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,10 +77,9 @@ type handler struct {
7777

7878
paramDecoders map[reflect.Type]ParamDecoder
7979

80-
methodNameTransformer MethodNameTransformer
80+
methodNameFormatter MethodNameFormatter
8181

82-
tracer Tracer
83-
separator string
82+
tracer Tracer
8483
}
8584

8685
type Tracer func(method string, params []reflect.Value, results []reflect.Value, err error)
@@ -93,12 +92,11 @@ func makeHandler(sc ServerConfig) *handler {
9392
aliasedMethods: map[string]string{},
9493
paramDecoders: sc.paramDecoders,
9594

96-
methodNameTransformer: sc.methodNameTransformer,
95+
methodNameFormatter: sc.methodNameFormatter,
9796

9897
maxRequestSize: sc.maxRequestSize,
9998

100-
tracer: sc.tracer,
101-
separator: sc.separator,
99+
tracer: sc.tracer,
102100
}
103101
}
104102

@@ -131,11 +129,8 @@ func (s *handler) register(namespace string, r interface{}) {
131129
}
132130

133131
valOut, errOut, _ := processFuncOut(funcType)
134-
if s.methodNameTransformer != nil {
135-
method.Name = s.methodNameTransformer(method.Name)
136-
}
137132

138-
s.methods[namespace+s.separator+method.Name] = methodHandler{
133+
s.methods[s.methodNameFormatter(namespace, method.Name)] = methodHandler{
139134
paramReceivers: recvs,
140135
nParams: ins,
141136

options_server.go

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,7 @@ type jsonrpcReverseClient struct{ reflect.Type }
1313

1414
type ParamDecoder func(ctx context.Context, json []byte) (reflect.Value, error)
1515

16-
type MethodNameTransformer func(string) string
17-
18-
const defaultSeparator = "."
16+
type MethodNameFormatter func(namespace string, method string) string
1917

2018
type ServerConfig struct {
2119
maxRequestSize int64
@@ -24,10 +22,9 @@ type ServerConfig struct {
2422
paramDecoders map[reflect.Type]ParamDecoder
2523
errors *Errors
2624

27-
reverseClientBuilder func(context.Context, *wsConn) (context.Context, error)
28-
tracer Tracer
29-
methodNameTransformer MethodNameTransformer
30-
separator string
25+
reverseClientBuilder func(context.Context, *wsConn) (context.Context, error)
26+
tracer Tracer
27+
methodNameFormatter MethodNameFormatter
3128
}
3229

3330
type ServerOption func(c *ServerConfig)
@@ -38,7 +35,9 @@ func defaultServerConfig() ServerConfig {
3835
maxRequestSize: DEFAULT_MAX_REQUEST_SIZE,
3936

4037
pingInterval: 5 * time.Second,
41-
separator: defaultSeparator,
38+
methodNameFormatter: func(namespace, method string) string {
39+
return namespace + "." + method
40+
},
4241
}
4342
}
4443

@@ -66,15 +65,9 @@ func WithServerPingInterval(d time.Duration) ServerOption {
6665
}
6766
}
6867

69-
func WithNamespaceSeparator(separator string) ServerOption {
70-
return func(c *ServerConfig) {
71-
c.separator = separator
72-
}
73-
}
74-
75-
func WithMethodNameTransformer(methodNameTransformer MethodNameTransformer) ServerOption {
68+
func WithMethodNameFormatter(formatter MethodNameFormatter) ServerOption {
7669
return func(c *ServerConfig) {
77-
c.methodNameTransformer = methodNameTransformer
70+
c.methodNameFormatter = formatter
7871
}
7972
}
8073

rpc_test.go

Lines changed: 2 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"net/http/httptest"
1313
"os"
1414
"reflect"
15-
"regexp"
1615
"strconv"
1716
"strings"
1817
"sync"
@@ -1717,10 +1716,10 @@ func TestNewCustomClient(t *testing.T) {
17171716
require.Equal(t, int32(13), serverHandler.n)
17181717
}
17191718

1720-
func TestReverseCallWithCustomSeparator(t *testing.T) {
1719+
func TestReverseCallWithCustomMethodName(t *testing.T) {
17211720
// setup server
17221721

1723-
rpcServer := NewServer(WithNamespaceSeparator("_"))
1722+
rpcServer := NewServer(WithMethodNameFormatter(func(namespace, method string) string { return namespace + "_" + method }))
17241723
rpcServer.Register("Server", &RawParamHandler{})
17251724

17261725
// httptest stuff
@@ -1750,37 +1749,3 @@ type MethodTransformedHandler struct{}
17501749
func (h *RawParamHandler) CallSomethingInSnakeCase(ctx context.Context, v int) (int, error) {
17511750
return v + 1, nil
17521751
}
1753-
1754-
func TestCallWithMethodTransformer(t *testing.T) {
1755-
// setup server
1756-
1757-
var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
1758-
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
1759-
1760-
rpcServer := NewServer(WithMethodNameTransformer(func(method string) string {
1761-
snake := matchFirstCap.ReplaceAllString(method, "${1}_${2}")
1762-
snake = matchAllCap.ReplaceAllString(snake, "${1}_${2}")
1763-
return strings.ToLower(snake)
1764-
}))
1765-
rpcServer.Register("Raw", &RawParamHandler{})
1766-
1767-
// httptest stuff
1768-
testServ := httptest.NewServer(rpcServer)
1769-
defer testServ.Close()
1770-
1771-
// setup client
1772-
var client struct {
1773-
Call func(ctx context.Context, v int) (int, error) `rpc_method:"Raw.call_something_in_snake_case"`
1774-
}
1775-
closer, err := NewMergeClient(context.Background(), "ws://"+testServ.Listener.Addr().String(), "Raw", []interface{}{
1776-
&client,
1777-
}, nil)
1778-
require.NoError(t, err)
1779-
1780-
// this will block if it's not sent as a notification
1781-
n, err := client.Call(context.Background(), 6)
1782-
require.NoError(t, err)
1783-
require.Equal(t, 7, n)
1784-
1785-
closer()
1786-
}

0 commit comments

Comments
 (0)