Skip to content

Commit c7d450f

Browse files
Koptelov Nikitajohanbrandhorst
authored andcommitted
genswagger: emit errors if swagger name can't be resolved
1 parent 6b0cd60 commit c7d450f

File tree

3 files changed

+88
-37
lines changed

3 files changed

+88
-37
lines changed

protoc-gen-swagger/genswagger/template.go

Lines changed: 78 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -221,28 +221,40 @@ func findServicesMessagesAndEnumerations(s []*descriptor.Service, reg *descripto
221221
for _, svc := range s {
222222
for _, meth := range svc.Methods {
223223
// Request may be fully included in query
224-
if _, ok := refs[fmt.Sprintf("#/definitions/%s", fullyQualifiedNameToSwaggerName(meth.RequestType.FQMN(), reg))]; ok {
225-
if !skipRenderingRef(meth.RequestType.FQMN()) {
226-
m[fullyQualifiedNameToSwaggerName(meth.RequestType.FQMN(), reg)] = meth.RequestType
224+
{
225+
swgReqName, ok := fullyQualifiedNameToSwaggerName(meth.RequestType.FQMN(), reg)
226+
if !ok {
227+
glog.Errorf("couldn't resolve swagger name for FQMN '%v'", meth.RequestType.FQMN())
228+
continue
229+
}
230+
if _, ok := refs[fmt.Sprintf("#/definitions/%s", swgReqName)]; ok {
231+
if !skipRenderingRef(meth.RequestType.FQMN()) {
232+
m[swgReqName] = meth.RequestType
233+
}
227234
}
228235
}
236+
237+
swgRspName, ok := fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)
238+
if !ok && !skipRenderingRef(meth.ResponseType.FQMN()) {
239+
glog.Errorf("couldn't resolve swagger name for FQMN '%v'", meth.ResponseType.FQMN())
240+
continue
241+
}
242+
229243
findNestedMessagesAndEnumerations(meth.RequestType, reg, m, e)
230244

231245
if !skipRenderingRef(meth.ResponseType.FQMN()) {
232-
m[fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)] = meth.ResponseType
246+
m[swgRspName] = meth.ResponseType
233247
if meth.GetServerStreaming() {
234-
runtimeStreamError := fullyQualifiedNameToSwaggerName(".grpc.gateway.runtime.StreamError", reg)
235-
glog.V(1).Infof("StreamError FQMN: %s", runtimeStreamError)
236-
streamError, err := reg.LookupMsg(".grpc.gateway.runtime", "StreamError")
237-
if err == nil {
248+
streamError, runtimeStreamError, err := lookupMsgAndSwaggerName(".grpc.gateway.runtime", "StreamError", reg)
249+
if err != nil {
250+
glog.Error(err)
251+
} else {
238252
glog.V(1).Infof("StreamError: %v", streamError)
253+
glog.V(1).Infof("StreamError FQMN: %s", runtimeStreamError)
239254
m[runtimeStreamError] = streamError
240255
findNestedMessagesAndEnumerations(streamError, reg, m, e)
241-
} else {
242-
//just in case there is an error looking up StreamError
243-
glog.Error(err)
244256
}
245-
ms[fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)] = meth.ResponseType
257+
ms[swgRspName] = meth.ResponseType
246258
}
247259
}
248260
findNestedMessagesAndEnumerations(meth.ResponseType, reg, m, e)
@@ -281,6 +293,10 @@ func skipRenderingRef(refName string) bool {
281293

282294
func renderMessagesAsDefinition(messages messageMap, d swaggerDefinitionsObject, reg *descriptor.Registry, customRefs refMap) {
283295
for name, msg := range messages {
296+
swgName, ok := fullyQualifiedNameToSwaggerName(msg.FQMN(), reg)
297+
if !ok {
298+
panic(fmt.Sprintf("can't resolve swagger name from '%v'", msg.FQMN()))
299+
}
284300
if skipRenderingRef(name) {
285301
continue
286302
}
@@ -354,7 +370,7 @@ func renderMessagesAsDefinition(messages messageMap, d swaggerDefinitionsObject,
354370
}
355371
*schema.Properties = append(*schema.Properties, kv)
356372
}
357-
d[fullyQualifiedNameToSwaggerName(msg.FQMN(), reg)] = schema
373+
d[swgName] = schema
358374
}
359375
}
360376

@@ -392,12 +408,15 @@ func schemaOfField(f *descriptor.Field, reg *descriptor.Registry, refs refMap) s
392408
props = &swaggerSchemaObjectProperties{}
393409
}
394410
} else {
411+
swgRef, ok := fullyQualifiedNameToSwaggerName(fd.GetTypeName(), reg)
412+
if !ok {
413+
panic(fmt.Sprintf("can't resolve swagger ref from typename '%v'", fd.GetTypeName()))
414+
}
395415
core = schemaCore{
396-
Ref: "#/definitions/" + fullyQualifiedNameToSwaggerName(fd.GetTypeName(), reg),
416+
Ref: "#/definitions/" + swgRef,
397417
}
398418
if refs != nil {
399419
refs[fd.GetTypeName()] = struct{}{}
400-
401420
}
402421
}
403422
default:
@@ -492,6 +511,10 @@ func primitiveSchema(t pbdescriptor.FieldDescriptorProto_Type) (ftype, format st
492511
// renderEnumerationsAsDefinition inserts enums into the definitions object.
493512
func renderEnumerationsAsDefinition(enums enumMap, d swaggerDefinitionsObject, reg *descriptor.Registry) {
494513
for _, enum := range enums {
514+
swgName, ok := fullyQualifiedNameToSwaggerName(enum.FQEN(), reg)
515+
if !ok {
516+
panic(fmt.Sprintf("can't resolve swagger name from FQEN '%v'", enum.FQEN()))
517+
}
495518
enumComments := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index))
496519

497520
// it may be necessary to sort the result of the GetValue function.
@@ -512,20 +535,13 @@ func renderEnumerationsAsDefinition(enums enumMap, d swaggerDefinitionsObject, r
512535
panic(err)
513536
}
514537

515-
d[fullyQualifiedNameToSwaggerName(enum.FQEN(), reg)] = enumSchemaObject
538+
d[swgName] = enumSchemaObject
516539
}
517540
}
518541

