Description
🐛 Bug Report
The WithForwardResponseOption does not allow you to cast to the original response proto when the service has a response_body
field set. This makes it very difficult to conditionally attach headers, cookies, or do other conditional logic based off of the response proto.
This is a follow up bug report from a chat with @johanbrandhorst in slack.
To Reproduce
- Create a grpc response proto with
response_body
set.
import "protoc-gen-swagger/options/annotations.proto";
import "google/api/annotations.proto";
service Service {
rpc GetObject (GetObjectRequest) returns (GetObjectResponse) {
option (google.api.http) = {
get: "/object"
response_body: "value"
};
}
}
message GetObjectRequest {
}
message GetObjectResponse {
string value = 1;
}
- Register a WithForwardResponseOption function with test:
type fake struct {}
func (f fake) GetObject(ctx context.Context, request *services.GetObjectRequest) (*services.GetObjectResponse, error) {
return &services.GetObjectResponse{Value: "this is the value"}, nil
}
func TestGetValue(t *testing.T) {
type GetValuer interface {
GetValue() string
}
mux := runtime.NewServeMux(runtime.WithForwardResponseOption(func(ctx context.Context, w http.ResponseWriter, m proto.Message) error {
switch m.(type) {
case *services.GetObjectResponse:
t.Logf("Cast to struct ptr")
case GetValuer:
t.Logf("Cast to interface")
default:
t.Errorf("Couldn't cast response to expected format.")
}
return nil
}))
services.RegisterServiceHandlerServer(context.Background(), mux, &fake{})
req := httptest.NewRequest("GET","http://localhost/object", nil)
resp := httptest.NewRecorder()
mux.ServeHTTP(resp, req)
}
Expected behavior
Either "Cast to struct ptr" or "Cast to interface" would get logged and the test would pass.
This behavior would allow headers and cookies to be conditionally attached to the response using the fields that sit alongside the "value" field in the response.
Actual Behavior
The test fails with "Couldn't cast response to expected format."
Your Environment
MacOS 10.15.5
Go 1.13
Potential solutions
The reason this cast doesn't work is because the response proto.Message is embedded in a custom internal struct when response_body is defined. See this example
A few potential solutions in order of preference:
- Add a method for this internal struct that returns the embedded proto.Message directly so we can then cast to the interface for the wrapped structure and extract out our response proto. Something like the following (name TBD):
func (m response_ResponseBodyService_GetResponseBody_0) GetEmbeddedProtoMessage() proto.Message {
return m.Message
}
- Instead of embedding a proto.Message, have the internal struct embed the response proto type (ResponseBodyOut in this example)