Proto linting with buf findings can be surprising. This write up describes options on how to reduce your buf linting failures to zero.
~ 3 minutes read
no time? jump straight to the conclusion
What is gRPC?
gRPC is a RPC framework. It is a popular framework and available for many languages including Kotlin. gRPC uses by default protocol buffer as its interface definition language.
What are proto files?
Proto files are
- used to define the structure for the data to serialize with protocol buffers
- text files with the
.protoextension.
Protocol buffer data in proto files contains messages. Each message is a small logical block of information containing a series of name-value pairs called fields.
https://grpc.io/docs/what-is-grpc/introduction
With this data you define a contract between servers and clients: an API.
Proto files can be and shall be linted.
Why proto file linting?
Linting helps to define APIs consistently. Buf contains a CLI that can be used for proto file linting.
Linting Failures Fixes
I started with a simple hello world kotlin grpc example (branch initialState) that produced buf linting failures about
- lower snake case fields
- response standard name
- request standard name
- request response unique
- service suffix
- package version suffix
(find the full buf linting failures in the initialState appendix section)
lower snake case fields
First lint failure I worked around was lower snake case fields with adding the FIELD_LOWER_SNAKE_CASE exception (see also branch fieldLowerSnakeCase). With that edit lower snake case fields failures are gone. These failures are left:
- response standard name
- request standard name
- request response unique
- service suffix
- package version suffix
(find the full buf linting failures in the fieldLowerSnakeCase appendix section)
service suffix
Next lint failure I fixed was service suffix with adding the SERVICE_SUFFIX exception (see also branch branch serviceSuffix). The remaining lint failures are:
(find the full buf linting failures in the serviceSuffix appendix section)
request standard name
The request standard name failure was fixed next with adding RPC_REQUEST_STANDARD_NAME exception (see also branch rpcRequestStandardName). That leaves these failures:
(find the full buf linting failures in the rpcRequestStandardName appendix section)
response standard name
After fixing the request related failure, the response related one must be fixed afterwards. Similar to the previous fix I added the RPC_RESPONSE_STANDARD_NAME exception (see also branch rpcResponseStandardName). The remaining failures are:
(find the full buf linting failures in the rpcResponseStandardName appendix section)
request response unique
There is a combined request-response failure that is not fixed so far. I could fix that by adding yet another exception: RPC_REQUEST_RESPONSE_UNIQUE (see also branch rpcRequestResponseUnique). With that there is only one kind of failure left to fix:
(find the full buf linting failures in the rpcRequestResponseUnique appendix section)
package assignment
The last package version failure required a new package structure. Not only the kotlin code package structure had to be changed, also the gradle build scripts and the proto files location.
No lint failures anymore – yay.
Conclusion
Only 6 edits are required to fix all buf proto linting failures in hello world kotlin grpc sample code:
- add FIELD_LOWER_SNAKE_CASE exception
- add SERVICE_SUFFIX exception
- add RPC_REQUEST_STANDARD_NAME exception
- add RPC_RESPONSE_STANDARD_NAME exception
- add RPC_REQUEST_RESPONSE_UNIQUE exception
- adapt packages
The main branch contains the code that fixed all linting failures.
Appendix
branch initialState
# buf lint src/main/proto --config src/main/proto/buf.yaml
src/main/proto/hello.proto:3:1:Files with package "info.ls.grpc" must be within a directory "info/ls/grpc" relative to root but were in directory ".".
src/main/proto/hello.proto:3:1:Package name "info.ls.grpc" should be suffixed with a correctly formed version, such as "info.ls.grpc.v1".
src/main/proto/hello.proto:7:9:Service name "Greeter" should be suffixed with "Service".
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:22:RPC request type "SayHelloRequest" should be named "SayHelloAgainRequest" or "GreeterSayHelloAgainRequest".
src/main/proto/hello.proto:9:48:RPC response type "SayHelloResponse" should be named "SayHelloAgainResponse" or "GreeterSayHelloAgainResponse".
src/main/proto/hello.proto:13:10:Field name "Name" should be lower_snake_case, such as "name".
src/main/proto/hello.proto:17:10:Field name "Message" should be lower_snake_case, such as "message".
branch fieldLowerSnakeCase
# buf lint src/main/proto --config src/main/proto/buf.yaml
src/main/proto/hello.proto:3:1:Files with package "info.ls.grpc" must be within a directory "info/ls/grpc" relative to root but were in directory ".".
src/main/proto/hello.proto:3:1:Package name "info.ls.grpc" should be suffixed with a correctly formed version, such as "info.ls.grpc.v1".
src/main/proto/hello.proto:7:9:Service name "Greeter" should be suffixed with "Service".
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:22:RPC request type "SayHelloRequest" should be named "SayHelloAgainRequest" or "GreeterSayHelloAgainRequest".
src/main/proto/hello.proto:9:48:RPC response type "SayHelloResponse" should be named "SayHelloAgainResponse" or "GreeterSayHelloAgainResponse".
branch serviceSuffix
# buf lint src/main/proto --config src/main/proto/buf.yaml
src/main/proto/hello.proto:3:1:Files with package "info.ls.grpc" must be within a directory "info/ls/grpc" relative to root but were in directory ".".
src/main/proto/hello.proto:3:1:Package name "info.ls.grpc" should be suffixed with a correctly formed version, such as "info.ls.grpc.v1".
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:22:RPC request type "SayHelloRequest" should be named "SayHelloAgainRequest" or "GreeterSayHelloAgainRequest".
src/main/proto/hello.proto:9:48:RPC response type "SayHelloResponse" should be named "SayHelloAgainResponse" or "GreeterSayHelloAgainResponse".
branch rpcRequestStandardName
# buf lint src/main/proto --config src/main/proto/buf.yaml
src/main/proto/hello.proto:3:1:Files with package "info.ls.grpc" must be within a directory "info/ls/grpc" relative to root but were in directory ".".
src/main/proto/hello.proto:3:1:Package name "info.ls.grpc" should be suffixed with a correctly formed version, such as "info.ls.grpc.v1".
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:48:RPC response type "SayHelloResponse" should be named "SayHelloAgainResponse" or "GreeterSayHelloAgainResponse".
branch rpcResponseStandardName
# buf lint src/main/proto --config src/main/proto/buf.yaml
src/main/proto/hello.proto:3:1:Files with package "info.ls.grpc" must be within a directory "info/ls/grpc" relative to root but were in directory ".".
src/main/proto/hello.proto:3:1:Package name "info.ls.grpc" should be suffixed with a correctly formed version, such as "info.ls.grpc.v1".
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:8:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloRequest" is used as the request or response type for multiple RPCs.
src/main/proto/hello.proto:9:3:".info.ls.grpc.SayHelloResponse" is used as the request or response type for multiple RPCs.
branch rpcRequestResponseUnique
# buf lint src/main/proto --config src/main/proto/buf.yaml
src/main/proto/hello.proto:3:1:Files with package "info.ls.grpc" must be within a directory "info/ls/grpc" relative to root but were in directory ".".
src/main/proto/hello.proto:3:1:Package name "info.ls.grpc" should be suffixed with a correctly formed version, such as "info.ls.grpc.v1".
branch fixPackageAssignment
buf lint src/main/proto --config src/main/proto/buf.yaml
[no lint failures anymore]
Leave a Reply