From 340b9e07b08c0d763fa29f0a355e0c923e3381e0 Mon Sep 17 00:00:00 2001 From: Edward McFarlane Date: Wed, 8 Jan 2025 10:16:58 -0500 Subject: [PATCH] Fix protoc-gen-go schema variable case handling Signed-off-by: Edward McFarlane --- cmd/protoc-gen-connect-go/main.go | 8 +- cmd/protoc-gen-connect-go/main_test.go | 1 + .../testdata/v1beta1service/buf.gen.yaml | 15 ++ .../v1beta1service/v1beta1service.connect.go | 123 ++++++++++++ .../v1beta1service/v1beta1service.pb.go | 188 ++++++++++++++++++ .../v1beta1service/v1beta1service.proto | 26 +++ 6 files changed, 359 insertions(+), 2 deletions(-) create mode 100644 cmd/protoc-gen-connect-go/testdata/v1beta1service/buf.gen.yaml create mode 100644 cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.connect.go create mode 100644 cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.pb.go create mode 100644 cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.proto diff --git a/cmd/protoc-gen-connect-go/main.go b/cmd/protoc-gen-connect-go/main.go index a852a1c5..6b417f74 100644 --- a/cmd/protoc-gen-connect-go/main.go +++ b/cmd/protoc-gen-connect-go/main.go @@ -259,7 +259,7 @@ func generateServiceMethodsVar(g *protogen.GeneratedFile, file *protogen.File, s if len(service.Methods) == 0 { return } - serviceMethodsName := unexport(fmt.Sprintf("%sMethods", service.Desc.Name())) + serviceMethodsName := serviceVarMethodsName(service) g.P(serviceMethodsName, ` := `, g.QualifiedGoIdent(file.GoDescriptorIdent), `.Services().ByName("`, service.Desc.Name(), `").Methods()`) @@ -558,8 +558,12 @@ func procedureHandlerName(m *protogen.Method) string { return fmt.Sprintf("%s%sHandler", unexport(m.Parent.GoName), m.GoName) } +func serviceVarMethodsName(m *protogen.Service) string { + return unexport(fmt.Sprintf("%sMethods", m.GoName)) +} + func procedureVarMethodDescriptor(m *protogen.Method) string { - serviceMethodsName := unexport(fmt.Sprintf("%sMethods", m.Parent.GoName)) + serviceMethodsName := serviceVarMethodsName(m.Parent) return serviceMethodsName + `.ByName("` + string(m.Desc.Name()) + `")` } diff --git a/cmd/protoc-gen-connect-go/main_test.go b/cmd/protoc-gen-connect-go/main_test.go index 03c23cfb..1a2e647f 100644 --- a/cmd/protoc-gen-connect-go/main_test.go +++ b/cmd/protoc-gen-connect-go/main_test.go @@ -42,6 +42,7 @@ import ( "connectrpc.com/connect/cmd/protoc-gen-connect-go/testdata/diffpackage/diffpackagediff" "connectrpc.com/connect/cmd/protoc-gen-connect-go/testdata/noservice" "connectrpc.com/connect/cmd/protoc-gen-connect-go/testdata/samepackage" + _ "connectrpc.com/connect/cmd/protoc-gen-connect-go/testdata/v1beta1service" ) //go:embed testdata diff --git a/cmd/protoc-gen-connect-go/testdata/v1beta1service/buf.gen.yaml b/cmd/protoc-gen-connect-go/testdata/v1beta1service/buf.gen.yaml new file mode 100644 index 00000000..105d5fbf --- /dev/null +++ b/cmd/protoc-gen-connect-go/testdata/v1beta1service/buf.gen.yaml @@ -0,0 +1,15 @@ +version: v2 +managed: + enabled: true + override: + - file_option: go_package_prefix + value: connectrpc.com/connect/cmd/protoc-gen-connect-go/testdata/v1beta1service +plugins: + - local: protoc-gen-go + out: . + opt: paths=source_relative + - local: protoc-gen-connect-go + out: . + opt: + - paths=source_relative + - package_suffix diff --git a/cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.connect.go b/cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.connect.go new file mode 100644 index 00000000..bfefacd6 --- /dev/null +++ b/cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.connect.go @@ -0,0 +1,123 @@ +// Copyright 2021-2024 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file tests varying casing of the service name and method name. + +// Code generated by protoc-gen-connect-go. DO NOT EDIT. +// +// Source: v1beta1service.proto + +package v1beta1service + +import ( + connect "connectrpc.com/connect" + context "context" + errors "errors" + http "net/http" + strings "strings" +) + +// This is a compile-time assertion to ensure that this generated file and the connect package are +// compatible. If you get a compiler error that this constant is not defined, this code was +// generated with a version of connect newer than the one compiled into your binary. You can fix the +// problem by either regenerating this code with an older version of connect or updating the connect +// version compiled into your binary. +const _ = connect.IsAtLeastVersion1_13_0 + +const ( + // ExampleV1betaName is the fully-qualified name of the ExampleV1beta service. + ExampleV1betaName = "example.ExampleV1beta" +) + +// These constants are the fully-qualified names of the RPCs defined in this package. They're +// exposed at runtime as Spec.Procedure and as the final two segments of the HTTP route. +// +// Note that these are different from the fully-qualified method names used by +// google.golang.org/protobuf/reflect/protoreflect. To convert from these constants to +// reflection-formatted method names, remove the leading slash and convert the remaining slash to a +// period. +const ( + // ExampleV1BetaMethodProcedure is the fully-qualified name of the ExampleV1beta's Method RPC. + ExampleV1BetaMethodProcedure = "/example.ExampleV1beta/Method" +) + +// ExampleV1BetaClient is a client for the example.ExampleV1beta service. +type ExampleV1BetaClient interface { + Method(context.Context, *connect.Request[GetExample_Request]) (*connect.Response[Get1ExampleResponse], error) +} + +// NewExampleV1BetaClient constructs a client for the example.ExampleV1beta service. By default, it +// uses the Connect protocol with the binary Protobuf Codec, asks for gzipped responses, and sends +// uncompressed requests. To use the gRPC or gRPC-Web protocols, supply the connect.WithGRPC() or +// connect.WithGRPCWeb() options. +// +// The URL supplied here should be the base URL for the Connect or gRPC server (for example, +// http://api.acme.com or https://acme.com/grpc). +func NewExampleV1BetaClient(httpClient connect.HTTPClient, baseURL string, opts ...connect.ClientOption) ExampleV1BetaClient { + baseURL = strings.TrimRight(baseURL, "/") + exampleV1BetaMethods := File_v1beta1service_proto.Services().ByName("ExampleV1beta").Methods() + return &exampleV1BetaClient{ + method: connect.NewClient[GetExample_Request, Get1ExampleResponse]( + httpClient, + baseURL+ExampleV1BetaMethodProcedure, + connect.WithSchema(exampleV1BetaMethods.ByName("Method")), + connect.WithClientOptions(opts...), + ), + } +} + +// exampleV1BetaClient implements ExampleV1BetaClient. +type exampleV1BetaClient struct { + method *connect.Client[GetExample_Request, Get1ExampleResponse] +} + +// Method calls example.ExampleV1beta.Method. +func (c *exampleV1BetaClient) Method(ctx context.Context, req *connect.Request[GetExample_Request]) (*connect.Response[Get1ExampleResponse], error) { + return c.method.CallUnary(ctx, req) +} + +// ExampleV1BetaHandler is an implementation of the example.ExampleV1beta service. +type ExampleV1BetaHandler interface { + Method(context.Context, *connect.Request[GetExample_Request]) (*connect.Response[Get1ExampleResponse], error) +} + +// NewExampleV1BetaHandler builds an HTTP handler from the service implementation. It returns the +// path on which to mount the handler and the handler itself. +// +// By default, handlers support the Connect, gRPC, and gRPC-Web protocols with the binary Protobuf +// and JSON codecs. They also support gzip compression. +func NewExampleV1BetaHandler(svc ExampleV1BetaHandler, opts ...connect.HandlerOption) (string, http.Handler) { + exampleV1BetaMethods := File_v1beta1service_proto.Services().ByName("ExampleV1beta").Methods() + exampleV1BetaMethodHandler := connect.NewUnaryHandler( + ExampleV1BetaMethodProcedure, + svc.Method, + connect.WithSchema(exampleV1BetaMethods.ByName("Method")), + connect.WithHandlerOptions(opts...), + ) + return "/example.ExampleV1beta/", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch r.URL.Path { + case ExampleV1BetaMethodProcedure: + exampleV1BetaMethodHandler.ServeHTTP(w, r) + default: + http.NotFound(w, r) + } + }) +} + +// UnimplementedExampleV1BetaHandler returns CodeUnimplemented from all methods. +type UnimplementedExampleV1BetaHandler struct{} + +func (UnimplementedExampleV1BetaHandler) Method(context.Context, *connect.Request[GetExample_Request]) (*connect.Response[Get1ExampleResponse], error) { + return nil, connect.NewError(connect.CodeUnimplemented, errors.New("example.ExampleV1beta.Method is not implemented")) +} diff --git a/cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.pb.go b/cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.pb.go new file mode 100644 index 00000000..75f21153 --- /dev/null +++ b/cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.pb.go @@ -0,0 +1,188 @@ +// Copyright 2021-2024 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file tests varying casing of the service name and method name. + +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.36.0 +// protoc (unknown) +// source: v1beta1service.proto + +package v1beta1service + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +type GetExample_Request struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *GetExample_Request) Reset() { + *x = GetExample_Request{} + mi := &file_v1beta1service_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *GetExample_Request) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*GetExample_Request) ProtoMessage() {} + +func (x *GetExample_Request) ProtoReflect() protoreflect.Message { + mi := &file_v1beta1service_proto_msgTypes[0] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use GetExample_Request.ProtoReflect.Descriptor instead. +func (*GetExample_Request) Descriptor() ([]byte, []int) { + return file_v1beta1service_proto_rawDescGZIP(), []int{0} +} + +type Get1ExampleResponse struct { + state protoimpl.MessageState `protogen:"open.v1"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache +} + +func (x *Get1ExampleResponse) Reset() { + *x = Get1ExampleResponse{} + mi := &file_v1beta1service_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) +} + +func (x *Get1ExampleResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Get1ExampleResponse) ProtoMessage() {} + +func (x *Get1ExampleResponse) ProtoReflect() protoreflect.Message { + mi := &file_v1beta1service_proto_msgTypes[1] + if x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Get1ExampleResponse.ProtoReflect.Descriptor instead. +func (*Get1ExampleResponse) Descriptor() ([]byte, []int) { + return file_v1beta1service_proto_rawDescGZIP(), []int{1} +} + +var File_v1beta1service_proto protoreflect.FileDescriptor + +var file_v1beta1service_proto_rawDesc = []byte{ + 0x0a, 0x14, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, + 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x22, + 0x14, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x16, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x31, 0x65, 0x78, 0x61, + 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0x57, 0x0a, + 0x0d, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x12, 0x46, + 0x0a, 0x06, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1b, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, + 0x6c, 0x65, 0x2e, 0x47, 0x65, 0x74, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1d, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, + 0x47, 0x65, 0x74, 0x31, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5f, 0x72, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0xa8, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x65, + 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x42, 0x13, 0x56, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x73, + 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x48, 0x63, + 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x72, 0x70, 0x63, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, + 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2f, 0x63, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x63, + 0x2d, 0x67, 0x65, 0x6e, 0x2d, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2d, 0x67, 0x6f, 0x2f, + 0x74, 0x65, 0x73, 0x74, 0x64, 0x61, 0x74, 0x61, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, + 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0xa2, 0x02, 0x03, 0x45, 0x58, 0x58, 0xaa, 0x02, 0x07, + 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0xca, 0x02, 0x07, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0xe2, 0x02, 0x13, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, + 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x45, 0x78, 0x61, 0x6d, 0x70, 0x6c, + 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, +} + +var ( + file_v1beta1service_proto_rawDescOnce sync.Once + file_v1beta1service_proto_rawDescData = file_v1beta1service_proto_rawDesc +) + +func file_v1beta1service_proto_rawDescGZIP() []byte { + file_v1beta1service_proto_rawDescOnce.Do(func() { + file_v1beta1service_proto_rawDescData = protoimpl.X.CompressGZIP(file_v1beta1service_proto_rawDescData) + }) + return file_v1beta1service_proto_rawDescData +} + +var file_v1beta1service_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_v1beta1service_proto_goTypes = []any{ + (*GetExample_Request)(nil), // 0: example.GetExample_Request + (*Get1ExampleResponse)(nil), // 1: example.Get1example_response +} +var file_v1beta1service_proto_depIdxs = []int32{ + 0, // 0: example.ExampleV1beta.Method:input_type -> example.GetExample_Request + 1, // 1: example.ExampleV1beta.Method:output_type -> example.Get1example_response + 1, // [1:2] is the sub-list for method output_type + 0, // [0:1] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_v1beta1service_proto_init() } +func file_v1beta1service_proto_init() { + if File_v1beta1service_proto != nil { + return + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_v1beta1service_proto_rawDesc, + NumEnums: 0, + NumMessages: 2, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_v1beta1service_proto_goTypes, + DependencyIndexes: file_v1beta1service_proto_depIdxs, + MessageInfos: file_v1beta1service_proto_msgTypes, + }.Build() + File_v1beta1service_proto = out.File + file_v1beta1service_proto_rawDesc = nil + file_v1beta1service_proto_goTypes = nil + file_v1beta1service_proto_depIdxs = nil +} diff --git a/cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.proto b/cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.proto new file mode 100644 index 00000000..5d25d4c9 --- /dev/null +++ b/cmd/protoc-gen-connect-go/testdata/v1beta1service/v1beta1service.proto @@ -0,0 +1,26 @@ +// Copyright 2021-2024 The Connect Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// This file tests varying casing of the service name and method name. +syntax = "proto3"; + +package example; + +message GetExample_Request {} + +message Get1example_response {} + +service ExampleV1beta { + rpc Method(GetExample_Request) returns (Get1example_response) {} +}