Skip to content

Commit 4a27188

Browse files
Support deep levels reserved json name (#1191)
* Add message as another argument for supporting deep level reserved json name * Get reserved json name in non direct field of a grpc method * Change variable names based on google naming policy * Change variable names based on google naming policy in test * Reuse variable value * Format code * Update test names * Add doCamelCase and one more test case * Correct a comment in test * Keep all prefix in camel case * Use regular for loop instead of foreach to avoid unnecessary slicing * Update protoc-gen-swagger/genswagger/template_test.go Co-Authored-By: Johan Brandhorst <[email protected]> * Add space in comments * Remove a unnecessary comment Co-authored-by: Johan Brandhorst <[email protected]>
1 parent 48f2e5a commit 4a27188

File tree

2 files changed

+179
-17
lines changed

2 files changed

+179
-17
lines changed

protoc-gen-swagger/genswagger/template.go

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ func resolveFullyQualifiedNameToSwaggerNames(messages []string, useFQNForSwagger
653653
var canRegexp = regexp.MustCompile("{([a-zA-Z][a-zA-Z0-9_.]*).*}")
654654

655655
// Swagger expects paths of the form /path/{string_value} but grpc-gateway paths are expected to be of the form /path/{string_value=strprefix/*}. This should reformat it correctly.
656-
func templateToSwaggerPath(path string, reg *descriptor.Registry, fields []*descriptor.Field) string {
656+
func templateToSwaggerPath(path string, reg *descriptor.Registry, fields []*descriptor.Field, msgs []*descriptor.Message) string {
657657
// It seems like the right thing to do here is to just use
658658
// strings.Split(path, "/") but that breaks badly when you hit a url like
659659
// /{my_field=prefix/*}/ and end up with 2 sections representing my_field.
@@ -682,7 +682,7 @@ func templateToSwaggerPath(path string, reg *descriptor.Registry, fields []*desc
682682
if reg.GetUseJSONNamesForFields() &&
683683
len(jsonBuffer) > 1 {
684684
jsonSnakeCaseName := string(jsonBuffer[1:])
685-
jsonCamelCaseName := string(lowerCamelCase(jsonSnakeCaseName, fields))
685+
jsonCamelCaseName := string(lowerCamelCase(jsonSnakeCaseName, fields, msgs))
686686
prev := string(buffer[:len(buffer)-len(jsonSnakeCaseName)-2])
687687
buffer = strings.Join([]string{prev, "{", jsonCamelCaseName, "}"}, "")
688688
jsonBuffer = ""
@@ -731,7 +731,7 @@ func isResourceName(prefix string) bool {
731731
return field == "parent" || field == "name"
732732
}
733733

734-
func renderServices(services []*descriptor.Service, paths swaggerPathsObject, reg *descriptor.Registry, requestResponseRefs, customRefs refMap) error {
734+
func renderServices(services []*descriptor.Service, paths swaggerPathsObject, reg *descriptor.Registry, requestResponseRefs, customRefs refMap, msgs []*descriptor.Message) error {
735735
// Correctness of svcIdx and methIdx depends on 'services' containing the services in the same order as the 'file.Service' array.
736736
for svcIdx, svc := range services {
737737
for methIdx, meth := range svc.Methods {
@@ -806,7 +806,7 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re
806806
}
807807
parameterString := parameter.String()
808808
if reg.GetUseJSONNamesForFields() {
809-
parameterString = lowerCamelCase(parameterString, meth.RequestType.Fields)
809+
parameterString = lowerCamelCase(parameterString, meth.RequestType.Fields, msgs)
810810
}
811811
parameters = append(parameters, swaggerParameterObject{
812812
Name: parameterString,
@@ -876,7 +876,7 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re
876876
parameters = append(parameters, queryParams...)
877877
}
878878

879-
pathItemObject, ok := paths[templateToSwaggerPath(b.PathTmpl.Template, reg, meth.RequestType.Fields)]
879+
pathItemObject, ok := paths[templateToSwaggerPath(b.PathTmpl.Template, reg, meth.RequestType.Fields, msgs)]
880880
if !ok {
881881
pathItemObject = swaggerPathItemObject{}
882882
}
@@ -1098,7 +1098,7 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re
10981098
pathItemObject.Patch = operationObject
10991099
break
11001100
}
1101-
paths[templateToSwaggerPath(b.PathTmpl.Template, reg, meth.RequestType.Fields)] = pathItemObject
1101+
paths[templateToSwaggerPath(b.PathTmpl.Template, reg, meth.RequestType.Fields, msgs)] = pathItemObject
11021102
}
11031103
}
11041104
}
@@ -1128,7 +1128,7 @@ func applyTemplate(p param) (*swaggerObject, error) {
11281128
// and create entries for all of them.
11291129
// Also adds custom user specified references to second map.
11301130
requestResponseRefs, customRefs := refMap{}, refMap{}
1131-
if err := renderServices(p.Services, s.Paths, p.reg, requestResponseRefs, customRefs); err != nil {
1131+
if err := renderServices(p.Services, s.Paths, p.reg, requestResponseRefs, customRefs, p.Messages); err != nil {
11321132
panic(err)
11331133
}
11341134

@@ -1935,15 +1935,55 @@ func addCustomRefs(d swaggerDefinitionsObject, reg *descriptor.Registry, refs re
19351935
addCustomRefs(d, reg, refs)
19361936
}
19371937

1938-
func lowerCamelCase(fieldName string, fields []*descriptor.Field) string {
1938+
func lowerCamelCase(fieldName string, fields []*descriptor.Field, msgs []*descriptor.Message) string {
19391939
for _, oneField := range fields {
19401940
if oneField.GetName() == fieldName {
19411941
return oneField.GetJsonName()
19421942
}
19431943
}
1944-
parameterString := gogen.CamelCase(fieldName)
1944+
messageNameToFieldsToJSONName := make(map[string]map[string]string, 0)
1945+
fieldNameToType := make(map[string]string, 0)
1946+
for _, msg := range msgs {
1947+
fieldNameToJSONName := make(map[string]string, 0)
1948+
for _, oneField := range msg.GetField() {
1949+
fieldNameToJSONName[oneField.GetName()] = oneField.GetJsonName()
1950+
fieldNameToType[oneField.GetName()] = oneField.GetTypeName()
1951+
}
1952+
messageNameToFieldsToJSONName[msg.GetName()] = fieldNameToJSONName
1953+
}
1954+
if strings.Contains(fieldName, ".") {
1955+
fieldNames := strings.Split(fieldName, ".")
1956+
fieldNamesWithCamelCase := make([]string, 0)
1957+
for i := 0; i < len(fieldNames) - 1; i++ {
1958+
fieldNamesWithCamelCase = append(fieldNamesWithCamelCase, doCamelCase(string(fieldNames[i])))
1959+
}
1960+
prefix := strings.Join(fieldNamesWithCamelCase, ".")
1961+
reservedJSONName := getReservedJSONName(fieldName, messageNameToFieldsToJSONName,fieldNameToType)
1962+
if reservedJSONName != "" {
1963+
return prefix + "." + reservedJSONName
1964+
}
1965+
}
1966+
return doCamelCase(fieldName)
1967+
}
1968+
1969+
func doCamelCase(input string) string {
1970+
parameterString := gogen.CamelCase(input)
19451971
builder := &strings.Builder{}
19461972
builder.WriteString(strings.ToLower(string(parameterString[0])))
19471973
builder.WriteString(parameterString[1:])
19481974
return builder.String()
19491975
}
1976+
1977+
func getReservedJSONName(fieldName string, messageNameToFieldsToJSONName map[string]map[string]string, fieldNameToType map[string]string) string {
1978+
if len(strings.Split(fieldName, ".")) == 2 {
1979+
fieldNames := strings.Split(fieldName, ".")
1980+
firstVariable := fieldNames[0]
1981+
firstType := fieldNameToType[firstVariable]
1982+
firstTypeShortNames := strings.Split(firstType, ".")
1983+
firstTypeShortName := firstTypeShortNames[len(firstTypeShortNames) - 1]
1984+
return messageNameToFieldsToJSONName[firstTypeShortName][fieldNames[1]]
1985+
}
1986+
fieldNames := strings.Split(fieldName, ".")
1987+
return getReservedJSONName(strings.Join(fieldNames[1:], "."), messageNameToFieldsToJSONName, fieldNameToType)
1988+
}
1989+

protoc-gen-swagger/genswagger/template_test.go

Lines changed: 130 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1314,6 +1314,37 @@ func generateFieldsForJSONReservedName() []*descriptor.Field {
13141314
return append(fields, field)
13151315
}
13161316

1317+
func generateMsgsForJSONReservedName() []*descriptor.Message {
1318+
result := make([]*descriptor.Message, 0)
1319+
// The first message, its field is field_abc and its type is NewType
1320+
// NewType field_abc
1321+
fieldName := "field_abc"
1322+
fieldJSONName := "fieldAbc"
1323+
messageName1 := "message1"
1324+
messageType := "pkg.a.NewType"
1325+
pfd := protodescriptor.FieldDescriptorProto{Name: &fieldName, JsonName: &fieldJSONName, TypeName: &messageType}
1326+
result = append(result,
1327+
&descriptor.Message{
1328+
DescriptorProto: &protodescriptor.DescriptorProto{
1329+
Name: &messageName1, Field: []*protodescriptor.FieldDescriptorProto{&pfd},
1330+
},
1331+
})
1332+
// The second message, its name is NewName, its type is string
1333+
// message NewType {
1334+
// string field_newName [json_name = RESERVEDJSONNAME]
1335+
// }
1336+
messageName := "NewType"
1337+
field := "field_newName"
1338+
fieldJSONName2 := "RESERVEDJSONNAME"
1339+
pfd2 := protodescriptor.FieldDescriptorProto{Name: &field, JsonName: &fieldJSONName2,}
1340+
result = append(result, &descriptor.Message{
1341+
DescriptorProto: &protodescriptor.DescriptorProto{
1342+
Name: &messageName, Field: []*protodescriptor.FieldDescriptorProto{&pfd2},
1343+
},
1344+
})
1345+
return result
1346+
}
1347+
13171348
func TestTemplateWithJsonCamelCase(t *testing.T) {
13181349
var tests = []struct {
13191350
input string
@@ -1331,11 +1362,12 @@ func TestTemplateWithJsonCamelCase(t *testing.T) {
13311362
{"test/{a_a}", "test/{aA}"},
13321363
{"test/{ab_c}", "test/{abC}"},
13331364
{"test/{json_name}", "test/{jsonNAME}"},
1365+
{"test/{field_abc.field_newName}", "test/{fieldAbc.RESERVEDJSONNAME}"},
13341366
}
13351367
reg := descriptor.NewRegistry()
13361368
reg.SetUseJSONNamesForFields(true)
13371369
for _, data := range tests {
1338-
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName())
1370+
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
13391371
if data.expected != actual {
13401372
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
13411373
}
@@ -1358,11 +1390,12 @@ func TestTemplateWithoutJsonCamelCase(t *testing.T) {
13581390
{"test/{ab}", "test/{ab}"},
13591391
{"test/{a_a}", "test/{a_a}"},
13601392
{"test/{json_name}", "test/{json_name}"},
1393+
{"test/{field_abc.field_newName}", "test/{field_abc.field_newName}"},
13611394
}
13621395
reg := descriptor.NewRegistry()
13631396
reg.SetUseJSONNamesForFields(false)
13641397
for _, data := range tests {
1365-
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName())
1398+
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
13661399
if data.expected != actual {
13671400
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
13681401
}
@@ -1394,14 +1427,14 @@ func TestTemplateToSwaggerPath(t *testing.T) {
13941427
reg := descriptor.NewRegistry()
13951428
reg.SetUseJSONNamesForFields(false)
13961429
for _, data := range tests {
1397-
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName())
1430+
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
13981431
if data.expected != actual {
13991432
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
14001433
}
14011434
}
14021435
reg.SetUseJSONNamesForFields(true)
14031436
for _, data := range tests {
1404-
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName())
1437+
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
14051438
if data.expected != actual {
14061439
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
14071440
}
@@ -1416,7 +1449,7 @@ func BenchmarkTemplateToSwaggerPath(b *testing.B) {
14161449
reg.SetUseJSONNamesForFields(false)
14171450

14181451
for i := 0; i < b.N; i++ {
1419-
_ = templateToSwaggerPath(input, reg, generateFieldsForJSONReservedName())
1452+
_ = templateToSwaggerPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
14201453
}
14211454
})
14221455

@@ -1425,7 +1458,7 @@ func BenchmarkTemplateToSwaggerPath(b *testing.B) {
14251458
reg.SetUseJSONNamesForFields(true)
14261459

14271460
for i := 0; i < b.N; i++ {
1428-
_ = templateToSwaggerPath(input, reg, generateFieldsForJSONReservedName())
1461+
_ = templateToSwaggerPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
14291462
}
14301463
})
14311464
}
@@ -1501,14 +1534,14 @@ func TestFQMNtoSwaggerName(t *testing.T) {
15011534
reg := descriptor.NewRegistry()
15021535
reg.SetUseJSONNamesForFields(false)
15031536
for _, data := range tests {
1504-
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName())
1537+
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
15051538
if data.expected != actual {
15061539
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
15071540
}
15081541
}
15091542
reg.SetUseJSONNamesForFields(true)
15101543
for _, data := range tests {
1511-
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName())
1544+
actual := templateToSwaggerPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
15121545
if data.expected != actual {
15131546
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
15141547
}
@@ -2369,3 +2402,92 @@ func TestTemplateWithoutErrorDefinition(t *testing.T) {
23692402
t.Errorf("default Error response with reflink '%v', but its definition was not found", refName)
23702403
}
23712404
}
2405+
2406+
func Test_getReservedJsonName(t *testing.T) {
2407+
type args struct {
2408+
fieldName string
2409+
messageNameToFieldsToJSONName map[string]map[string]string
2410+
fieldNameToType map[string]string
2411+
}
2412+
tests := []struct {
2413+
name string
2414+
args args
2415+
want string
2416+
}{
2417+
{
2418+
"test case 1: single dot use case",
2419+
args{
2420+
fieldName: "abc.a_1",
2421+
messageNameToFieldsToJSONName: map[string]map[string]string{
2422+
"Msg": {
2423+
"a_1": "a1JSONNAME",
2424+
"b_1": "b1JSONNAME",
2425+
},
2426+
},
2427+
fieldNameToType: map[string]string{
2428+
"abc": "pkg1.test.Msg",
2429+
"bcd": "pkg1.test.Msg",
2430+
},
2431+
},
2432+
"a1JSONNAME",
2433+
},
2434+
{
2435+
"test case 2: single dot use case with no existing field",
2436+
args{
2437+
fieldName: "abc.d_1",
2438+
messageNameToFieldsToJSONName: map[string]map[string]string{
2439+
"Msg": {
2440+
"a_1": "a1JSONNAME",
2441+
"b_1": "b1JSONNAME",
2442+
},
2443+
},
2444+
fieldNameToType: map[string]string{
2445+
"abc": "pkg1.test.Msg",
2446+
"bcd": "pkg1.test.Msg",
2447+
},
2448+
},
2449+
"",
2450+
},
2451+
{
2452+
"test case 3: double dot use case",
2453+
args{
2454+
fieldName: "pkg.abc.a_1",
2455+
messageNameToFieldsToJSONName: map[string]map[string]string{
2456+
"Msg": {
2457+
"a_1": "a1JSONNAME",
2458+
"b_1": "b1JSONNAME",
2459+
},
2460+
},
2461+
fieldNameToType: map[string]string{
2462+
"abc": "pkg1.test.Msg",
2463+
"bcd": "pkg1.test.Msg",
2464+
},
2465+
},
2466+
"a1JSONNAME",
2467+
},
2468+
{
2469+
"test case 4: double dot use case with a not existed field",
2470+
args{
2471+
fieldName: "pkg.abc.c_1",
2472+
messageNameToFieldsToJSONName: map[string]map[string]string{
2473+
"Msg": {
2474+
"a_1": "a1JSONNAME",
2475+
"b_1": "b1JSONNAME",
2476+
},
2477+
},
2478+
fieldNameToType: map[string]string{
2479+
"abc": "pkg1.test.Msg",
2480+
"bcd": "pkg1.test.Msg",
2481+
},
2482+
},
2483+
"",
2484+
},
2485+
}
2486+
for _, tt := range tests {
2487+
t.Run(tt.name, func(t *testing.T) {
2488+
if got := getReservedJSONName(tt.args.fieldName, tt.args.messageNameToFieldsToJSONName, tt.args.fieldNameToType); got != tt.want {
2489+
t.Errorf("getReservedJSONName() = %v, want %v", got, tt.want)
2490+
}
2491+
})
2492+
}
2493+
}

0 commit comments

Comments
 (0)