diff --git a/app/cli_commands.go b/app/cli_commands.go index 466e115a..afd713ef 100644 --- a/app/cli_commands.go +++ b/app/cli_commands.go @@ -11,8 +11,9 @@ import ( func newCLICallCommand(flags *flags, ui cui.UI) *cobra.Command { var ( - out string - enrich bool + out string + enrich bool + emitDefaults bool ) cmd := &cobra.Command{ Use: "call [options ...] ", @@ -34,7 +35,13 @@ func newCLICallCommand(flags *flags, ui cui.UI) *cobra.Command { if len(args) == 0 { return errors.New("method is required") } - invoker, err := mode.NewCallCLIInvoker(ui, args[0], cfg.file, cfg.Config.Request.Header, enrich, out) + invoker, err := mode.NewCallCLIInvoker(ui, args[0], &mode.CallCLIInvokerOption{ + Headers: cfg.Config.Request.Header, + Enrich: enrich, + EmitDefaults: emitDefaults, + FilePath: cfg.file, + FormatType: out, + }) if err != nil { return err } @@ -50,6 +57,7 @@ func newCLICallCommand(flags *flags, ui cui.UI) *cobra.Command { f := cmd.Flags() initFlagSet(f, ui.Writer()) f.BoolVar(&enrich, "enrich", false, `enrich response output includes header, message, trailer and status`) + f.BoolVar(&emitDefaults, "emit-defaults", false, `render fields with default values`) f.StringVarP(&out, "output", "o", "curl", `output format. one of "json" or "curl". "curl" is a curl-like format.`) cmd.SetHelpFunc(usageFunc(ui.Writer(), []string{"file"})) diff --git a/app/commands.go b/app/commands.go index 5748d2dc..82406f38 100644 --- a/app/commands.go +++ b/app/commands.go @@ -113,7 +113,10 @@ func newOldCommand(flags *flags, ui cui.UI) *command { if cfg.repl || !isCLIMode { return runREPLCommand(cfg, ui) } - invoker, err := mode.NewCallCLIInvoker(ui, cfg.call, cfg.file, cfg.Config.Request.Header, false, "") + invoker, err := mode.NewCallCLIInvoker(ui, cfg.call, &mode.CallCLIInvokerOption{ + Headers: cfg.Config.Request.Header, + FilePath: cfg.file, + }) if err != nil { return err } @@ -207,7 +210,10 @@ func newCLICommand(flags *flags, ui cui.UI) *cobra.Command { } call = args[0] } - invoker, err := mode.NewCallCLIInvoker(ui, call, cfg.file, cfg.Config.Request.Header, false, "") + invoker, err := mode.NewCallCLIInvoker(ui, call, &mode.CallCLIInvokerOption{ + Headers: cfg.Config.Request.Header, + FilePath: cfg.file, + }) if err != nil { return err } diff --git a/e2e/cli_test.go b/e2e/cli_test.go index 2d18d79b..236ae04d 100644 --- a/e2e/cli_test.go +++ b/e2e/cli_test.go @@ -161,39 +161,39 @@ func TestE2E_CLI(t *testing.T) { cmd: "call", args: "--file testdata/unary_call.in Unary", deprecatedUsage: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC with an input file": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call fully-qualified unary RPC with an input file": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC with --call flag (backward-compatibility)": { commonFlags: "--package api --service Example --proto testdata/test.proto", cmd: "", args: "--file testdata/unary_call.in --call Unary", deprecatedUsage: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC without package name because the size of packages is 1 (backward-compatibility)": { commonFlags: "--service Example --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in Unary", deprecatedUsage: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC without package and service name because the size of packages and services are 1": { commonFlags: "--proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in Unary", - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC with an input reader": { commonFlags: "--proto testdata/test.proto", @@ -206,7 +206,13 @@ func TestE2E_CLI(t *testing.T) { mode.DefaultCLIReader = old } }, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, + }, + "call unary RPC with --emit-defaults": { + commonFlags: "--proto testdata/test.proto", + cmd: "call", + args: "--emit-defaults --file testdata/unary_empty_call.in api.Example.Unary", + expectedOut: `{ "message": "" }`, }, "call client streaming RPC": { commonFlags: "--proto testdata/test.proto", @@ -271,7 +277,7 @@ func TestE2E_CLI(t *testing.T) { commonFlags: "--header grpc-timeout=1s --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "client streaming call timed out": { commonFlags: "--header grpc-timeout=0s --proto testdata/test.proto", @@ -344,14 +350,14 @@ func TestE2E_CLI(t *testing.T) { args: "--file testdata/unary_call.in Unary", reflection: true, deprecatedUsage: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC with reflection with an input file": { commonFlags: "--reflection", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", reflection: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, // call command with TLS @@ -389,7 +395,7 @@ func TestE2E_CLI(t *testing.T) { cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "cannot launch with TLS and reflection because server didn't enable TLS": { commonFlags: "--tls -r --host localhost --cacert testdata/rootCA.pem", @@ -405,14 +411,14 @@ func TestE2E_CLI(t *testing.T) { args: "--file testdata/unary_call.in api.Example.Unary", tls: true, reflection: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC with TLS and --servername": { commonFlags: "--tls --servername localhost --cacert testdata/rootCA.pem --proto testdata/test.proto", cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "cannot launch with mutual TLS auth because --certkey is missing": { commonFlags: "--tls --host localhost --cacert testdata/rootCA.pem --cert testdata/localhost.pem --proto testdata/test.proto", @@ -433,7 +439,7 @@ func TestE2E_CLI(t *testing.T) { cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", tls: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, // call command with gRPC-Web @@ -450,7 +456,7 @@ func TestE2E_CLI(t *testing.T) { cmd: "call", args: "--file testdata/unary_call.in api.Example.Unary", web: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC with an input file and reflection against to gRPC-Web server": { commonFlags: "--web -r", @@ -458,7 +464,7 @@ func TestE2E_CLI(t *testing.T) { args: "--file testdata/unary_call.in api.Example.Unary", web: true, reflection: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call client streaming RPC against to gRPC-Web server": { commonFlags: "--web --proto testdata/test.proto", diff --git a/e2e/old_cli_test.go b/e2e/old_cli_test.go index 48369b8f..13445a25 100644 --- a/e2e/old_cli_test.go +++ b/e2e/old_cli_test.go @@ -116,15 +116,15 @@ func TestE2E_OldCLI(t *testing.T) { }, "call unary RPC with an input file by CLI mode": { args: "--package api --service Example --call Unary --file testdata/unary_call.in testdata/test.proto", - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC without package name because the size of packages is 1": { args: "--service Example --call Unary --file testdata/unary_call.in testdata/test.proto", - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC without package and service name because the size of packages and services are 1": { args: "--call Unary --file testdata/unary_call.in testdata/test.proto", - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC with an input reader by CLI mode": { args: "--package api --service Example --call Unary testdata/test.proto", @@ -135,7 +135,7 @@ func TestE2E_OldCLI(t *testing.T) { mode.DefaultCLIReader = old } }, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call client streaming RPC by CLI mode": { args: "--package api --service Example --call ClientStreaming --file testdata/client_streaming.in testdata/test.proto", @@ -195,7 +195,7 @@ func TestE2E_OldCLI(t *testing.T) { "call unary RPC by CLI mode with reflection with an input file": { args: "--reflection --package api --service Example --call Unary --file testdata/unary_call.in", reflection: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, // CLI mode with TLS @@ -223,7 +223,7 @@ func TestE2E_OldCLI(t *testing.T) { "call unary RPC with TLS by CLI mode": { args: "--tls --host localhost --cacert testdata/rootCA.pem --service Example --call Unary --file testdata/unary_call.in testdata/test.proto", tls: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "cannot launch CLI mode with TLS and reflection by CLI mode because server didn't enable TLS": { args: "--tls -r --host localhost --cacert testdata/rootCA.pem --service Example --call Unary --file testdata/unary_call.in", @@ -235,12 +235,12 @@ func TestE2E_OldCLI(t *testing.T) { args: "--tls -r --host localhost --cacert testdata/rootCA.pem --service Example --call Unary --file testdata/unary_call.in", tls: true, reflection: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC with TLS and --servername by CLI mode": { args: "--tls --servername localhost --cacert testdata/rootCA.pem --service Example --call Unary --file testdata/unary_call.in testdata/test.proto", tls: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "cannot launch CLI mode with mutual TLS auth because --certkey is missing": { args: "--tls --host localhost --cacert testdata/rootCA.pem --cert testdata/localhost.pem --service Example --call Unary --file testdata/unary_call.in testdata/test.proto", @@ -255,7 +255,7 @@ func TestE2E_OldCLI(t *testing.T) { "call unary RPC with mutual TLS auth by CLI mode": { args: "--tls --host localhost --cacert testdata/rootCA.pem --cert testdata/localhost.pem --certkey testdata/localhost-key.pem --service Example --call Unary --file testdata/unary_call.in testdata/test.proto", tls: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, // CLI mode with gRPC-Web @@ -268,13 +268,13 @@ func TestE2E_OldCLI(t *testing.T) { "call unary RPC with an input file by CLI mode against to gRPC-Web server": { args: "--web --package api --service Example --call Unary --file testdata/unary_call.in testdata/test.proto testdata/test.proto", web: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call unary RPC with an input file by CLI mode and reflection against to gRPC-Web server": { args: "--web -r --service Example --call Unary --file testdata/unary_call.in", web: true, reflection: true, - expectedOut: `{ "message": "hello, oumae" }`, + expectedOut: `{ "message": "oumae" }`, }, "call client streaming RPC by CLI mode against to gRPC-Web server": { args: "--web --service Example --call ClientStreaming --file testdata/client_streaming.in testdata/test.proto", diff --git a/e2e/repl_test.go b/e2e/repl_test.go index 1599588f..e53bf121 100644 --- a/e2e/repl_test.go +++ b/e2e/repl_test.go @@ -84,6 +84,10 @@ func TestE2E_REPL(t *testing.T) { commonFlags: "--service Example --proto testdata/test.proto", input: []interface{}{"call Unary", "kaguya"}, }, + "call Unary with --emit-defaults": { + commonFlags: "--proto testdata/test.proto", + input: []interface{}{"package api", "service Example", "call --emit-defaults Unary", ""}, + }, "call ClientStreaming": { commonFlags: "--proto testdata/test.proto", // io.EOF means end of inputting. diff --git a/e2e/testdata/fixtures/teste2e_cli-print_call_command_usage.golden b/e2e/testdata/fixtures/teste2e_cli-print_call_command_usage.golden index 21a6351b..2a897ba2 100644 --- a/e2e/testdata/fixtures/teste2e_cli-print_call_command_usage.golden +++ b/e2e/testdata/fixtures/teste2e_cli-print_call_command_usage.golden @@ -12,6 +12,7 @@ Examples: Options: --enrich enrich response output includes header, message, trailer and status (default "false") + --emit-defaults render fields with default values (default "false") --output, -o string output format. one of "json" or "curl". "curl" is a curl-like format. (default "curl") --file, -f string a script file that will be executed by (used only CLI mode) --help, -h display help text and exit (default "false") diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_selecting_only_service.golden b/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_selecting_only_service.golden index ac30482c..92088c5a 100644 --- a/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_selecting_only_service.golden +++ b/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_selecting_only_service.golden @@ -1,5 +1,5 @@ { - "message": "hello, kaguya" + "message": "kaguya" } diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_selecting_package_and_service.golden b/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_selecting_package_and_service.golden index 8dbdc72b..6921a502 100644 --- a/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_selecting_package_and_service.golden +++ b/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_selecting_package_and_service.golden @@ -1,6 +1,6 @@ { - "message": "hello, kaguya" + "message": "kaguya" } diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_specifying_--service.golden b/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_specifying_--service.golden index 3cbedde6..9e44568e 100644 --- a/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_specifying_--service.golden +++ b/e2e/testdata/fixtures/teste2e_oldrepl-call_unary_by_specifying_--service.golden @@ -1,4 +1,4 @@ { - "message": "hello, kaguya" + "message": "kaguya" } diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-call_unarymessage.golden b/e2e/testdata/fixtures/teste2e_oldrepl-call_unarymessage.golden index c735239d..4dab53fa 100644 --- a/e2e/testdata/fixtures/teste2e_oldrepl-call_unarymessage.golden +++ b/e2e/testdata/fixtures/teste2e_oldrepl-call_unarymessage.golden @@ -1,4 +1,4 @@ { - "message": "hello, kaguya shinomiya" + "message": "kaguya shinomiya" } diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-call_unaryoneof.golden b/e2e/testdata/fixtures/teste2e_oldrepl-call_unaryoneof.golden index 01f8951b..93e97fab 100644 --- a/e2e/testdata/fixtures/teste2e_oldrepl-call_unaryoneof.golden +++ b/e2e/testdata/fixtures/teste2e_oldrepl-call_unaryoneof.golden @@ -1,4 +1,4 @@ { - "message": "hello, ai hayasaka" + "message": "ai hayasaka" } diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-call_unaryrepeated.golden b/e2e/testdata/fixtures/teste2e_oldrepl-call_unaryrepeated.golden index 000101d1..86c7e653 100644 --- a/e2e/testdata/fixtures/teste2e_oldrepl-call_unaryrepeated.golden +++ b/e2e/testdata/fixtures/teste2e_oldrepl-call_unaryrepeated.golden @@ -1,4 +1,4 @@ { - "message": "hello, miyuki, kaguya, chika, yu" + "message": "miyuki, kaguya, chika, yu" } diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_fields_if_there_are_no_message_type_fields.golden b/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_fields_if_there_are_no_message_type_fields.golden index 5915f4e2..311847da 100644 --- a/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_fields_if_there_are_no_message_type_fields.golden +++ b/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_fields_if_there_are_no_message_type_fields.golden @@ -1,4 +1,2 @@ -{ - "message": "hello, " -} +{} diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_the_current_message.golden b/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_the_current_message.golden index c7406f6a..b9ca0738 100644 --- a/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_the_current_message.golden +++ b/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_the_current_message.golden @@ -1,4 +1,4 @@ { - "message": "hello, mumei " + "message": "mumei " } diff --git a/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_the_current_message_and_exits_the_repeated_field.golden b/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_the_current_message_and_exits_the_repeated_field.golden index e2ca8fa4..fc234461 100644 --- a/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_the_current_message_and_exits_the_repeated_field.golden +++ b/e2e/testdata/fixtures/teste2e_oldrepl-ctrl-c_skips_the_rest_of_the_current_message_and_exits_the_repeated_field.golden @@ -1,4 +1,4 @@ { - "message": "hello, kanade hisaishi, kumiko " + "message": "kanade hisaishi, kumiko " } diff --git a/e2e/testdata/fixtures/teste2e_repl-call_--help.golden b/e2e/testdata/fixtures/teste2e_repl-call_--help.golden index 3de07264..1486230b 100644 --- a/e2e/testdata/fixtures/teste2e_repl-call_--help.golden +++ b/e2e/testdata/fixtures/teste2e_repl-call_--help.golden @@ -3,5 +3,6 @@ usage: call Options: --bytes-from-file interpret TYPE_BYTES input as a relative path to a file --dig-manually prompt asks whether to dig down if it encountered to a message field + --emit-defaults render fields with default values --enrich enrich response output includes header, message, trailer and status diff --git a/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_only_service.golden b/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_only_service.golden index ac30482c..92088c5a 100644 --- a/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_only_service.golden +++ b/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_only_service.golden @@ -1,5 +1,5 @@ { - "message": "hello, kaguya" + "message": "kaguya" } diff --git a/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_package_and_service.golden b/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_package_and_service.golden index 8dbdc72b..6921a502 100644 --- a/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_package_and_service.golden +++ b/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_package_and_service.golden @@ -1,6 +1,6 @@ { - "message": "hello, kaguya" + "message": "kaguya" } diff --git a/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_package_and_service_with_enriched_output.golden b/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_package_and_service_with_enriched_output.golden index e5a671b3..e71deeba 100644 --- a/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_package_and_service_with_enriched_output.golden +++ b/e2e/testdata/fixtures/teste2e_repl-call_unary_by_selecting_package_and_service_with_enriched_output.golden @@ -5,7 +5,7 @@ header_key1: header_val1 header_key2: header_val2 { - "message": "hello, kaguya" + "message": "kaguya" } trailer_key1: trailer_val1 diff --git a/e2e/testdata/fixtures/teste2e_repl-call_unary_by_specifying_--service.golden b/e2e/testdata/fixtures/teste2e_repl-call_unary_by_specifying_--service.golden index 3cbedde6..9e44568e 100644 --- a/e2e/testdata/fixtures/teste2e_repl-call_unary_by_specifying_--service.golden +++ b/e2e/testdata/fixtures/teste2e_repl-call_unary_by_specifying_--service.golden @@ -1,4 +1,4 @@ { - "message": "hello, kaguya" + "message": "kaguya" } diff --git a/e2e/testdata/fixtures/teste2e_repl-call_unary_with_--emit-defaults.golden b/e2e/testdata/fixtures/teste2e_repl-call_unary_with_--emit-defaults.golden new file mode 100644 index 00000000..83f34f7c --- /dev/null +++ b/e2e/testdata/fixtures/teste2e_repl-call_unary_with_--emit-defaults.golden @@ -0,0 +1,6 @@ + + +{ + "message": "" +} + diff --git a/e2e/testdata/fixtures/teste2e_repl-call_unarymessage.golden b/e2e/testdata/fixtures/teste2e_repl-call_unarymessage.golden index c735239d..4dab53fa 100644 --- a/e2e/testdata/fixtures/teste2e_repl-call_unarymessage.golden +++ b/e2e/testdata/fixtures/teste2e_repl-call_unarymessage.golden @@ -1,4 +1,4 @@ { - "message": "hello, kaguya shinomiya" + "message": "kaguya shinomiya" } diff --git a/e2e/testdata/fixtures/teste2e_repl-call_unaryoneof.golden b/e2e/testdata/fixtures/teste2e_repl-call_unaryoneof.golden index 01f8951b..93e97fab 100644 --- a/e2e/testdata/fixtures/teste2e_repl-call_unaryoneof.golden +++ b/e2e/testdata/fixtures/teste2e_repl-call_unaryoneof.golden @@ -1,4 +1,4 @@ { - "message": "hello, ai hayasaka" + "message": "ai hayasaka" } diff --git a/e2e/testdata/fixtures/teste2e_repl-call_unaryrepeated.golden b/e2e/testdata/fixtures/teste2e_repl-call_unaryrepeated.golden index 000101d1..86c7e653 100644 --- a/e2e/testdata/fixtures/teste2e_repl-call_unaryrepeated.golden +++ b/e2e/testdata/fixtures/teste2e_repl-call_unaryrepeated.golden @@ -1,4 +1,4 @@ { - "message": "hello, miyuki, kaguya, chika, yu" + "message": "miyuki, kaguya, chika, yu" } diff --git a/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_fields_if_there_are_no_message_type_fields.golden b/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_fields_if_there_are_no_message_type_fields.golden index 5915f4e2..311847da 100644 --- a/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_fields_if_there_are_no_message_type_fields.golden +++ b/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_fields_if_there_are_no_message_type_fields.golden @@ -1,4 +1,2 @@ -{ - "message": "hello, " -} +{} diff --git a/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_the_current_message.golden b/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_the_current_message.golden index c7406f6a..b9ca0738 100644 --- a/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_the_current_message.golden +++ b/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_the_current_message.golden @@ -1,4 +1,4 @@ { - "message": "hello, mumei " + "message": "mumei " } diff --git a/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_the_current_message_and_exits_the_repeated_field.golden b/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_the_current_message_and_exits_the_repeated_field.golden index e2ca8fa4..fc234461 100644 --- a/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_the_current_message_and_exits_the_repeated_field.golden +++ b/e2e/testdata/fixtures/teste2e_repl-ctrl-c_skips_the_rest_of_the_current_message_and_exits_the_repeated_field.golden @@ -1,4 +1,4 @@ { - "message": "hello, kanade hisaishi, kumiko " + "message": "kanade hisaishi, kumiko " } diff --git a/e2e/testdata/unary_empty_call.in b/e2e/testdata/unary_empty_call.in new file mode 100644 index 00000000..0967ef42 --- /dev/null +++ b/e2e/testdata/unary_empty_call.in @@ -0,0 +1 @@ +{} diff --git a/format/curl/curl.go b/format/curl/curl.go index 9987de15..ca70b3aa 100644 --- a/format/curl/curl.go +++ b/format/curl/curl.go @@ -31,11 +31,13 @@ type responseFormatter struct { wroteHeader, wroteMessage, wroteTrailer bool } -func NewResponseFormatter(w io.Writer) format.ResponseFormatterInterface { +func NewResponseFormatter(w io.Writer, emitDefaults bool) format.ResponseFormatterInterface { return &responseFormatter{ - w: w, - json: json.NewPresenter(" "), - pbMarshaler: &jsonpb.Marshaler{}, + w: w, + json: json.NewPresenter(" "), + pbMarshaler: &jsonpb.Marshaler{ + EmitDefaults: emitDefaults, + }, } } @@ -58,7 +60,13 @@ func (p *responseFormatter) FormatMessage(v interface{}) error { if p.wroteHeader { fmt.Fprintf(p.w, "\n") } - msg, err := p.json.Format(v) + + m, err := p.convertProtoMessageToMap(v.(proto.Message)) + if err != nil { + return err + } + + msg, err := p.json.Format(m) if err != nil { return err } diff --git a/format/json/json.go b/format/json/json.go index cd60d88c..0b81fe46 100644 --- a/format/json/json.go +++ b/format/json/json.go @@ -36,8 +36,10 @@ type responseFormatter struct { pbMarshaler *jsonpb.Marshaler } -func NewResponseFormatter(w io.Writer) format.ResponseFormatterInterface { - return &responseFormatter{w: w, p: json.NewPresenter(" "), pbMarshaler: &jsonpb.Marshaler{}} +func NewResponseFormatter(w io.Writer, emitDefaults bool) format.ResponseFormatterInterface { + return &responseFormatter{w: w, p: json.NewPresenter(" "), pbMarshaler: &jsonpb.Marshaler{ + EmitDefaults: emitDefaults, + }} } func (p *responseFormatter) FormatHeader(header metadata.MD) { diff --git a/go.mod b/go.mod index 79bf2dc3..1fe126af 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( github.com/ktr0731/go-prompt v0.2.2-0.20190609072126-7894cc3f2925 github.com/ktr0731/go-shellstring v0.1.3 github.com/ktr0731/go-updater v0.1.5 - github.com/ktr0731/grpc-test v0.1.7 + github.com/ktr0731/grpc-test v0.1.8 github.com/ktr0731/grpc-web-go-client v0.2.7 github.com/lunixbochs/vtclean v1.0.0 // indirect github.com/manifoldco/promptui v0.8.0 diff --git a/go.sum b/go.sum index ff584765..7d2c177e 100644 --- a/go.sum +++ b/go.sum @@ -248,6 +248,7 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.0.14/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= @@ -334,7 +335,6 @@ github.com/gofrs/flock v0.8.0/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= -github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= @@ -367,6 +367,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= @@ -630,8 +631,8 @@ github.com/ktr0731/go-shellstring v0.1.3/go.mod h1:f0/XX0hsm6A02Es/UDQ9MGd3VqpCH github.com/ktr0731/go-updater v0.1.5 h1:AiaQxJoI0OTGqvsJYdIITCOaEzQQOqU2MHKUwttVShY= github.com/ktr0731/go-updater v0.1.5/go.mod h1:dsdOg7a9sj6ttcOU5ZxPCtKdm9WeB1hwcX/7oKgz9/0= github.com/ktr0731/grpc-test v0.1.4/go.mod h1:v47616grayBYXQveGWxO3OwjLB3nEEnHsZuMTc73FM0= -github.com/ktr0731/grpc-test v0.1.7 h1:QwjxlKMHb1FzQMoXjjK1F2HDdIf5f72WXWZs4He8nNI= -github.com/ktr0731/grpc-test v0.1.7/go.mod h1:vjMPOfGerucM+ysAKiynHDS6IUSErB6O9gBOHWyUs5s= +github.com/ktr0731/grpc-test v0.1.8 h1:ZGX8bQu9aIlJFJ4NBUBvcyyzyFa2xH5STfmZZWoP4tc= +github.com/ktr0731/grpc-test v0.1.8/go.mod h1:NfrFy0MifkU4tftOx0iZirhLHjz8+dAOKJQ5XRiYjDg= github.com/ktr0731/grpc-web-go-client v0.2.7 h1:Op1U1XC29kb7S8XptqYd742JwcG7oR5Q7QoYPZMN2T4= github.com/ktr0731/grpc-web-go-client v0.2.7/go.mod h1:1Iac8gFJvC/DRfZoGnFZsfEbEq/wQFK+2Ve1o3pHkCQ= github.com/ktr0731/modfile v1.11.2/go.mod h1:LzNwnHJWHbuDh3BO17lIqzqDldXqGu1HCydWH3SinE0= @@ -1433,8 +1434,10 @@ google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.37.0 h1:uSZWeQJX5j11bIQ4AJoj+McDBo29cY1MCoC1wO3ts+c= google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/mode/cli.go b/mode/cli.go index 9a74e423..1a62d381 100644 --- a/mode/cli.go +++ b/mode/cli.go @@ -29,16 +29,23 @@ var DefaultCLIReader io.Reader = os.Stdin // CLIInvoker represents an invokable function for CLI mode. type CLIInvoker func(context.Context) error +type CallCLIInvokerOption struct { + Headers config.Header + Enrich bool + EmitDefaults bool + FilePath string // If empty, the invoker tries to read input from stdin. + FormatType string +} + // NewCallCLIInvoker returns an CLIInvoker implementation for calling RPCs. -// If filePath is empty, the invoker tries to read input from stdin. -func NewCallCLIInvoker(ui cui.UI, methodName, filePath string, headers config.Header, enrich bool, formatType string) (CLIInvoker, error) { +func NewCallCLIInvoker(ui cui.UI, methodName string, opt *CallCLIInvokerOption) (CLIInvoker, error) { if methodName == "" { return nil, errors.New("method is required") } return func(ctx context.Context) error { in := DefaultCLIReader - if filePath != "" { - f, err := os.Open(filePath) + if opt.FilePath != "" { + f, err := os.Open(opt.FilePath) if err != nil { return errors.Wrap(err, "failed to open the script file") } @@ -47,20 +54,20 @@ func NewCallCLIInvoker(ui cui.UI, methodName, filePath string, headers config.He } filler := fill.NewSilentFiller(in) var rfi format.ResponseFormatterInterface - switch formatType { + switch opt.FormatType { case "curl": - rfi = curl.NewResponseFormatter(ui.Writer()) + rfi = curl.NewResponseFormatter(ui.Writer(), opt.EmitDefaults) case "json": - rfi = fmtjson.NewResponseFormatter(ui.Writer()) + rfi = fmtjson.NewResponseFormatter(ui.Writer(), opt.EmitDefaults) default: - rfi = curl.NewResponseFormatter(ui.Writer()) + rfi = curl.NewResponseFormatter(ui.Writer(), opt.EmitDefaults) } usecase.InjectPartially(usecase.Dependencies{ - ResponseFormatter: format.NewResponseFormatter(rfi, enrich), + ResponseFormatter: format.NewResponseFormatter(rfi, opt.Enrich), Filler: filler, }) - for k, v := range headers { + for k, v := range opt.Headers { for _, vv := range v { usecase.AddHeader(k, vv) } diff --git a/repl/commands.go b/repl/commands.go index 1ab1f341..1fa773c0 100644 --- a/repl/commands.go +++ b/repl/commands.go @@ -155,7 +155,7 @@ func (c *showCommand) Run(w io.Writer, args []string) error { } type callCommand struct { - enrich, digManually, bytesFromFile bool + enrich, digManually, bytesFromFile, emitDefaults bool } func (c *callCommand) FlagSet() (*pflag.FlagSet, bool) { @@ -164,6 +164,7 @@ func (c *callCommand) FlagSet() (*pflag.FlagSet, bool) { fs.BoolVar(&c.enrich, "enrich", false, "enrich response output includes header, message, trailer and status") fs.BoolVar(&c.digManually, "dig-manually", false, "prompt asks whether to dig down if it encountered to a message field") fs.BoolVar(&c.bytesFromFile, "bytes-from-file", false, "interpret TYPE_BYTES input as a relative path to a file") + fs.BoolVar(&c.emitDefaults, "emit-defaults", false, "render fields with default values") return fs, true } @@ -192,7 +193,7 @@ func (c *callCommand) Validate(args []string) error { func (c *callCommand) Run(w io.Writer, args []string) error { usecase.InjectPartially( usecase.Dependencies{ - ResponseFormatter: format.NewResponseFormatter(curl.NewResponseFormatter(w), c.enrich), + ResponseFormatter: format.NewResponseFormatter(curl.NewResponseFormatter(w, c.emitDefaults), c.enrich), }, )