Closed mwitkow closed 9 years ago
Yup, indeed, intalizing the field seems to have worked:
if protoReq.Deployment == nil {
protoReq.Deployment = &DeploymentId{}
}
protoReq.Deployment.Name, err = runtime.String(val)
if err != nil {
return nil, err
}
The tricky bit is to initialize the field without knowing what the type is. Some reflection perhaps? Or template-generating a NewDeploymentField
that will act like Deployment.Reset()
The following function is my (probably misguided) stab at reflecting out of the problem. It instantiates all missing structs along the way.
func instantiateMissingStructs(structPtr interface{}, fieldPath string) (error) {
fieldNames := strings.Split(fieldPath, ".")
ptrToStruct := reflect.ValueOf(structPtr)
if ptrToStruct.Kind() != reflect.Ptr {
return fmt.Errorf("structPtr must be a pointer struct, is %v", ptrToStruct.Kind())
}
currentStruct := reflect.Indirect(ptrToStruct)
if currentStruct.Kind() != reflect.Struct {
return fmt.Errorf("structPtr must be a pointer to a struct, is %v.", ptrToStruct.Kind())
}
for _, name := range fieldNames {
fieldV := currentStruct.FieldByName(name)
if fieldV.Kind() != reflect.Ptr {
return fmt.Errorf("field %v must be a pointer", name)
}
concreteType := fieldV.Type().Elem()
if concreteType.Kind() != reflect.Struct {
return fmt.Errorf("field %v must be a pointer to a struct", name)
}
if fieldV.IsNil() {
fieldV.Set(reflect.New(concreteType))
}
currentStruct = reflect.Indirect(fieldV)
}
return nil
}
For example in the above example, this would instantiate the missing DeploymentId
struct under the Deployment
field.
instantiateMissingStructs(&protoReq, "Deployment")
Or if the URL reference was to deployment.app.id
, and the assignment was protoReq.Deployment.App.Id
, the following one would instantiate both DeploymentId
and AppId
:
instantiateMissingStructs(&protoReq, "Deployment.App")
Any idea where to hook this up? I guess gengo/grpc-gateway/protoc-gen-grpc-gateway/gengateway/generator.go
, but I don't know how to do the tests for it :(
So yea, if golang/protobuf#54 makes it instantiate a new type, this is trivial. Otherwise, I think I'll make a PR that reuse the query filter magic.
So I didn't realise that the grpc-gateway already had a reflection instantiator for the query filtering. The above pull request contains the fix that reuses this.
@yugui, do you need anything from me in relation to #34 to get this fixed? :)
Sorry for my late reply. I have just merged #34.
On Tue, Aug 11, 2015 at 4:36 PM Michal Witkowski notifications@github.com wrote:
@yugui https://github.com/yugui, do you need anything from me in relation to #34 https://github.com/gengo/grpc-gateway/pull/34 to get this fixed? :)
— Reply to this email directly or view it on GitHub https://github.com/gengo/grpc-gateway/issues/32#issuecomment-129740634.
Take this as an example (in
proto3
):When I call the service through
GET
/deployment/v1/mydeployment/flag/myflag
I get a nil pointer dereference:the Gateway-generated code in question:
The problem seems to be that the
Deployment
field ofFlagId
is not initialized and is anil
reference:I found some complex expressions like
{bucket_name=buckets/*} in
parse_test.go`, but I don't think these would help here. @yugui is this a bug or am I just using grpc gateway wrong?