519-
// Take in a FQMN or FQEN and return a swagger safe version of the FQMN
520-
func fullyQualifiedNameToSwaggerName(fqn string, reg *descriptor.Registry) string {
521-
ret, _ := fullyQualifiedNameToSwaggerNameOk(fqn, reg)
522-
return ret
523-
}
524-
525542
// Take in a FQMN or FQEN and return a swagger safe version of the FQMN and
526543
// a boolean indicating if FQMN was properly resolved.
527-
// TODO utrack: bad name?
528-
func fullyQualifiedNameToSwaggerNameOk(fqn string, reg *descriptor.Registry) (string, bool) {
544+
func fullyQualifiedNameToSwaggerName(fqn string, reg *descriptor.Registry) (string, bool) {
529545
registriesSeenMutex.Lock()
530546
defer registriesSeenMutex.Unlock()
531547
if mapping, present := registriesSeen[reg]; present {
@@ -538,6 +554,20 @@ func fullyQualifiedNameToSwaggerNameOk(fqn string, reg *descriptor.Registry) (st
538554
return ret, ok
539555
}
540556

557+
// Lookup message type by location.name and return a swagger-safe version
558+
// of its FQMN.
559+
func lookupMsgAndSwaggerName(location, name string, reg *descriptor.Registry) (*descriptor.Message, string, error) {
560+
msg, err := reg.LookupMsg(location, name)
561+
if err != nil {
562+
return nil, "", err
563+
}
564+
swgName, ok := fullyQualifiedNameToSwaggerName(msg.FQMN(), reg)
565+
if !ok {
566+
return nil, "", fmt.Errorf("can't map swagger name from FQMN '%v'", msg.FQMN())
567+
}
568+
return msg, swgName, nil
569+
}
570+
541571
// registriesSeen is used to memoise calls to resolveFullyQualifiedNameToSwaggerNames so
542572
// we don't repeat it unnecessarily, since it can take some time.
543573
var registriesSeen = map[*descriptor.Registry]map[string]string{}
@@ -778,7 +808,10 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re
778808

779809
wknSchemaCore, isWkn := wktSchemas[meth.RequestType.FQMN()]
780810
if !isWkn {
781-
schema.Ref = fmt.Sprintf("#/definitions/%s", fullyQualifiedNameToSwaggerName(meth.RequestType.FQMN(), reg))
811+
err := schema.setRefFromFQN(meth.RequestType.FQMN(), reg)
812+
if err != nil {
813+
return err
814+
}
782815
} else {
783816
schema.schemaCore = wknSchemaCore
784817

@@ -836,7 +869,10 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re
836869
// well, without a definition
837870
wknSchemaCore, isWkn := wktSchemas[meth.ResponseType.FQMN()]
838871
if !isWkn {
839-
responseSchema.Ref = fmt.Sprintf("#/definitions/%s", fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg))
872+
err := responseSchema.setRefFromFQN(meth.ResponseType.FQMN(), reg)
873+
if err != nil {
874+
return err
875+
}
840876
} else {
841877
responseSchema.schemaCore = wknSchemaCore
842878

@@ -858,7 +894,8 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re
858894
if meth.GetServerStreaming() {
859895
desc += "(streaming responses)"
860896
responseSchema.Type = "object"
861-
responseSchema.Title = fmt.Sprintf("Stream result of %s", fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg))
897+
swgRef, _ := fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)
898+
responseSchema.Title = "Stream result of " + swgRef
862899

863900
props := swaggerSchemaObjectProperties{
864901
keyVal{
@@ -870,7 +907,7 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re
870907
},
871908
},
872909
}
873-
streamErrDef, hasStreamError := fullyQualifiedNameToSwaggerNameOk(".grpc.gateway.runtime.StreamError", reg)
910+
streamErrDef, hasStreamError := fullyQualifiedNameToSwaggerName(".grpc.gateway.runtime.StreamError", reg)
874911
if hasStreamError {
875912
props = append(props, keyVal{
876913
Key: "error",
@@ -900,7 +937,7 @@ func renderServices(services []*descriptor.Service, paths swaggerPathsObject, re
900937
},
901938
}
902939
if !reg.GetDisableDefaultErrors() {
903-
errDef, hasErrDef := fullyQualifiedNameToSwaggerNameOk(".grpc.gateway.runtime.Error", reg)
940+
errDef, hasErrDef := fullyQualifiedNameToSwaggerName(".grpc.gateway.runtime.Error", reg)
904941
if hasErrDef {
905942
// https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responses-object
906943
operationObject.Responses["default"] = swaggerResponseObject{
@@ -1074,9 +1111,9 @@ func applyTemplate(p param) (*swaggerObject, error) {
10741111

10751112
if !p.reg.GetDisableDefaultErrors() {
10761113
// Add the error type to the message map
1077-
runtimeError, err := p.reg.LookupMsg(".grpc.gateway.runtime", "Error")
1114+
runtimeError, swgRef, err := lookupMsgAndSwaggerName(".grpc.gateway.runtime", "Error", p.reg)
10781115
if err == nil {
1079-
messages[fullyQualifiedNameToSwaggerName(".grpc.gateway.runtime.Error", p.reg)] = runtimeError
1116+
messages[swgRef] = runtimeError
10801117
} else {
10811118
// just in case there is an error looking up runtimeError
10821119
glog.Error(err)
@@ -1701,8 +1738,8 @@ func protoJSONSchemaToSwaggerSchemaCore(j *swagger_options.JSONSchema, reg *desc
17011738
ret := schemaCore{}
17021739

17031740
if j.GetRef() != "" {
1704-
swaggerName := fullyQualifiedNameToSwaggerName(j.GetRef(), reg)
1705-
if swaggerName != "" {
1741+
swaggerName, ok := fullyQualifiedNameToSwaggerName(j.GetRef(), reg)
1742+
if ok {
17061743
ret.Ref = "#/definitions/" + swaggerName
17071744
if refs != nil {
17081745
refs[j.GetRef()] = struct{}{}
@@ -1838,19 +1875,24 @@ func addCustomRefs(d swaggerDefinitionsObject, reg *descriptor.Registry, refs re
18381875
msgMap := make(messageMap)
18391876
enumMap := make(enumMap)
18401877
for ref := range refs {
1841-
if _, ok := d[fullyQualifiedNameToSwaggerName(ref, reg)]; ok {
1878+
swgName, swgOk := fullyQualifiedNameToSwaggerName(ref, reg)
1879+
if !swgOk {
1880+
glog.Errorf("can't resolve swagger name from CustomRef '%v'", ref)
1881+
continue
1882+
}
1883+
if _, ok := d[swgName]; ok {
18421884
// Skip already existing definitions
18431885
delete(refs, ref)
18441886
continue
18451887
}
18461888
msg, err := reg.LookupMsg("", ref)
18471889
if err == nil {
1848-
msgMap[fullyQualifiedNameToSwaggerName(ref, reg)] = msg
1890+
msgMap[swgName] = msg
18491891
continue
18501892
}
18511893
enum, err := reg.LookupEnum("", ref)
18521894
if err == nil {
1853-
enumMap[fullyQualifiedNameToSwaggerName(ref, reg)] = enum
1895+
enumMap[swgName] = enum
18541896
continue
18551897
}
18561898

protoc-gen-swagger/genswagger/template_test.go

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2148,7 +2148,6 @@ func TestTemplateWithoutErrorDefinition(t *testing.T) {
21482148
}
21492149

21502150
ref := defRsp.Schema.schemaCore.Ref
2151-
// TODO utrack: is there a better way to find object by ref?
21522151
refName := strings.TrimPrefix(ref, "#/definitions/")
21532152
if refName == "" {
21542153
t.Fatal("created default Error response with empty reflink")

protoc-gen-swagger/genswagger/types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package genswagger
33
import (
44
"bytes"
55
"encoding/json"
6+
"fmt"
67

78
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
89
)
@@ -161,6 +162,15 @@ type schemaCore struct {
161162
Default string `json:"default,omitempty"`
162163
}
163164

165+
func (s *schemaCore) setRefFromFQN(ref string, reg *descriptor.Registry) error {
166+
name, ok := fullyQualifiedNameToSwaggerName(ref, reg)
167+
if !ok {
168+
return fmt.Errorf("setRefFromFQN: can't resolve swagger name from '%v'", ref)
169+
}
170+
s.Ref = fmt.Sprintf("#/definitions/%s", name)
171+
return nil
172+
}
173+
164174
type swaggerItemsObject schemaCore
165175

166176
// http://swagger.io/specification/#responsesObject

0 commit comments

Comments
 (0)