Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wrong codes generated when nested enum in path #6587

Closed
metrue opened this issue Aug 29, 2019 · 2 comments
Closed

Wrong codes generated when nested enum in path #6587

metrue opened this issue Aug 29, 2019 · 2 comments

Comments

@metrue
Copy link

metrue commented Aug 29, 2019

What version of protobuf and what language are you using?
Version:

$ protoc --version
libprotoc 3.9.1

Language: Go

What operating system (Linux, Windows, ...) and version?
MacOS

What runtime / compiler are you using (e.g., python version or gcc version)

gcc -v
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX10.14.sdk/usr/include/c++/4.2.1
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

What did you do?

protoc -I. \
		-I$(GOPATH)/src/ \
		-I$(GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway \
		-I$(GOPATH)/src/github.com/grpc-ecosystem/grpc-gateway/third_party/googleapis \
		--grpc-gateway_out=logtostderr=true,allow_delete_body=true:. \
		--go_out=plugins=grpc:. \
		serivce.proto

What did you expect to see
Correct codes generated.

What did you see instead?
Obvious wrong code generated.

Make sure you include information that can help us debug (full error message, exception listing, stack trace, logs).

Anything else we should know about your project / environment
This is proto code,

syntax = "proto3";

package backup_svc;

import "google/protobuf/timestamp.proto";
import "google/protobuf/duration.proto";
import "google/protobuf/empty.proto";
import "google/api/annotations.proto";
import "protoc-gen-swagger/options/annotations.proto";

option (grpc.gateway.protoc_gen_swagger.options.openapiv2_swagger) = {
    info: {
        title: "My Service"
        version: "1.0"
        contact: {
            name: "backup-svc project"
            url: ""
        }
    }
    base_path: "/api"
};


// MySvc defines interface for backup service
service MySvc {
    // AwesomeEndpoint enables scheduled backup for the app with specified policy
    rpc AwesomeEndpoint(AwesomeEndpointRequest) returns (google.protobuf.Empty) {
        option (google.api.http) = {
            post: "/v1/tenants/{resource.tenant}/clouds/{resource.cloud}/{resource.type}/{resource.id}/scheduled_backup"
            body: "data"
        };
        option (grpc.gateway.protoc_gen_swagger.options.openapiv2_operation) = {
            security: {
                security_requirement: {
                    key: "BasicAuth";
                    value: {};
                }
            }
        };
    };
}

// AwesomeEndpointRequest defines request parameter for enable scheduled backup
message AwesomeEndpointRequest {
    message Body {
        map<string, string> metadata = 1;
        repeated Schedule schedules = 2;
    }

    Resource resource = 1;
    Body data = 2;
}

// Resource defines restful resource in url
message Resource {
    // Type defines the type of the resource(e.g., "databases", "virtualmachines")
    enum Type {
        databases = 0;
        virtualmachines = 1;
    }

    string tenant = 1;
    string cloud = 2;
    Type type = 3;
    string id = 4;
}

// Retention defines backup retention policy
message Retention {
    google.protobuf.Duration duration = 1;
    string storage_type = 2;
}

// Schedule defines backup schedule policy
message Schedule {
    // backup type: complete, incremental, differential
    string type = 1;
    google.protobuf.Duration frequency = 2;
    google.protobuf.Timestamp window = 3;
    // allow multiple retention policies, e.g., move backup to cold storage after 1 day and then delete after 10 days
    repeated Retention retentions = 4;
}

and generated code is

// Code generated by protoc-gen-grpc-gateway. DO NOT EDIT.
// source: backup_svc/backup_svc.proto

/*
Package backup_svc is a reverse proxy.

It translates gRPC into RESTful JSON APIs.
*/
package backup_svc

import (
	"context"
	"io"
	"net/http"

	"github.com/golang/protobuf/proto"
	"github.com/grpc-ecosystem/grpc-gateway/runtime"
	"github.com/grpc-ecosystem/grpc-gateway/utilities"
	"google.golang.org/grpc"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/status"
)

var _ codes.Code
var _ io.Reader
var _ status.Status
var _ = runtime.String
var _ = utilities.NewDoubleArray

var (
	filter_MySvc_AwesomeEndpoint_0 = &utilities.DoubleArray{Encoding: map[string]int{"data": 0, "resource": 1, "tenant": 2, "cloud": 3, "type": 4, "id": 5}, Base: []int{1, 1, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0}, Check: []int{0, 1, 1, 3, 3, 3, 3, 2, 4, 5, 6, 7}}
)

func request_MySvc_AwesomeEndpoint_0(ctx context.Context, marshaler runtime.Marshaler, client MySvcClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
	var protoReq AwesomeEndpointRequest
	var metadata runtime.ServerMetadata

	newReader, berr := utilities.IOReaderFactory(req.Body)
	if berr != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", berr)
	}
	if err := marshaler.NewDecoder(newReader()).Decode(&protoReq.Data); err != nil && err != io.EOF {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	var (
		val string
		e   int32
		ok  bool
		err error
		_   = err
	)

	val, ok = pathParams["resource.tenant"]
	if !ok {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "resource.tenant")
	}

	err = runtime.PopulateFieldFromPath(&protoReq, "resource.tenant", val)

	if err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "resource.tenant", err)
	}

	val, ok = pathParams["resource.cloud"]
	if !ok {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "resource.cloud")
	}

	err = runtime.PopulateFieldFromPath(&protoReq, "resource.cloud", val)

	if err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "resource.cloud", err)
	}

	val, ok = pathParams["resource.type"]
	if !ok {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "resource.type")
	}

	err = runtime.PopulateFieldFromPath(&protoReq, "resource.type", val)

	if err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "resource.type", err)
	}

	protoReq.Resource.Type = Resource_Type(e)

	val, ok = pathParams["resource.id"]
	if !ok {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "resource.id")
	}

	err = runtime.PopulateFieldFromPath(&protoReq, "resource.id", val)

	if err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "resource.id", err)
	}

	if err := req.ParseForm(); err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}
	if err := runtime.PopulateQueryParameters(&protoReq, req.Form, filter_MySvc_AwesomeEndpoint_0); err != nil {
		return nil, metadata, status.Errorf(codes.InvalidArgument, "%v", err)
	}

	msg, err := client.AwesomeEndpoint(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
	return msg, metadata, err

}

