Skip to content

Commit

Permalink
Add details flag to prototool grpc and do some cleanup (uber#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
bufdev authored Mar 6, 2019
1 parent 76b542a commit ed8a7b2
Show file tree
Hide file tree
Showing 10 changed files with 166 additions and 124 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add `--list-lint-group` flag to the `lint` command to list a lint group's
rules.
- Add `--diff-lint-groups` flag to the `lint` command to print the diff
between two lint groups
between two lint groups.
- Breaking change detector added as the `break check` command.
- A Docker image is now provided on Docker Hub as [uber/prototool](https://hub.docker.com/r/uber/prototool)
which provides an environment with commonly-used plugins.
Expand All @@ -43,6 +43,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
- Add file locking around the `protoc` downloader to eliminate concurrency
issues where multiple `prototool` invocations may be accessing the cache
at the same time.
- Add `--details` flag to the `grpc` command to output headers, trailers,
and statuses as well as the responses.
- Unix domain sockets can now be specified for the `--address` flag of the
`grpc` command via the prefix `unix://`.

Expand Down
62 changes: 28 additions & 34 deletions docs/grpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,58 +19,52 @@ Either use `--data 'requestData'` as the the JSON data to input, or `--stdin` wh

```bash
$ make example # make sure everything is built just in case
$ go run example/cmd/excited/main.go # run in another terminal

$ prototool grpc example \
--address 0.0.0.0:8080 \
--method foo.ExcitedService/Exclamation \
--method uber.foo.v1.ExcitedAPI/Exclamation \
--data '{"value":"hello"}'
{
"value": "hello!"
}
{"value": "hello!"}

$ prototool grpc example \
--address 0.0.0.0:8080 \
--method foo.ExcitedService/ExclamationServerStream \
--method uber.foo.v1.ExcitedAPI/ExclamationServerStream \
--data '{"value":"hello"}'
{
"value": "h"
}
{
"value": "e"
}
{
"value": "l"
}
{
"value": "l"
}
{
"value": "o"
}
{
"value": "!"
}
{"value": "h"}
{"value": "e"}
{"value": "l"}
{"value": "l"}
{"value": "o"}
{"value": "!"}

$ cat input.json
{"value":"hello"}
{"value":"salutations"}

$ cat input.json | prototool grpc example \
--address 0.0.0.0:8080 \
--method foo.ExcitedService/ExclamationClientStream \
--method uber.foo.v1.ExcitedAPI/ExclamationClientStream \
--stdin
{
"value": "hellosalutations!"
}
{"value": "hellosalutations!"}

$ cat input.json | prototool grpc example \
--address 0.0.0.0:8080 \
--method foo.ExcitedService/ExclamationBidiStream \
--method uber.foo.v1.ExcitedAPI/ExclamationBidiStream \
--stdin
{
"value": "hello!"
}
{
"value": "salutations!"
}
{"value": "hello!"}
{"value": "salutations!"}

$ prototool grpc example \
--address 0.0.0.0:8080 \
--method uber.foo.v1.ExcitedAPI/ExclamationServerStream \
--data '{"value":"hello"}' \
--details
{"headers":{"content-type":["application/grpc"]}}
{"response":{"value":"h"}}
{"response":{"value":"e"}}
{"response":{"value":"l"}}
{"response":{"value":"l"}}
{"response":{"value":"o"}}
{"response":{"value":"!"}}
```
64 changes: 26 additions & 38 deletions internal/cmd/cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1116,68 +1116,56 @@ func TestGRPC(t *testing.T) {
t.Parallel()
assertGRPC(t,
0,
`
{
"value": "hello!"
}
`,
`{"value":"hello!"}`,
"testdata/grpc/grpc.proto",
"grpc.ExcitedService/Exclamation",
`{"value":"hello"}`,
)
assertGRPC(t,
0,
`
{
"value": "hellosalutations!"
}
`,
`{"value":"hellosalutations!"}`,
"testdata/grpc/grpc.proto",
"grpc.ExcitedService/ExclamationClientStream",
`{"value":"hello"}
{"value":"salutations"}`,
)
assertGRPC(t,
0,
`
{
"value": "h"
}
{
"value": "e"
}
{
"value": "l"
}
{
"value": "l"
}
{
"value": "o"
}
{
"value": "!"
}
`,
`{"value":"h"}
{"value":"e"}
{"value":"l"}
{"value":"l"}
{"value":"o"}
{"value":"!"}`,
"testdata/grpc/grpc.proto",
"grpc.ExcitedService/ExclamationServerStream",
`{"value":"hello"}`,
)
assertGRPC(t,
0,
`
{
"value": "hello!"
}
{
"value": "salutations!"
}
{"value":"hello!"}
{"value":"salutations!"}
`,
"testdata/grpc/grpc.proto",
"grpc.ExcitedService/ExclamationBidiStream",
`{"value":"hello"}
{"value":"salutations"}`,
)
assertGRPC(t,
0,
`{"headers":{"content-type":["application/grpc"]}}
{"response":{"value":"h"}}
{"response":{"value":"e"}}
{"response":{"value":"l"}}
{"response":{"value":"l"}}
{"response":{"value":"o"}}
{"response":{"value":"!"}}`,
"testdata/grpc/grpc.proto",
"grpc.ExcitedService/ExclamationServerStream",
`{"value":"hello"}`,
`--details`,
)
}

func TestVersion(t *testing.T) {
Expand Down Expand Up @@ -1381,10 +1369,10 @@ func assertGoldenFormat(t *testing.T, expectSuccess bool, fix bool, filePath str
assert.Equal(t, strings.TrimSpace(string(golden)), output)
}

func assertGRPC(t *testing.T, expectedExitCode int, expectedLinePrefixes string, filePath string, method string, jsonData string) {
func assertGRPC(t *testing.T, expectedExitCode int, expectedLinePrefixes string, filePath string, method string, jsonData string, extraFlags ...string) {
excitedTestCase := startExcitedTestCase(t)
defer excitedTestCase.Close()
assertDoStdin(t, strings.NewReader(jsonData), true, expectedExitCode, expectedLinePrefixes, "grpc", filePath, "--address", excitedTestCase.Address(), "--method", method, "--stdin")
assertDoStdin(t, strings.NewReader(jsonData), true, expectedExitCode, expectedLinePrefixes, append([]string{"grpc", filePath, "--address", excitedTestCase.Address(), "--method", method, "--stdin"}, extraFlags...)...)
}

func assertRegexp(t *testing.T, extraErrorFormat bool, expectedExitCode int, expectedRegexp string, args ...string) {
Expand Down
5 changes: 5 additions & 0 deletions internal/cmd/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type flags struct {
connectTimeout string
data string
debug bool
details bool
diffLintGroups string
diffMode bool
disableFormat bool
Expand Down Expand Up @@ -95,6 +96,10 @@ func (f *flags) bindDebug(flagSet *pflag.FlagSet) {
flagSet.BoolVar(&f.debug, "debug", false, "Run in debug mode, which will print out debug logging.")
}

func (f *flags) bindDetails(flagSet *pflag.FlagSet) {
flagSet.BoolVar(&f.details, "details", false, "Output headers, trailers, and status as well as the responses.")
}

func (f *flags) bindDiffLintGroups(flagSet *pflag.FlagSet) {
flagSet.StringVar(&f.diffLintGroups, "diff-lint-groups", "", "Diff the two lint groups separated by '.', for example google,uber2.")
}
Expand Down
65 changes: 30 additions & 35 deletions internal/cmd/templates.go
Original file line number Diff line number Diff line change
Expand Up @@ -286,63 +286,57 @@ prototool grpc [dirOrFile] \
Either use "--data 'requestData'" as the the JSON data to input, or "--stdin" which will result in the input being read from stdin as JSON.
$ make example # make sure everything is built just in case
$ go run example/cmd/excited/main.go # run in another terminal
$ prototool grpc example \
--address 0.0.0.0:8080 \
--method foo.ExcitedService/Exclamation \
--method uber.foo.v1.ExcitedAPI/Exclamation \
--data '{"value":"hello"}'
{
"value": "hello!"
}
{"value": "hello!"}
$ prototool grpc example \
--address 0.0.0.0:8080 \
--method foo.ExcitedService/ExclamationServerStream \
--method uber.foo.v1.ExcitedAPI/ExclamationServerStream \
--data '{"value":"hello"}'
{
"value": "h"
}
{
"value": "e"
}
{
"value": "l"
}
{
"value": "l"
}
{
"value": "o"
}
{
"value": "!"
}
{"value": "h"}
{"value": "e"}
{"value": "l"}
{"value": "l"}
{"value": "o"}
{"value": "!"}
$ cat input.json
{"value":"hello"}
{"value":"salutations"}
$ cat input.json | prototool grpc example \
--address 0.0.0.0:8080 \
--method foo.ExcitedService/ExclamationClientStream \
--method uber.foo.v1.ExcitedAPI/ExclamationClientStream \
--stdin
{
"value": "hellosalutations!"
}
{"value": "hellosalutations!"}
$ cat input.json | prototool grpc example \
--address 0.0.0.0:8080 \
--method foo.ExcitedService/ExclamationBidiStream \
--method uber.foo.v1.ExcitedAPI/ExclamationBidiStream \
--stdin
{
"value": "hello!"
}
{
"value": "salutations!"
}`,
{"value": "hello!"}
{"value": "salutations!"}
$ prototool grpc example \
--address 0.0.0.0:8080 \
--method uber.foo.v1.ExcitedAPI/ExclamationServerStream \
--data '{"value":"hello"}' \
--details
{"headers":{"content-type":["application/grpc"]}}
{"response":{"value":"h"}}
{"response":{"value":"e"}}
{"response":{"value":"l"}}
{"response":{"value":"l"}}
{"response":{"value":"o"}}
{"response":{"value":"!"}}`,
Args: cobra.MaximumNArgs(1),
Run: func(runner exec.Runner, args []string, flags *flags) error {
return runner.GRPC(args, flags.headers, flags.address, flags.method, flags.data, flags.callTimeout, flags.connectTimeout, flags.keepaliveTime, flags.stdin)
return runner.GRPC(args, flags.headers, flags.address, flags.method, flags.data, flags.callTimeout, flags.connectTimeout, flags.keepaliveTime, flags.stdin, flags.details)
},
BindFlags: func(flagSet *pflag.FlagSet, flags *flags) {
flags.bindCachePath(flagSet)
Expand All @@ -351,6 +345,7 @@ $ cat input.json | prototool grpc example \
flags.bindCallTimeout(flagSet)
flags.bindConnectTimeout(flagSet)
flags.bindData(flagSet)
flags.bindDetails(flagSet)
flags.bindErrorFormat(flagSet)
flags.bindHeaders(flagSet)
flags.bindKeepaliveTime(flagSet)
Expand Down
2 changes: 1 addition & 1 deletion internal/exec/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ type Runner interface {
Lint(args []string, listAllLinters bool, listLinters bool, listAllLintGroups bool, listLintGroup string, diffLintGroups string) error
Format(args []string, overwrite, diffMode, lintMode, fix bool) error
All(args []string, disableFormat, disableLint, fix bool) error
GRPC(args, headers []string, address, method, data, callTimeout, connectTimeout, keepaliveTime string, stdin bool) error
GRPC(args, headers []string, address, method, data, callTimeout, connectTimeout, keepaliveTime string, stdin bool, details bool) error
InspectPackages(args []string) error
InspectPackageDeps(args []string, name string) error
InspectPackageImporters(args []string, name string) error
Expand Down
7 changes: 6 additions & 1 deletion internal/exec/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,7 @@ func (r *runner) All(args []string, disableFormat, disableLint, fixFlag bool) er
return nil
}

func (r *runner) GRPC(args, headers []string, address, method, data, callTimeout, connectTimeout, keepaliveTime string, stdin bool) error {
func (r *runner) GRPC(args, headers []string, address, method, data, callTimeout, connectTimeout, keepaliveTime string, stdin bool, details bool) error {
if address == "" {
return newExitErrorf(255, "must set address")
}
Expand Down Expand Up @@ -561,6 +561,7 @@ func (r *runner) GRPC(args, headers []string, address, method, data, callTimeout
parsedCallTimeout,
parsedConnectTimeout,
parsedKeepaliveTime,
details,
).Invoke(fileDescriptorSets.Unwrap(), address, method, reader, r.output)
}

Expand Down Expand Up @@ -843,6 +844,7 @@ func (r *runner) newGRPCHandler(
callTimeout time.Duration,
connectTimeout time.Duration,
keepaliveTime time.Duration,
details bool,
) grpc.Handler {
handlerOptions := []grpc.HandlerOption{
grpc.HandlerWithLogger(r.logger),
Expand All @@ -859,6 +861,9 @@ func (r *runner) newGRPCHandler(
if keepaliveTime != 0 {
handlerOptions = append(handlerOptions, grpc.HandlerWithKeepaliveTime(keepaliveTime))
}
if details {
handlerOptions = append(handlerOptions, grpc.HandlerWithDetails())
}
return grpc.NewHandler(handlerOptions...)
}

Expand Down
10 changes: 10 additions & 0 deletions internal/grpc/grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,16 @@ func HandlerWithLogger(logger *zap.Logger) HandlerOption {
}
}

// HandlerWithDetails returns a HandlerOption that outputs responses
// in a structured JSON message that includes headers, trailers, and statuses.
//
// The default is to just print the responses.
func HandlerWithDetails() HandlerOption {
return func(handler *handler) {
handler.details = true
}
}

// HandlerWithCallTimeout returns a HandlerOption that has the given call timeout.
//
// Each invocation must be completed within this time.
Expand Down
Loading

0 comments on commit ed8a7b2

Please sign in to comment.