From c8bc7e1d29fa90542c0e50c5f1c207f2d16b4970 Mon Sep 17 00:00:00 2001 From: Cezar Azevedo de Faveri Date: Mon, 19 Jun 2023 18:25:40 -0300 Subject: [PATCH] Draft: Fix names with special characters and numbers on the beggining (#80) * - Update gnostic dependency to latest version - Add '_' at the beggining of enum fields that start with a number * Replace hyphens with underscore * Add test cases * Handle messages with a leading number * Escape the + char * Remove functionality that changes output name * Fix test --------- Co-authored-by: Lorenz Hofmann-Wellenhof --- generator/checker_test.go | 1 + generator/generator_messages.go | 17 +++++++-- generator/generator_service.go | 10 +---- generator/language.go | 22 +++++++++-- generator/testfiles/goldstandard/other.proto | 4 +- .../testfiles/goldstandard/parameters.proto | 37 +++++++++++++------ .../goldstandard/requestbodies.proto | 8 ++-- generator/testfiles/parameters.yaml | 16 +++++++- go.mod | 2 +- go.sum | 4 +- 10 files changed, 84 insertions(+), 37 deletions(-) diff --git a/generator/checker_test.go b/generator/checker_test.go index 7145b47..bf09d5e 100644 --- a/generator/checker_test.go +++ b/generator/checker_test.go @@ -33,6 +33,7 @@ func TestNewFeatureCheckerParameters(t *testing.T) { checker := NewGrpcChecker(documentv3) messages := checker.Run() expectedMessageKeys := [][]string{ + {"components", "parameters", "required"}, {"paths", "/testParameterQueryEnum", "get", "parameters", "explode"}, {"paths", "/testParameterQueryEnum", "get", "parameters", "schema", "items", "default"}, {"paths", "/testParameterPathEnum/{param1}", "get", "parameters", "schema", "default"}, diff --git a/generator/generator_messages.go b/generator/generator_messages.go index 18781aa..20f40f9 100644 --- a/generator/generator_messages.go +++ b/generator/generator_messages.go @@ -29,8 +29,6 @@ func buildAllMessageDescriptors(renderer *Renderer) (messageDescriptors []*dpb.D } if isRequestParameter(surfaceType) { validateRequestParameter(surfaceField) - // convert base message to parameter message, e.g., GetPetRequest -> GetPetParameters - message.Name = &surfaceType.Name } addFieldDescriptor(message, surfaceField, i, renderer.Package) @@ -250,12 +248,25 @@ func addEnumDescriptorIfNecessary(message *dpb.DescriptorProto, f *surface_v1.Fi } } +func getEnumFieldName(value string) string { + name := strings.ToUpper(value) + name = strings.ReplaceAll(name, "-", "_") + + firstChar := name[0] + + if '0' <= firstChar && firstChar <= '9' { + return "_" + name + } + + return name +} + // buildEnumDescriptorProto builds the necessary descriptor to render a enum. (https://developers.google.com/protocol-buffers/docs/proto3#enum) func buildEnumDescriptorProto(f *surface_v1.Field) *dpb.EnumDescriptorProto { enumDescriptor := &dpb.EnumDescriptorProto{Name: &f.NativeType} for enumCtr, value := range f.EnumValues { num := int32(enumCtr) - name := strings.ToUpper(value) + name := getEnumFieldName(value) valueDescriptor := &dpb.EnumValueDescriptorProto{ Name: &name, Number: &num, diff --git a/generator/generator_service.go b/generator/generator_service.go index 1fad5f2..aeb984c 100644 --- a/generator/generator_service.go +++ b/generator/generator_service.go @@ -42,7 +42,7 @@ func buildMethodDescriptor(method *surface_v1.Method, types []*surface_v1.Type) if err != nil { return nil, err } - inputType, outputType := buildInputTypeAndOutputType(method.ParametersTypeName, method.ResponsesTypeName, types) + inputType, outputType := buildInputTypeAndOutputType(method.ParametersTypeName, method.ResponsesTypeName) methodDescriptor = &dpb.MethodDescriptorProto{ Name: &method.HandlerName, InputType: &inputType, @@ -120,7 +120,7 @@ func getRequestBodyForRequestParameter(name string, types []*surface_v1.Type) st return "" } -func buildInputTypeAndOutputType(parametersTypeName, responseTypeName string, types []*surface_v1.Type) (inputType, outputType string) { +func buildInputTypeAndOutputType(parametersTypeName, responseTypeName string) (inputType, outputType string) { inputType = parametersTypeName outputType = responseTypeName if parametersTypeName == "" { @@ -129,12 +129,6 @@ func buildInputTypeAndOutputType(parametersTypeName, responseTypeName string, ty if responseTypeName == "" { outputType = "google.protobuf.Empty" } - for _, t := range types { - // convert base input type to parameter input type, e.g., GetPetRequest -> GetPetParameters - if t.TypeName == inputType && t.Name != inputType { - inputType = t.Name - } - } return inputType, outputType } diff --git a/generator/language.go b/generator/language.go index d631878..d236843 100644 --- a/generator/language.go +++ b/generator/language.go @@ -4,7 +4,7 @@ // 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 +// 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, @@ -283,7 +283,7 @@ func protoTypeName(originalName string) (name string) { return name } -// Removes characters which are not allowed for message names or field names inside .proto files. +// CleanName removes characters which are not allowed for message names or field names inside .proto files. func CleanName(name string) string { name = strings.Replace(name, "application/json", "", -1) name = strings.Replace(name, ".", "_", -1) @@ -295,7 +295,23 @@ func CleanName(name string) string { name = strings.Replace(name, "}", "", -1) name = strings.Replace(name, "/", "_", -1) name = strings.Replace(name, "$", "", -1) - return name + name = strings.Replace(name, "+", "", -1) + return escapeNumericFirstChar(name) +} + +// escapeNumericFirstChar add _ to the beggining of the string if first char is a number +func escapeNumericFirstChar(str string) string { + if len(str) == 0 { + return str + } + + firstChar := str[0] + + if '0' <= firstChar && firstChar <= '9' { + return "_" + str + } + + return str } // toCamelCase converts str to CamelCase diff --git a/generator/testfiles/goldstandard/other.proto b/generator/testfiles/goldstandard/other.proto index 6df1afd..e59649e 100644 --- a/generator/testfiles/goldstandard/other.proto +++ b/generator/testfiles/goldstandard/other.proto @@ -107,7 +107,7 @@ message TestAnyOfApiResponse { } //TestExernalReference2Parameters holds parameters to TestExernalReference2 -message TestExernalReference2Parameters { +message TestExernalReference2Request { parameters.Parameter2 parameter2 = 1; } @@ -137,7 +137,7 @@ service Other { option (google.api.http) = { get:"/testExternalReference" }; } - rpc TestExernalReference2 ( TestExernalReference2Parameters ) returns ( google.protobuf.Empty ) { + rpc TestExernalReference2 ( TestExernalReference2Request ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/testExternalReference2" body:"parameter2" }; } diff --git a/generator/testfiles/goldstandard/parameters.proto b/generator/testfiles/goldstandard/parameters.proto index f74f301..3230029 100644 --- a/generator/testfiles/goldstandard/parameters.proto +++ b/generator/testfiles/goldstandard/parameters.proto @@ -19,12 +19,12 @@ message Parameter2 { } //TestParameterQueryParameters holds parameters to TestParameterQuery -message TestParameterQueryParameters { +message TestParameterQueryRequest { int32 param1 = 1; } //TestParameterQueryEnumParameters holds parameters to TestParameterQueryEnum -message TestParameterQueryEnumParameters { +message TestParameterQueryEnumRequest { repeated Param2 param2 = 1; enum Param2 { @@ -33,16 +33,20 @@ message TestParameterQueryEnumParameters { HUSKY = 1; RETRIEVER = 2; + + _5CHIHUAHUA = 3; + + SHARPEI_2 = 4; } } //TestParameterPathParameters holds parameters to TestParameterPath -message TestParameterPathParameters { +message TestParameterPathRequest { string param3 = 1; } //TestParameterPathEnumParameters holds parameters to TestParameterPathEnum -message TestParameterPathEnumParameters { +message TestParameterPathEnumRequest { Param4 param4 = 1; enum Param4 { @@ -53,40 +57,49 @@ message TestParameterPathEnumParameters { } //TestParameterMultiplePathParameters holds parameters to TestParameterMultiplePath -message TestParameterMultiplePathParameters { +message TestParameterMultiplePathRequest { string param5 = 1; string param6 = 2; } //TestParameterReferenceParameters holds parameters to TestParameterReference -message TestParameterReferenceParameters { +message TestParameterReferenceRequest { + Parameter1 parameter1 = 1; +} + +//5testRouteWithNumberParameters holds parameters to 5testRouteWithNumber +message _5testRouteWithNumberRequest { Parameter1 parameter1 = 1; } service Parameters { - rpc TestParameterQuery ( TestParameterQueryParameters ) returns ( google.protobuf.Empty ) { + rpc TestParameterQuery ( TestParameterQueryRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/testParameterQuery" }; } - rpc TestParameterQueryEnum ( TestParameterQueryEnumParameters ) returns ( google.protobuf.Empty ) { + rpc TestParameterQueryEnum ( TestParameterQueryEnumRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/testParameterQueryEnum" }; } - rpc TestParameterPath ( TestParameterPathParameters ) returns ( google.protobuf.Empty ) { + rpc TestParameterPath ( TestParameterPathRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/testParameterPath/{param1}" }; } - rpc TestParameterPathEnum ( TestParameterPathEnumParameters ) returns ( google.protobuf.Empty ) { + rpc TestParameterPathEnum ( TestParameterPathEnumRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/testParameterPathEnum/{param1}" }; } - rpc TestParameterMultiplePath ( TestParameterMultiplePathParameters ) returns ( google.protobuf.Empty ) { + rpc TestParameterMultiplePath ( TestParameterMultiplePathRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/testParameterMultiplePath/{param1}/{param2}" }; } - rpc TestParameterReference ( TestParameterReferenceParameters ) returns ( google.protobuf.Empty ) { + rpc TestParameterReference ( TestParameterReferenceRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/testParameterReference" }; } + + rpc _5testRouteWithNumber ( _5testRouteWithNumberRequest ) returns ( google.protobuf.Empty ) { + option (google.api.http) = { get:"/5testRouteWithNumber" }; + } } diff --git a/generator/testfiles/goldstandard/requestbodies.proto b/generator/testfiles/goldstandard/requestbodies.proto index 56976bc..bd22f6d 100644 --- a/generator/testfiles/goldstandard/requestbodies.proto +++ b/generator/testfiles/goldstandard/requestbodies.proto @@ -21,21 +21,21 @@ message Person { } //TestRequestBodyParameters holds parameters to TestRequestBody -message TestRequestBodyParameters { +message TestRequestBodyRequest { Person person = 1; } //TestRequestBodyReferenceParameters holds parameters to TestRequestBodyReference -message TestRequestBodyReferenceParameters { +message TestRequestBodyReferenceRequest { Person person = 1; } service Requestbodies { - rpc TestRequestBody ( TestRequestBodyParameters ) returns ( google.protobuf.Empty ) { + rpc TestRequestBody ( TestRequestBodyRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/testRequestBody" body:"person" }; } - rpc TestRequestBodyReference ( TestRequestBodyReferenceParameters ) returns ( google.protobuf.Empty ) { + rpc TestRequestBodyReference ( TestRequestBodyReferenceRequest ) returns ( google.protobuf.Empty ) { option (google.api.http) = { get:"/testRequestBodyReference" body:"person" }; } } diff --git a/generator/testfiles/parameters.yaml b/generator/testfiles/parameters.yaml index a1e4683..a34fdd5 100644 --- a/generator/testfiles/parameters.yaml +++ b/generator/testfiles/parameters.yaml @@ -25,7 +25,7 @@ paths: responses: 200: description: success - /testParameterQueryEnum: #TODO: Generates invalid proto for integer enums + /testParameterQueryEnum: get: operationId: testParameterQueryEnum parameters: @@ -40,6 +40,8 @@ paths: - Dingo - Husky - Retriever + - 5Chihuahua + - Sharpei-2 default: Husky responses: 200: @@ -95,6 +97,14 @@ paths: responses: 200: description: success + /5testRouteWithNumber: + get: + operationId: 5testRouteWithNumber + parameters: + - $ref: '#/components/parameters/Parameter1' + responses: + 200: + description: successful operation components: parameters: Parameter1: @@ -103,9 +113,11 @@ components: schema: type: integer format: int64 + required: false Parameter2: name: param8 in: path schema: type: integer - format: int64 \ No newline at end of file + format: int64 + required: true \ No newline at end of file diff --git a/go.mod b/go.mod index f04e65a..a79d392 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.20 require ( github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/protobuf v1.5.2 - github.com/google/gnostic v0.6.8 + github.com/google/gnostic v0.6.9 github.com/google/go-cmp v0.5.6 github.com/grpc-ecosystem/grpc-gateway v1.16.0 github.com/jhump/protoreflect v1.10.0 diff --git a/go.sum b/go.sum index 3760779..899eed0 100644 --- a/go.sum +++ b/go.sum @@ -41,8 +41,8 @@ github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/gnostic v0.6.8 h1:bT56GPYBWh1tvBuBEd94qcS3+60b+y0HQur0ITkGuCk= -github.com/google/gnostic v0.6.8/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= +github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= +github.com/google/gnostic v0.6.9/go.mod h1:Nm8234We1lq6iB9OmlgNv3nH91XLLVZHCDayfA3xq+E= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=