Skip to content

Commit 06f546d

Browse files
James-Bartman=
and
=
authored
Adding Emit Defaults Option (#281)
## Problem Statement When using gRPC UI, I came across a scenario where I did not want default values emitted to the Response tab. While gRPCurl has an option to enable/disable emit-defaults, gRPC UI does not since the call it makes to gRPCurl hard-codes emit-defaults to true. Thus, there was an inconsistency between these two closely related tools. ## Example Proto File ```Proto service Greeter { rpc SayHello (HelloRequest) returns (HelloReply); } message HelloRequest { string name = 1; } message TwoNums { int32 abc = 1; int32 xyz = 2; } message HelloReply { oneof HelloReplies { TwoNums nums = 1; int32 num = 2; } string answer = 3; } ``` ## Sample Server Implementation - Written in Go [server.txt](https://github.com/fullstorydev/grpcui/files/13245072/server.txt) ## gRPC UI Output ```JSON { "nums": { "abc": 3, "xyz": 3 }, "answer": "" } ``` ## gRPCurl Output with `-emit-defaults=false` ```JSON { "nums": { "abc": 3, "xyz": 3 } } ``` ## Fix - Added `emit-defaults` flag to the `cmd/grpcui.go` file. - Set a global variable in `handlers.go` to accept value of `emit-defaults` flag. - The default value of this flag is `true`, to preserve existing behavior ```bash grpcui -h ... -emit-defaults Emit default values for JSON-encoded responses. (default true) ... ``` ## gRPC UI Output After Fix with `-emit-defaults=false` ```JSON { "nums": { "abc": 3, "xyz": 3 } } ``` --------- Co-authored-by: = <[email protected]>
1 parent 0afbf93 commit 06f546d

File tree

4 files changed

+30
-14
lines changed

4 files changed

+30
-14
lines changed

cmd/grpcui/grpcui.go

+3
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,8 @@ var (
116116
maxMsgSz = flags.Int("max-msg-sz", 0, prettify(`
117117
The maximum encoded size of a message that grpcui will accept. If not
118118
specified, defaults to 4mb.`))
119+
emitDefaults = flags.Bool("emit-defaults", true, prettify(`
120+
Emit default values for JSON-encoded responses.`))
119121
debug optionalBoolFlag
120122
verbose = flags.Bool("v", false, prettify(`
121123
Enable verbose output.`))
@@ -625,6 +627,7 @@ func main() {
625627
if examplesOpt != nil {
626628
handlerOpts = append(handlerOpts, examplesOpt)
627629
}
630+
handlerOpts = append(handlerOpts, standalone.EmitDefaults(*emitDefaults))
628631
handlerOpts = append(handlerOpts, configureJSandCSS(extraJS, standalone.AddJSFile)...)
629632
handlerOpts = append(handlerOpts, configureJSandCSS(extraCSS, standalone.AddCSSFile)...)
630633
handlerOpts = append(handlerOpts, configureAssets(otherAssets)...)

handlers.go

+18-14
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ type InvokeOptions struct {
5858
// includes conflicting metadata, the values in the HTTP request headers
5959
// will override, and the values in the request will not be sent.
6060
PreserveHeaders []string
61+
// Whether or not default values should be emitted in the JSON response
62+
EmitDefaults bool
6163
// If verbosity is greater than zero, the handler may log events, such as
6264
// cases where the request included metadata that conflicts with the
6365
// ExtraMetadata and PreserveHeaders fields above. It is an int, instead
@@ -457,8 +459,9 @@ func invokeRPC(ctx context.Context, methodName string, ch grpc.ClientConnInterfa
457459
}
458460

459461
result := rpcResult{
460-
descSource: descSource,
461-
Requests: &reqStats,
462+
descSource: descSource,
463+
emitDefaults: options.EmitDefaults,
464+
Requests: &reqStats,
462465
}
463466
if err := grpcurl.InvokeRPC(ctx, descSource, ch, methodName, invokeHdrs, &result, requestFunc); err != nil {
464467
return nil, err
@@ -539,12 +542,13 @@ type rpcError struct {
539542
}
540543

541544
type rpcResult struct {
542-
descSource grpcurl.DescriptorSource
543-
Headers []rpcMetadata `json:"headers"`
544-
Error *rpcError `json:"error"`
545-
Responses []rpcResponseElement `json:"responses"`
546-
Requests *rpcRequestStats `json:"requests"`
547-
Trailers []rpcMetadata `json:"trailers"`
545+
descSource grpcurl.DescriptorSource
546+
emitDefaults bool
547+
Headers []rpcMetadata `json:"headers"`
548+
Error *rpcError `json:"error"`
549+
Responses []rpcResponseElement `json:"responses"`
550+
Requests *rpcRequestStats `json:"requests"`
551+
Trailers []rpcMetadata `json:"trailers"`
548552
}
549553

550554
func (*rpcResult) OnResolveMethod(*desc.MethodDescriptor) {}
@@ -556,12 +560,12 @@ func (r *rpcResult) OnReceiveHeaders(md metadata.MD) {
556560
}
557561

558562
func (r *rpcResult) OnReceiveResponse(m proto.Message) {
559-
r.Responses = append(r.Responses, responseToJSON(r.descSource, m))
563+
r.Responses = append(r.Responses, responseToJSON(r.descSource, m, r.emitDefaults))
560564
}
561565

562566
func (r *rpcResult) OnReceiveTrailers(stat *status.Status, md metadata.MD) {
563567
r.Trailers = responseMetadata(md)
564-
r.Error = toRpcError(r.descSource, stat)
568+
r.Error = toRpcError(r.descSource, stat, r.emitDefaults)
565569
}
566570

567571
func responseMetadata(md metadata.MD) []rpcMetadata {
@@ -583,15 +587,15 @@ func responseMetadata(md metadata.MD) []rpcMetadata {
583587
return ret
584588
}
585589

586-
func toRpcError(descSource grpcurl.DescriptorSource, stat *status.Status) *rpcError {
590+
func toRpcError(descSource grpcurl.DescriptorSource, stat *status.Status, emitDefaults bool) *rpcError {
587591
if stat.Code() == codes.OK {
588592
return nil
589593
}
590594

591595
details := stat.Proto().Details
592596
msgs := make([]rpcResponseElement, len(details))
593597
for i, d := range details {
594-
msgs[i] = responseToJSON(descSource, d)
598+
msgs[i] = responseToJSON(descSource, d, emitDefaults)
595599
}
596600
return &rpcError{
597601
Code: uint32(stat.Code()),
@@ -601,9 +605,9 @@ func toRpcError(descSource grpcurl.DescriptorSource, stat *status.Status) *rpcEr
601605
}
602606
}
603607

604-
func responseToJSON(descSource grpcurl.DescriptorSource, msg proto.Message) rpcResponseElement {
608+
func responseToJSON(descSource grpcurl.DescriptorSource, msg proto.Message, emitDefaults bool) rpcResponseElement {
605609
anyResolver := grpcurl.AnyResolverFromDescriptorSourceWithFallback(descSource)
606-
jsm := jsonpb.Marshaler{EmitDefaults: true, OrigName: true, Indent: " ", AnyResolver: anyResolver}
610+
jsm := jsonpb.Marshaler{EmitDefaults: emitDefaults, OrigName: true, Indent: " ", AnyResolver: anyResolver}
607611
var b bytes.Buffer
608612
if err := jsm.Marshal(&b, msg); err == nil {
609613
return rpcResponseElement{Data: json.RawMessage(b.Bytes())}

standalone/opts.go

+8
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,13 @@ func PreserveHeaders(headerNames []string) HandlerOption {
188188
})
189189
}
190190

191+
// EmitDefaults tells gRPCurl whether or not default values should be emitted
192+
func EmitDefaults(emit bool) HandlerOption {
193+
return optFunc(func(opts *handlerOptions) {
194+
opts.emitDefaults = emit
195+
})
196+
}
197+
191198
// WithInvokeVerbosity indicates the level of log output from the gRPC UI server
192199
// handler that performs RPC invocations.
193200
func WithInvokeVerbosity(verbosity int) HandlerOption {
@@ -229,6 +236,7 @@ type handlerOptions struct {
229236
defaultMetadata []string
230237
extraMetadata []string
231238
preserveHeaders []string
239+
emitDefaults bool
232240
invokeVerbosity int
233241
debug *bool
234242
}

standalone/standalone.go

+1
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ func Handler(ch grpcdynamic.Channel, target string, methods []*desc.MethodDescri
9393
invokeOpts := grpcui.InvokeOptions{
9494
ExtraMetadata: uiOpts.extraMetadata,
9595
PreserveHeaders: uiOpts.preserveHeaders,
96+
EmitDefaults: uiOpts.emitDefaults,
9697
Verbosity: uiOpts.invokeVerbosity,
9798
}
9899
rpcInvokeHandler := http.StripPrefix("/invoke", grpcui.RPCInvokeHandlerWithOptions(ch, methods, invokeOpts))

0 commit comments

Comments
 (0)