Skip to content

Commit 0c2b3b1

Browse files
hypnocejohanbrandhorst
authored andcommitted
protoc-gen-swagger: add fqn_for_swagger_name option
Adds a new option fqn_for_swagger_name. This uses the protobuf FQN in the generated swagger specification.
1 parent fff1507 commit 0c2b3b1

File tree

5 files changed

+73
-17
lines changed

5 files changed

+73
-17
lines changed

protoc-gen-grpc-gateway/descriptor/registry.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,12 @@ type Registry struct {
6161
// otherwise the original proto name is used. It's helpful for synchronizing the swagger definition
6262
// with grpc-gateway response, if it uses json tags for marshaling.
6363
useJSONNamesForFields bool
64+
65+
// useFQNForSwaggerName if true swagger names will use the full qualified name (FQN) from proto definition,
66+
// and generate a dot-separated swagger name concatenating all elements from the proto FQN.
67+
// If false, the default behavior is to concat the last 2 elements of the FQN if they are unique, otherwise concat
68+
// all the elements of the FQN without any separator
69+
useFQNForSwaggerName bool
6470
}
6571

6672
type repeatedFieldSeparator struct {
@@ -411,6 +417,16 @@ func (r *Registry) GetUseJSONNamesForFields() bool {
411417
return r.useJSONNamesForFields
412418
}
413419

420+
// SetUseFQNForSwaggerName sets useFQNForSwaggerName
421+
func (r *Registry) SetUseFQNForSwaggerName(use bool) {
422+
r.useFQNForSwaggerName = use
423+
}
424+
425+
// GetUseFQNForSwaggerName returns useFQNForSwaggerName
426+
func (r *Registry) GetUseFQNForSwaggerName() bool {
427+
return r.useFQNForSwaggerName
428+
}
429+
414430
// GetMergeFileName return the target merge swagger file name
415431
func (r *Registry) GetMergeFileName() string {
416432
return r.mergeFileName

protoc-gen-swagger/genswagger/template.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -527,7 +527,7 @@ func fullyQualifiedNameToSwaggerName(fqn string, reg *descriptor.Registry) strin
527527
if mapping, present := registriesSeen[reg]; present {
528528
return mapping[fqn]
529529
}
530-
mapping := resolveFullyQualifiedNameToSwaggerNames(append(reg.GetAllFQMNs(), reg.GetAllFQENs()...))
530+
mapping := resolveFullyQualifiedNameToSwaggerNames(append(reg.GetAllFQMNs(), reg.GetAllFQENs()...), reg.GetUseFQNForSwaggerName())
531531
registriesSeen[reg] = mapping
532532
return mapping[fqn]
533533
}
@@ -544,7 +544,7 @@ var registriesSeenMutex sync.Mutex
544544
// This likely could be made better. This will always generate the same names
545545
// but may not always produce optimal names. This is a reasonably close
546546
// approximation of what they should look like in most cases.
547-
func resolveFullyQualifiedNameToSwaggerNames(messages []string) map[string]string {
547+
func resolveFullyQualifiedNameToSwaggerNames(messages []string, useFQNForSwaggerName bool) map[string]string {
548548
packagesByDepth := make(map[int][][]string)
549549
uniqueNames := make(map[string]string)
550550

@@ -573,14 +573,19 @@ func resolveFullyQualifiedNameToSwaggerNames(messages []string) map[string]strin
573573
}
574574

575575
for _, p := range messages {
576-
h := hierarchy(p)
577-
for depth := 0; depth < len(h); depth++ {
578-
if count(packagesByDepth[depth], h[len(h)-depth:]) == 1 {
579-
uniqueNames[p] = strings.Join(h[len(h)-depth-1:], "")
580-
break
581-
}
582-
if depth == len(h)-1 {
583-
uniqueNames[p] = strings.Join(h, "")
576+
if useFQNForSwaggerName {
577+
// strip leading dot from proto fqn
578+
uniqueNames[p] = p[1:]
579+
} else {
580+
h := hierarchy(p)
581+
for depth := 0; depth < len(h); depth++ {
582+
if count(packagesByDepth[depth], h[len(h)-depth:]) == 1 {
583+
uniqueNames[p] = strings.Join(h[len(h)-depth-1:], "")
584+
break
585+
}
586+
if depth == len(h)-1 {
587+
uniqueNames[p] = strings.Join(h, "")
588+
}
584589
}
585590
}
586591
}

protoc-gen-swagger/genswagger/template_test.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -853,16 +853,18 @@ func TestTemplateToSwaggerPath(t *testing.T) {
853853

854854
func TestResolveFullyQualifiedNameToSwaggerName(t *testing.T) {
855855
var tests = []struct {
856-
input string
857-
output string
858-
listOfFQMNs []string
856+
input string
857+
output string
858+
listOfFQMNs []string
859+
useFQNForSwaggerName bool
859860
}{
860861
{
861862
".a.b.C",
862863
"C",
863864
[]string{
864865
".a.b.C",
865866
},
867+
false,
866868
},
867869
{
868870
".a.b.C",
@@ -871,6 +873,7 @@ func TestResolveFullyQualifiedNameToSwaggerName(t *testing.T) {
871873
".a.C",
872874
".a.b.C",
873875
},
876+
false,
874877
},
875878
{
876879
".a.b.C",
@@ -880,11 +883,22 @@ func TestResolveFullyQualifiedNameToSwaggerName(t *testing.T) {
880883
".a.C",
881884
".a.b.C",
882885
},
886+
false,
887+
},
888+
{
889+
".a.b.C",
890+
"a.b.C",
891+
[]string{
892+
".C",
893+
".a.C",
894+
".a.b.C",
895+
},
896+
true,
883897
},
884898
}
885899

886900
for _, data := range tests {
887-
names := resolveFullyQualifiedNameToSwaggerNames(data.listOfFQMNs)
901+
names := resolveFullyQualifiedNameToSwaggerNames(data.listOfFQMNs, data.useFQNForSwaggerName)
888902
output := names[data.input]
889903
if output != data.output {
890904
t.Errorf("Expected fullyQualifiedNameToSwaggerName(%v) to be %s but got %s",

protoc-gen-swagger/main.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ var (
2626
versionFlag = flag.Bool("version", false, "print the current verison")
2727
allowRepeatedFieldsInBody = flag.Bool("allow_repeated_fields_in_body", false, "allows to use repeated field in `body` and `response_body` field of `google.api.http` annotation option")
2828
includePackageInTags = flag.Bool("include_package_in_tags", false, "if unset, the gRPC service name is added to the `Tags` field of each operation. if set and the `package` directive is shown in the proto file, the package name will be prepended to the service name")
29+
useFQNForSwaggerName = flag.Bool("fqn_for_swagger_name", false, "if set, the object's swagger names will use the fully qualify name from the proto definition (ie my.package.MyMessage.MyInnerMessage")
2930
)
3031

3132
// Variables set by goreleaser at build time
@@ -76,6 +77,7 @@ func main() {
7677
reg.SetUseJSONNamesForFields(*useJSONNamesForFields)
7778
reg.SetAllowRepeatedFieldsInBody(*allowRepeatedFieldsInBody)
7879
reg.SetIncludePackageInTags(*includePackageInTags)
80+
reg.SetUseFQNForSwaggerName(*useFQNForSwaggerName)
7981
if err := reg.SetRepeatedPathParamSeparator(*repeatedPathParamSeparator); err != nil {
8082
emitError(err)
8183
return

protoc-gen-swagger/main_test.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ func TestParseReqParam(t *testing.T) {
2121
fileV string
2222
importPathV string
2323
mergeFileNameV string
24+
useFQNForSwaggerNameV bool
2425
}{
2526
{
2627
// this one must be first - with no leading clearFlags call it
@@ -99,6 +100,21 @@ func TestParseReqParam(t *testing.T) {
99100
allowDeleteBodyV: false, allowMergeV: false, allowRepeatedFieldsInBodyV: false, includePackageInTagsV: false,
100101
fileV: "stdin", importPathV: "", mergeFileNameV: "apidocs",
101102
},
103+
{
104+
name: "Test 10",
105+
expected: map[string]string{},
106+
request: "fqn_for_swagger_name=3",
107+
expectedError: errors.New(`Cannot set flag fqn_for_swagger_name=3: strconv.ParseBool: parsing "3": invalid syntax`),
108+
allowDeleteBodyV: false, allowMergeV: false, allowRepeatedFieldsInBodyV: false, includePackageInTagsV: false, useFQNForSwaggerNameV: false,
109+
fileV: "stdin", importPathV: "", mergeFileNameV: "apidocs",
110+
},
111+
{
112+
name: "Test 11",
113+
expected: map[string]string{},
114+
request: "fqn_for_swagger_name=true",
115+
allowDeleteBodyV: false, allowMergeV: false, allowRepeatedFieldsInBodyV: false, includePackageInTagsV: false, useFQNForSwaggerNameV: true,
116+
fileV: "stdin", importPathV: "", mergeFileNameV: "apidocs",
117+
},
102118
}
103119

104120
for i, tc := range testcases {
@@ -124,15 +140,15 @@ func TestParseReqParam(t *testing.T) {
124140
tt.Errorf("expected error malformed, expected %q, got %q", tc.expectedError.Error(), err.Error())
125141
}
126142
}
127-
checkFlags(tc.allowDeleteBodyV, tc.allowMergeV, tc.allowRepeatedFieldsInBodyV, tc.includePackageInTagsV, tc.fileV, tc.importPathV, tc.mergeFileNameV, tt, i)
143+
checkFlags(tc.allowDeleteBodyV, tc.allowMergeV, tc.allowRepeatedFieldsInBodyV, tc.includePackageInTagsV, tc.useFQNForSwaggerNameV, tc.fileV, tc.importPathV, tc.mergeFileNameV, tt, i)
128144

129145
clearFlags()
130146
})
131147
}
132148

133149
}
134150

135-
func checkFlags(allowDeleteV, allowMergeV, allowRepeatedFieldsInBodyV, includePackageInTagsV bool, fileV, importPathV, mergeFileNameV string, t *testing.T, tid int) {
151+
func checkFlags(allowDeleteV, allowMergeV, allowRepeatedFieldsInBodyV, includePackageInTagsV bool, useFQNForSwaggerNameV bool, fileV, importPathV, mergeFileNameV string, t *testing.T, tid int) {
136152
if *importPrefix != importPathV {
137153
t.Errorf("Test %v: import_prefix misparsed, expected '%v', got '%v'", tid, importPathV, *importPrefix)
138154
}
@@ -152,7 +168,10 @@ func checkFlags(allowDeleteV, allowMergeV, allowRepeatedFieldsInBodyV, includePa
152168
t.Errorf("Test %v: allow_repeated_fields_in_body misparsed, expected '%v', got '%v'", tid, allowRepeatedFieldsInBodyV, *allowRepeatedFieldsInBody)
153169
}
154170
if *includePackageInTags != includePackageInTagsV {
155-
t.Errorf("Test %v: allow_repeated_fields_in_body misparsed, expected '%v', got '%v'", tid, includePackageInTagsV, *includePackageInTags)
171+
t.Errorf("Test %v: include_package_in_tags misparsed, expected '%v', got '%v'", tid, includePackageInTagsV, *includePackageInTags)
172+
}
173+
if *useFQNForSwaggerName != useFQNForSwaggerNameV {
174+
t.Errorf("Test %v: fqn_for_swagger_name misparsed, expected '%v', got '%v'", tid, useFQNForSwaggerNameV, *useFQNForSwaggerName)
156175
}
157176
}
158177

0 commit comments

Comments
 (0)