// RegisterMySvcHandlerFromEndpoint is same as RegisterMySvcHandler but
// automatically dials to "endpoint" and closes the connection when "ctx" gets done.
func RegisterMySvcHandlerFromEndpoint(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) {
	conn, err := grpc.Dial(endpoint, opts...)
	if err != nil {
		return err
	}
	defer func() {
		if err != nil {
			if cerr := conn.Close(); cerr != nil {
				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
			}
			return
		}
		go func() {
			<-ctx.Done()
			if cerr := conn.Close(); cerr != nil {
				grpclog.Infof("Failed to close conn to %s: %v", endpoint, cerr)
			}
		}()
	}()

	return RegisterMySvcHandler(ctx, mux, conn)
}

// RegisterMySvcHandler registers the http handlers for service MySvc to "mux".
// The handlers forward requests to the grpc endpoint over "conn".
func RegisterMySvcHandler(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error {
	return RegisterMySvcHandlerClient(ctx, mux, NewMySvcClient(conn))
}

// RegisterMySvcHandlerClient registers the http handlers for service MySvc
// to "mux". The handlers forward requests to the grpc endpoint over the given implementation of "MySvcClient".
// Note: the gRPC framework executes interceptors within the gRPC handler. If the passed in "MySvcClient"
// doesn't go through the normal gRPC flow (creating a gRPC client etc.) then it will be up to the passed in
// "MySvcClient" to call the correct interceptors.
func RegisterMySvcHandlerClient(ctx context.Context, mux *runtime.ServeMux, client MySvcClient) error {

	mux.Handle("POST", pattern_MySvc_AwesomeEndpoint_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
		ctx, cancel := context.WithCancel(req.Context())
		defer cancel()
		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
		rctx, err := runtime.AnnotateContext(ctx, mux, req)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}
		resp, md, err := request_MySvc_AwesomeEndpoint_0(rctx, inboundMarshaler, client, req, pathParams)
		ctx = runtime.NewServerMetadataContext(ctx, md)
		if err != nil {
			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
			return
		}

		forward_MySvc_AwesomeEndpoint_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)

	})

	return nil
}

var (
	pattern_MySvc_AwesomeEndpoint_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 1, 0, 4, 1, 5, 2, 2, 3, 1, 0, 4, 1, 5, 4, 1, 0, 4, 1, 5, 5, 1, 0, 4, 1, 5, 6, 2, 7}, []string{"v1", "tenants", "resource.tenant", "clouds", "resource.cloud", "resource.type", "resource.id", "scheduled_backup"}, "", runtime.AssumeColonVerbOpt(true)))
)

var (
	forward_MySvc_AwesomeEndpoint_0 = runtime.ForwardResponseMessage
)
@metrue
Copy link
Author

metrue commented Aug 29, 2019

Obviously, there is a bug out there in generated code.
first,

e   int32

e is declared on line 49, then using directly to set the Resource.Type on line 88 without assigning any value before.

protoReq.Resource.Type = Resource_Type(e)

which cause Resource.Type always be 0 (since e is 0 default).

@metrue metrue changed the title Generate wrong code Wrong codes generated when nested enum in path Aug 29, 2019
@acozzette
Copy link
Member

@metrue Thank you for reporting this but it looks like the issue is related to the grpc-ecosystem/grpc-gateway project, which is a separate project not owned by our team. You might want to try filing an issue with that project. If it turns out to be an issue with Go protobuf then that is also maintained by the Go team at golang/protobuf.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants