1
0
Fork 0
mirror of https://github.com/sourcegraph/jsonrpc2.git synced 2026-06-19 05:30:03 +02:00

Add ability to omit params member from request (#62)

The JSON-RPC 2.0 specification allows the params member of a request to
be omitted [1]. Before this commit, this library did not allow the
params member to be omitted when sending a request. When the params
argument of the Conn.Call/Conn.DispatchCall or Conn.Notify method was
set to nil, then Request.Params was set to the JSON encoding of nil
which is null.

This commit adds a CallOption named OmitNilParams. If OmitNilParams is
used when sending a request with params set to nil, then the params
member in the JSON encoding of Request is omitted. If the OmitNilParams
option is not used then the previous behavior is maintained. In other
words, the changes in this commit are backwards compatible.

References
[1]: https://www.jsonrpc.org/specification#request_object
This commit is contained in:
Sam Herrmann 2023-01-24 01:47:36 -05:00 committed by GitHub
parent e1f9fdf1bb
commit 8012d49686
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 137 additions and 8 deletions

View file

@ -2,7 +2,10 @@ package jsonrpc2_test
import (
"context"
"encoding/json"
"fmt"
"net"
"sync"
"testing"
"github.com/sourcegraph/jsonrpc2"
@ -140,3 +143,111 @@ func TestExtraField(t *testing.T) {
t.Fatal(err)
}
}
func TestOmitNilParams(t *testing.T) {
rawJSONMessage := func(v string) *json.RawMessage {
b := []byte(v)
return (*json.RawMessage)(&b)
}
type testCase struct {
callOpt jsonrpc2.CallOption
sendParams interface{}
wantParams *json.RawMessage
}
testCases := []testCase{
{
sendParams: nil,
wantParams: rawJSONMessage("null"),
},
{
sendParams: rawJSONMessage("null"),
wantParams: rawJSONMessage("null"),
},
{
callOpt: jsonrpc2.OmitNilParams(),
sendParams: nil,
wantParams: nil,
},
{
callOpt: jsonrpc2.OmitNilParams(),
sendParams: rawJSONMessage("null"),
wantParams: rawJSONMessage("null"),
},
}
assert := func(got *json.RawMessage, want *json.RawMessage) error {
// Assert pointers.
if got == nil || want == nil {
if got != want {
return fmt.Errorf("got %v, want %v", got, want)
}
return nil
}
{
// If pointers are not nil, then assert values.
got := string(*got)
want := string(*want)
if got != want {
return fmt.Errorf("got %q, want %q", got, want)
}
}
return nil
}
newClientServer := func(handler jsonrpc2.Handler) (client *jsonrpc2.Conn, server *jsonrpc2.Conn) {
ctx := context.Background()
connA, connB := net.Pipe()
client = jsonrpc2.NewConn(
ctx,
jsonrpc2.NewPlainObjectStream(connA),
noopHandler{},
)
server = jsonrpc2.NewConn(
ctx,
jsonrpc2.NewPlainObjectStream(connB),
handler,
)
return client, server
}
for i, tc := range testCases {
t.Run(fmt.Sprintf("test case %v", i), func(t *testing.T) {
t.Run("call", func(t *testing.T) {
handler := jsonrpc2.HandlerWithError(func(ctx context.Context, c *jsonrpc2.Conn, r *jsonrpc2.Request) (result interface{}, err error) {
return nil, assert(r.Params, tc.wantParams)
})
client, server := newClientServer(handler)
defer client.Close()
defer server.Close()
if err := client.Call(context.Background(), "f", tc.sendParams, nil, tc.callOpt); err != nil {
t.Fatal(err)
}
})
t.Run("notify", func(t *testing.T) {
wg := &sync.WaitGroup{}
handler := handlerFunc(func(ctx context.Context, conn *jsonrpc2.Conn, req *jsonrpc2.Request) {
err := assert(req.Params, tc.wantParams)
if err != nil {
t.Error(err)
}
wg.Done()
})
client, server := newClientServer(handler)
defer client.Close()
defer server.Close()
wg.Add(1)
if err := client.Notify(context.Background(), "f", tc.sendParams, tc.callOpt); err != nil {
t.Fatal(err)
}
wg.Wait()
})
})
}
}