Hello,
Two of our teams independently defined several RPC calls. Both wanted to cover this usecase:
We have situation when command did not do what the caller wanted. For example (it is just an example, we do not have such code):
service Users {
rpc Login(loginRequest) returns (loginResponse) {
}
}
message loginRequest {
string username = 1;
string password = 2;
string super_secret1 = 3;
string super_secret2 = 4;
}
message loginResponse {
...
}
They want to handle some application-level errors. Each error itself brings more than just Error Code + String. For example, it brings fields that must be filled in to go on. Teams converged to this loginResponse
message loginResponse {
message Success {
string token = 1;
int some_param = 2;
}
message Secret1Needed {
params of this requirement
}
message Secret2Needed {
params of this requirement
}
oneof result {
Secret1Needed error1 = 1;
Secret2Needed error2 = 2;
Success success = 3;
}
}
Only token returned is considered as good result (because it actually performed Login). In the opposite case, there might be more than one reason to fail. The context of failure (beyond the verbosity of Status Code + Message) is transferred in specialized messages (here Secret1Needed and Secret2Needed).
In the code, they just handle it as a switch:
switch (loginResponse.result_case()) {
case ERROR1: %handler ...
case ERROR2: %handler ...
case SUCCESS: %handler ...
}
I do not like that. Even worse, to allow for changes, every CommandResponse message now contains oneof(ERROR1, 2, 3..., SUCCESS)
If google wanted to use GRPC like this, they would have defined ERROR and SUCCESS messages instead of commandResponse. However, both teams ended up with this solution. Is there something I am missing? How to handle application-level errors with more context? And how to handle typed contexts (we wanted them to be protobuf messages as well).
Thank you