2

I would like to validate enum values in generated Java code. I have following proto file:

syntax = "proto3";

import "google/protobuf/empty.proto";

option java_multiple_files = true;
option java_package = "com.package";

package helloworld;

service SomeService {
    rpc DoAction (Request) returns (google.protobuf.Empty) { }
}

enum Currency {
    EUR = 0;
    GBP = 1;
    USD = 2;
}

message Request {
    string id = 1;
    Currency currency = 2;
}

I'd like to get some validation for Currency enum in code which is generates by protobuf and drop error message in case value is not applicable to the provided enum (ex: invalid currency). Is it possible?

1 Answers1

2

You can validate it, but you should also be aware of the nuance of Protobuf Enumerations.

  1. Always include an UNKNOWN = 0; enum value for every enum. When Protobuf doesn't know what an enum value corresponds to, it sets it to the default value. This lets you detect when a newer client with new codes uses a value the server doesn't understand. Also, if the field is unset, it naturally is equal to UNKNOWN which let's you check if the field is absent. (If you do want to allow absent enums, wrap them in a message{}).

  2. In your server application handler, you should check to see which one of the supported values the client provided. In the case that it isn't one of them, you should fail the RPC with an INVALID_ARGUMENT status code:

    out: {
      switch (req.getCurrency()) {
        case EUR: 
        case GBP:
        case USD: break out;
        case UNKNOWN:
          responseObserver.onError(
              Status.INVALID_ARGUMENT
                  .withDescription("bad currency " + req.getCurrency())
                  .asRuntimeException());
          return;
      }
      throw new AssertionError("missed case!");
    }
    // keep handling the request

This code checks that the code is one of the supported ones. If an unsupported code comes in, it will be handled by the UNKNOWN case, and return an error early. In case you modify your proto and more cases are added, static analysis will catch the missing case (or throw an AssertionError). Note that this won't happen if the client updates their proto. Avoid using the default case, because it can be easily missed when modifying proto. Alternatively, you could put all the supported cases in a map and check if it is present.

Carl Mastrangelo
  • 5,970
  • 1
  • 28
  • 37