1 package genswagger
2
3 import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "io/ioutil"
8 "math"
9 "net/textproto"
10 "os"
11 "reflect"
12 "regexp"
13 "sort"
14 "strconv"
15 "strings"
16 "sync"
17 "text/template"
18 "time"
19
20 "github.com/golang/glog"
21 "github.com/golang/protobuf/jsonpb"
22 "github.com/golang/protobuf/proto"
23 pbdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
24 structpb "github.com/golang/protobuf/ptypes/struct"
25 "github.com/grpc-ecosystem/grpc-gateway/internal/casing"
26 "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
27 swagger_options "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/options"
28 )
29
30 var wktSchemas = map[string]schemaCore{
31 ".google.protobuf.Timestamp": schemaCore{
32 Type: "string",
33 Format: "date-time",
34 },
35 ".google.protobuf.Duration": schemaCore{
36 Type: "string",
37 },
38 ".google.protobuf.StringValue": schemaCore{
39 Type: "string",
40 },
41 ".google.protobuf.BytesValue": schemaCore{
42 Type: "string",
43 Format: "byte",
44 },
45 ".google.protobuf.Int32Value": schemaCore{
46 Type: "integer",
47 Format: "int32",
48 },
49 ".google.protobuf.UInt32Value": schemaCore{
50 Type: "integer",
51 Format: "int64",
52 },
53 ".google.protobuf.Int64Value": schemaCore{
54 Type: "string",
55 Format: "int64",
56 },
57 ".google.protobuf.UInt64Value": schemaCore{
58 Type: "string",
59 Format: "uint64",
60 },
61 ".google.protobuf.FloatValue": schemaCore{
62 Type: "number",
63 Format: "float",
64 },
65 ".google.protobuf.DoubleValue": schemaCore{
66 Type: "number",
67 Format: "double",
68 },
69 ".google.protobuf.BoolValue": schemaCore{
70 Type: "boolean",
71 },
72 ".google.protobuf.Empty": schemaCore{},
73 ".google.protobuf.Struct": schemaCore{
74 Type: "object",
75 },
76 ".google.protobuf.Value": schemaCore{
77 Type: "object",
78 },
79 ".google.protobuf.ListValue": schemaCore{
80 Type: "array",
81 Items: (*swaggerItemsObject)(&schemaCore{
82 Type: "object",
83 }),
84 },
85 ".google.protobuf.NullValue": schemaCore{
86 Type: "string",
87 },
88 }
89
90 func listEnumNames(enum *descriptor.Enum) (names []string) {
91 for _, value := range enum.GetValue() {
92 names = append(names, value.GetName())
93 }
94 return names
95 }
96
97 func listEnumNumbers(enum *descriptor.Enum) (numbers []string) {
98 for _, value := range enum.GetValue() {
99 numbers = append(numbers, strconv.Itoa(int(value.GetNumber())))
100 }
101 return
102 }
103
104 func getEnumDefault(enum *descriptor.Enum) string {
105 for _, value := range enum.GetValue() {
106 if value.GetNumber() == 0 {
107 return value.GetName()
108 }
109 }
110 return ""
111 }
112
113
114 func messageToQueryParameters(message *descriptor.Message, reg *descriptor.Registry, pathParams []descriptor.Parameter, body *descriptor.Body) (params []swaggerParameterObject, err error) {
115 for _, field := range message.Fields {
116 p, err := queryParams(message, field, "", reg, pathParams, body)
117 if err != nil {
118 return nil, err
119 }
120 params = append(params, p...)
121 }
122 return params, nil
123 }
124
125
126 func queryParams(message *descriptor.Message, field *descriptor.Field, prefix string, reg *descriptor.Registry, pathParams []descriptor.Parameter, body *descriptor.Body) (params []swaggerParameterObject, err error) {
127 return nestedQueryParams(message, field, prefix, reg, pathParams, body, map[string]bool{})
128 }
129
130
131
132
133
134
135
136 func nestedQueryParams(message *descriptor.Message, field *descriptor.Field, prefix string, reg *descriptor.Registry, pathParams []descriptor.Parameter, body *descriptor.Body, touchedIn map[string]bool) (params []swaggerParameterObject, err error) {
137
138 for _, pathParam := range pathParams {
139 if pathParam.Target == field {
140 return nil, nil
141 }
142 }
143
144 if body != nil {
145 if body.FieldPath == nil {
146 return nil, nil
147 }
148 for _, fieldPath := range body.FieldPath {
149 if fieldPath.Target == field {
150 return nil, nil
151 }
152 }
153 }
154 schema := schemaOfField(field, reg, nil)
155 fieldType := field.GetTypeName()
156 if message.File != nil {
157 comments := fieldProtoComments(reg, message, field)
158 if err := updateSwaggerDataFromComments(reg, &schema, message, comments, false); err != nil {
159 return nil, err
160 }
161 }
162
163 isEnum := field.GetType() == pbdescriptor.FieldDescriptorProto_TYPE_ENUM
164 items := schema.Items
165 if schema.Type != "" || isEnum {
166 if schema.Type == "object" {
167 return nil, nil
168 }
169 if items != nil && (items.Type == "" || items.Type == "object") && !isEnum {
170 return nil, nil
171 }
172 desc := schema.Description
173 if schema.Title != "" {
174 desc = strings.TrimSpace(schema.Title + ". " + schema.Description)
175 }
176
177
178 required := false
179 for _, fieldName := range schema.Required {
180 if fieldName == field.GetName() {
181 required = true
182 break
183 }
184 }
185
186 param := swaggerParameterObject{
187 Description: desc,
188 In: "query",
189 Default: schema.Default,
190 Type: schema.Type,
191 Items: schema.Items,
192 Format: schema.Format,
193 Required: required,
194 }
195 if param.Type == "array" {
196 param.CollectionFormat = "multi"
197 }
198
199 if reg.GetUseJSONNamesForFields() {
200 param.Name = prefix + field.GetJsonName()
201 } else {
202 param.Name = prefix + field.GetName()
203 }
204
205 if isEnum {
206 enum, err := reg.LookupEnum("", fieldType)
207 if err != nil {
208 return nil, fmt.Errorf("unknown enum type %s", fieldType)
209 }
210 if items != nil {
211 param.Items = &swaggerItemsObject{
212 Type: "string",
213 Enum: listEnumNames(enum),
214 }
215 if reg.GetEnumsAsInts() {
216 param.Items.Type = "integer"
217 param.Items.Enum = listEnumNumbers(enum)
218 }
219 } else {
220 param.Type = "string"
221 param.Enum = listEnumNames(enum)
222 param.Default = getEnumDefault(enum)
223 if reg.GetEnumsAsInts() {
224 param.Type = "integer"
225 param.Enum = listEnumNumbers(enum)
226 param.Default = "0"
227 }
228 }
229 valueComments := enumValueProtoComments(reg, enum)
230 if valueComments != "" {
231 param.Description = strings.TrimLeft(param.Description+"\n\n "+valueComments, "\n")
232 }
233 }
234 return []swaggerParameterObject{param}, nil
235 }
236
237
238 msg, err := reg.LookupMsg("", fieldType)
239 if err != nil {
240 return nil, fmt.Errorf("unknown message type %s", fieldType)
241 }
242
243
244 isCycle := touchedIn[*msg.Name]
245 if isCycle {
246 return nil, fmt.Errorf("Recursive types are not allowed for query parameters, cycle found on %q", fieldType)
247 }
248
249
250
251
252 touchedOut := make(map[string]bool)
253 for k, v := range touchedIn {
254 touchedOut[k] = v
255 }
256 touchedOut[*msg.Name] = true
257
258 for _, nestedField := range msg.Fields {
259 var fieldName string
260 if reg.GetUseJSONNamesForFields() {
261 fieldName = field.GetJsonName()
262 } else {
263 fieldName = field.GetName()
264 }
265 p, err := nestedQueryParams(msg, nestedField, prefix+fieldName+".", reg, pathParams, body, touchedOut)
266 if err != nil {
267 return nil, err
268 }
269 params = append(params, p...)
270 }
271 return params, nil
272 }
273
274
275 func findServicesMessagesAndEnumerations(s []*descriptor.Service, reg *descriptor.Registry, m messageMap, ms messageMap, e enumMap, refs refMap) {
276 for _, svc := range s {
277 for _, meth := range svc.Methods {
278
279 {
280 swgReqName, ok := fullyQualifiedNameToSwaggerName(meth.RequestType.FQMN(), reg)
281 if !ok {
282 glog.Errorf("couldn't resolve swagger name for FQMN '%v'", meth.RequestType.FQMN())
283 continue
284 }
285 if _, ok := refs[fmt.Sprintf("#/definitions/%s", swgReqName)]; ok {
286 if !skipRenderingRef(meth.RequestType.FQMN()) {
287 m[swgReqName] = meth.RequestType
288 }
289 }
290 }
291
292 swgRspName, ok := fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)
293 if !ok && !skipRenderingRef(meth.ResponseType.FQMN()) {
294 glog.Errorf("couldn't resolve swagger name for FQMN '%v'", meth.ResponseType.FQMN())
295 continue
296 }
297
298 findNestedMessagesAndEnumerations(meth.RequestType, reg, m, e)
299
300 if !skipRenderingRef(meth.ResponseType.FQMN()) {
301 m[swgRspName] = meth.ResponseType
302 if meth.GetServerStreaming() {
303 streamError, runtimeStreamError, err := lookupMsgAndSwaggerName(".grpc.gateway.runtime", "StreamError", reg)
304 if err != nil {
305 glog.Error(err)
306 } else {
307 glog.V(1).Infof("StreamError: %v", streamError)
308 glog.V(1).Infof("StreamError FQMN: %s", runtimeStreamError)
309 m[runtimeStreamError] = streamError
310 findNestedMessagesAndEnumerations(streamError, reg, m, e)
311 }
312 ms[swgRspName] = meth.ResponseType
313 }
314 }
315 findNestedMessagesAndEnumerations(meth.ResponseType, reg, m, e)
316 }
317 }
318 }
319
320
321 func findNestedMessagesAndEnumerations(message *descriptor.Message, reg *descriptor.Registry, m messageMap, e enumMap) {
322
323 for _, t := range message.Fields {
324 fieldType := t.GetTypeName()
325
326 if fieldType != "" {
327 if _, ok := m[fieldType]; !ok {
328 msg, err := reg.LookupMsg("", fieldType)
329 if err != nil {
330 enum, err := reg.LookupEnum("", fieldType)
331 if err != nil {
332 panic(err)
333 }
334 e[fieldType] = enum
335 continue
336 }
337 m[fieldType] = msg
338 findNestedMessagesAndEnumerations(msg, reg, m, e)
339 }
340 }
341 }
342 }
343
344 func skipRenderingRef(refName string) bool {
345 _, ok := wktSchemas[refName]
346 return ok
347 }
348
349 func renderMessagesAsDefinition(messages messageMap, d swaggerDefinitionsObject, reg *descriptor.Registry, customRefs refMap) {
350 for name, msg := range messages {
351 swgName, ok := fullyQualifiedNameToSwaggerName(msg.FQMN(), reg)
352 if !ok {
353 panic(fmt.Sprintf("can't resolve swagger name from '%v'", msg.FQMN()))
354 }
355 if skipRenderingRef(name) {
356 continue
357 }
358
359 if opt := msg.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
360 continue
361 }
362 schema := swaggerSchemaObject{
363 schemaCore: schemaCore{
364 Type: "object",
365 },
366 }
367 msgComments := protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index))
368 if err := updateSwaggerDataFromComments(reg, &schema, msg, msgComments, false); err != nil {
369 panic(err)
370 }
371 opts, err := extractSchemaOptionFromMessageDescriptor(msg.DescriptorProto)
372 if err != nil {
373 panic(err)
374 }
375 if opts != nil {
376 protoSchema := swaggerSchemaFromProtoSchema(opts, reg, customRefs, msg)
377
378
379 schema.ExternalDocs = protoSchema.ExternalDocs
380 schema.ReadOnly = protoSchema.ReadOnly
381 schema.MultipleOf = protoSchema.MultipleOf
382 schema.Maximum = protoSchema.Maximum
383 schema.ExclusiveMaximum = protoSchema.ExclusiveMaximum
384 schema.Minimum = protoSchema.Minimum
385 schema.ExclusiveMinimum = protoSchema.ExclusiveMinimum
386 schema.MaxLength = protoSchema.MaxLength
387 schema.MinLength = protoSchema.MinLength
388 schema.Pattern = protoSchema.Pattern
389 schema.Default = protoSchema.Default
390 schema.MaxItems = protoSchema.MaxItems
391 schema.MinItems = protoSchema.MinItems
392 schema.UniqueItems = protoSchema.UniqueItems
393 schema.MaxProperties = protoSchema.MaxProperties
394 schema.MinProperties = protoSchema.MinProperties
395 schema.Required = protoSchema.Required
396 if protoSchema.schemaCore.Type != "" || protoSchema.schemaCore.Ref != "" {
397 schema.schemaCore = protoSchema.schemaCore
398 }
399 if protoSchema.Title != "" {
400 schema.Title = protoSchema.Title
401 }
402 if protoSchema.Description != "" {
403 schema.Description = protoSchema.Description
404 }
405 if protoSchema.Example != nil {
406 schema.Example = protoSchema.Example
407 }
408 }
409
410 for _, f := range msg.Fields {
411 fieldValue := schemaOfField(f, reg, customRefs)
412 comments := fieldProtoComments(reg, msg, f)
413 if err := updateSwaggerDataFromComments(reg, &fieldValue, f, comments, false); err != nil {
414 panic(err)
415 }
416
417 kv := keyVal{Value: fieldValue}
418 if reg.GetUseJSONNamesForFields() {
419 kv.Key = f.GetJsonName()
420 } else {
421 kv.Key = f.GetName()
422 }
423 if schema.Properties == nil {
424 schema.Properties = &swaggerSchemaObjectProperties{}
425 }
426 *schema.Properties = append(*schema.Properties, kv)
427 }
428 d[swgName] = schema
429 }
430 }
431
432
433 func schemaOfField(f *descriptor.Field, reg *descriptor.Registry, refs refMap) swaggerSchemaObject {
434 const (
435 singular = 0
436 array = 1
437 object = 2
438 )
439 var (
440 core schemaCore
441 aggregate int
442 )
443
444 fd := f.FieldDescriptorProto
445 if m, err := reg.LookupMsg("", f.GetTypeName()); err == nil {
446 if opt := m.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
447 fd = m.GetField()[1]
448 aggregate = object
449 }
450 }
451 if fd.GetLabel() == pbdescriptor.FieldDescriptorProto_LABEL_REPEATED {
452 aggregate = array
453 }
454
455 var props *swaggerSchemaObjectProperties
456
457 switch ft := fd.GetType(); ft {
458 case pbdescriptor.FieldDescriptorProto_TYPE_ENUM, pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE, pbdescriptor.FieldDescriptorProto_TYPE_GROUP:
459 if wktSchema, ok := wktSchemas[fd.GetTypeName()]; ok {
460 core = wktSchema
461
462 if fd.GetTypeName() == ".google.protobuf.Empty" {
463 props = &swaggerSchemaObjectProperties{}
464 }
465 } else {
466 swgRef, ok := fullyQualifiedNameToSwaggerName(fd.GetTypeName(), reg)
467 if !ok {
468 panic(fmt.Sprintf("can't resolve swagger ref from typename '%v'", fd.GetTypeName()))
469 }
470 core = schemaCore{
471 Ref: "#/definitions/" + swgRef,
472 }
473 if refs != nil {
474 refs[fd.GetTypeName()] = struct{}{}
475 }
476 }
477 default:
478 ftype, format, ok := primitiveSchema(ft)
479 if ok {
480 core = schemaCore{Type: ftype, Format: format}
481 } else {
482 core = schemaCore{Type: ft.String(), Format: "UNKNOWN"}
483 }
484 }
485
486 ret := swaggerSchemaObject{}
487
488 switch aggregate {
489 case array:
490 ret = swaggerSchemaObject{
491 schemaCore: schemaCore{
492 Type: "array",
493 Items: (*swaggerItemsObject)(&core),
494 },
495 }
496 case object:
497 ret = swaggerSchemaObject{
498 schemaCore: schemaCore{
499 Type: "object",
500 },
501 AdditionalProperties: &swaggerSchemaObject{Properties: props, schemaCore: core},
502 }
503 default:
504 ret = swaggerSchemaObject{
505 schemaCore: core,
506 Properties: props,
507 }
508 }
509
510 if j, err := extractJSONSchemaFromFieldDescriptor(f.FieldDescriptorProto); err == nil {
511 updateSwaggerObjectFromJSONSchema(&ret, j, reg, f)
512 }
513
514 return ret
515 }
516
517
518
519
520 func primitiveSchema(t pbdescriptor.FieldDescriptorProto_Type) (ftype, format string, ok bool) {
521 switch t {
522 case pbdescriptor.FieldDescriptorProto_TYPE_DOUBLE:
523 return "number", "double", true
524 case pbdescriptor.FieldDescriptorProto_TYPE_FLOAT:
525 return "number", "float", true
526 case pbdescriptor.FieldDescriptorProto_TYPE_INT64:
527 return "string", "int64", true
528 case pbdescriptor.FieldDescriptorProto_TYPE_UINT64:
529
530
531
532
533
534 return "string", "uint64", true
535 case pbdescriptor.FieldDescriptorProto_TYPE_INT32:
536 return "integer", "int32", true
537 case pbdescriptor.FieldDescriptorProto_TYPE_FIXED64:
538
539 return "string", "uint64", true
540 case pbdescriptor.FieldDescriptorProto_TYPE_FIXED32:
541
542 return "integer", "int64", true
543 case pbdescriptor.FieldDescriptorProto_TYPE_BOOL:
544
545 return "boolean", "", true
546 case pbdescriptor.FieldDescriptorProto_TYPE_STRING:
547
548 return "string", "", true
549 case pbdescriptor.FieldDescriptorProto_TYPE_BYTES:
550 return "string", "byte", true
551 case pbdescriptor.FieldDescriptorProto_TYPE_UINT32:
552
553 return "integer", "int64", true
554 case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED32:
555 return "integer", "int32", true
556 case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED64:
557 return "string", "int64", true
558 case pbdescriptor.FieldDescriptorProto_TYPE_SINT32:
559 return "integer", "int32", true
560 case pbdescriptor.FieldDescriptorProto_TYPE_SINT64:
561 return "string", "int64", true
562 default:
563 return "", "", false
564 }
565 }
566
567
568 func renderEnumerationsAsDefinition(enums enumMap, d swaggerDefinitionsObject, reg *descriptor.Registry) {
569 for _, enum := range enums {
570 swgName, ok := fullyQualifiedNameToSwaggerName(enum.FQEN(), reg)
571 if !ok {
572 panic(fmt.Sprintf("can't resolve swagger name from FQEN '%v'", enum.FQEN()))
573 }
574 enumComments := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index))
575
576
577 enumNames := listEnumNames(enum)
578 defaultValue := getEnumDefault(enum)
579 valueComments := enumValueProtoComments(reg, enum)
580 if valueComments != "" {
581 enumComments = strings.TrimLeft(enumComments+"\n\n "+valueComments, "\n")
582 }
583 enumSchemaObject := swaggerSchemaObject{
584 schemaCore: schemaCore{
585 Type: "string",
586 Enum: enumNames,
587 Default: defaultValue,
588 },
589 }
590 if reg.GetEnumsAsInts() {
591 enumSchemaObject.Type = "integer"
592 enumSchemaObject.Format = "int32"
593 enumSchemaObject.Default = "0"
594 enumSchemaObject.Enum = listEnumNumbers(enum)
595 }
596 if err := updateSwaggerDataFromComments(reg, &enumSchemaObject, enum, enumComments, false); err != nil {
597 panic(err)
598 }
599
600 d[swgName] = enumSchemaObject
601 }
602 }
603
604
605
606 func fullyQualifiedNameToSwaggerName(fqn string, reg *descriptor.Registry) (string, bool) {
607 registriesSeenMutex.Lock()
608 defer registriesSeenMutex.Unlock()
609 if mapping, present := registriesSeen[reg]; present {
610 ret, ok := mapping[fqn]
611 return ret, ok
612 }
613 mapping := resolveFullyQualifiedNameToSwaggerNames(append(reg.GetAllFQMNs(), reg.GetAllFQENs()...), reg.GetUseFQNForSwaggerName())
614 registriesSeen[reg] = mapping
615 ret, ok := mapping[fqn]
616 return ret, ok
617 }
618
619
620
621 func lookupMsgAndSwaggerName(location, name string, reg *descriptor.Registry) (*descriptor.Message, string, error) {
622 msg, err := reg.LookupMsg(location, name)
623 if err != nil {
624 return nil, "", err
625 }
626 swgName, ok := fullyQualifiedNameToSwaggerName(msg.FQMN(), reg)
627 if !ok {
628 return nil, "", fmt.Errorf("can't map swagger name from FQMN '%v'", msg.FQMN())
629 }
630 return msg, swgName, nil
631 }
632
633
634
635 var registriesSeen = map[*descriptor.Registry]map[string]string{}
636 var registriesSeenMutex sync.Mutex
637
638
639
640
641
642
643
644
645 func resolveFullyQualifiedNameToSwaggerNames(messages []string, useFQNForSwaggerName bool) map[string]string {
646 packagesByDepth := make(map[int][][]string)
647 uniqueNames := make(map[string]string)
648
649 hierarchy := func(pkg string) []string {
650 return strings.Split(pkg, ".")
651 }
652
653 for _, p := range messages {
654 h := hierarchy(p)
655 for depth := range h {
656 if _, ok := packagesByDepth[depth]; !ok {
657 packagesByDepth[depth] = make([][]string, 0)
658 }
659 packagesByDepth[depth] = append(packagesByDepth[depth], h[len(h)-depth:])
660 }
661 }
662
663 count := func(list [][]string, item []string) int {
664 i := 0
665 for _, element := range list {
666 if reflect.DeepEqual(element, item) {
667 i++
668 }
669 }
670 return i
671 }
672
673 for _, p := range messages {
674 if useFQNForSwaggerName {
675
676 uniqueNames[p] = p[1:]
677 } else {
678 h := hierarchy(p)
679 for depth := 0; depth < len(h); depth++ {
680 if count(packagesByDepth[depth], h[len(h)-depth:]) == 1 {
681 uniqueNames[p] = strings.Join(h[len(h)-depth-1:], "")
682 break
683 }
684 if depth == len(h)-1 {
685 uniqueNames[p] = strings.Join(h, "")
686 }
687 }
688 }
689 }
690 return uniqueNames
691 }
692
693 var canRegexp = regexp.MustCompile("{([a-zA-Z][a-zA-Z0-9_.]*).*}")
694
695
696 func templateToSwaggerPath(path string, reg *descriptor.Registry, fields []*descriptor.Field, msgs []*descriptor.Message) string {
697
698
699
700
701
702 var parts []string
703 depth := 0
704 buffer := ""
705 jsonBuffer := ""
706 for _, char := range path {
707 switch char {
708 case '{':
709
710 depth++
711 buffer += string(char)
712 jsonBuffer = ""
713 jsonBuffer += string(char)
714 break
715 case '}':
716 if depth == 0 {
717 panic("Encountered } without matching { before it.")
718 }
719
720 depth--
721 buffer += string(char)
722 if reg.GetUseJSONNamesForFields() &&
723 len(jsonBuffer) > 1 {
724 jsonSnakeCaseName := string(jsonBuffer[1:])
725 jsonCamelCaseName := string(lowerCamelCase(jsonSnakeCaseName, fields, msgs))
726 prev := string(buffer[:len(buffer)-len(jsonSnakeCaseName)-2])
727 buffer = strings.Join([]string{prev, "{", jsonCamelCaseName, "}"}, "")
728 jsonBuffer = ""
729 }
730 case '/':
731 if depth == 0 {
732 parts = append(parts, buffer)
733 buffer = ""
734
735
736 continue
737 }
738 buffer += string(char)
739 jsonBuffer += string(char)
740 default:
741 buffer += string(char)
742 jsonBuffer += string(char)
743 break
744 }
745 }
746
747
748 parts = append(parts, buffer)
749
750
751
752
753 for index, part := range parts {
754
755 prefix := canRegexp.ReplaceAllString(part, "$1")
756 if isResourceName(prefix) {
757 continue
758 }
759 parts[index] = canRegexp.ReplaceAllString(part, "{$1}")
760 }
761
762 return strings.Join(parts, "/")
763 }
764
765 func isResourceName(prefix string) bool {
766 words := strings.Split(prefix, ".")
767 l := len(words)
768 field := words[l-1]
769 words = strings.Split(field, ":")
770 field = words[0]
771 return field == "parent" || field == "name"
772 }
773
774 func renderServices(services []*descriptor.Service, paths swaggerPathsObject, reg *descriptor.Registry, requestResponseRefs, customRefs refMap, msgs []*descriptor.Message) error {
775
776 svcBaseIdx := 0
777 var lastFile *descriptor.File = nil
778 for svcIdx, svc := range services {
779 if svc.File != lastFile {
780 lastFile = svc.File
781 svcBaseIdx = svcIdx
782 }
783 for methIdx, meth := range svc.Methods {
784 for bIdx, b := range meth.Bindings {
785
786 parameters := swaggerParametersObject{}
787 for _, parameter := range b.PathParams {
788
789 var paramType, paramFormat, desc, collectionFormat, defaultValue string
790 var enumNames []string
791 var items *swaggerItemsObject
792 var minItems *int
793 switch pt := parameter.Target.GetType(); pt {
794 case pbdescriptor.FieldDescriptorProto_TYPE_GROUP, pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE:
795 if descriptor.IsWellKnownType(parameter.Target.GetTypeName()) {
796 if parameter.IsRepeated() {
797 return fmt.Errorf("only primitive and enum types are allowed in repeated path parameters")
798 }
799 schema := schemaOfField(parameter.Target, reg, customRefs)
800 paramType = schema.Type
801 paramFormat = schema.Format
802 desc = schema.Description
803 defaultValue = schema.Default
804 } else {
805 return fmt.Errorf("only primitive and well-known types are allowed in path parameters")
806 }
807 case pbdescriptor.FieldDescriptorProto_TYPE_ENUM:
808 enum, err := reg.LookupEnum("", parameter.Target.GetTypeName())
809 if err != nil {
810 return err
811 }
812 paramType = "string"
813 paramFormat = ""
814 enumNames = listEnumNames(enum)
815 if reg.GetEnumsAsInts() {
816 paramType = "integer"
817 paramFormat = ""
818 enumNames = listEnumNumbers(enum)
819 }
820 schema := schemaOfField(parameter.Target, reg, customRefs)
821 desc = schema.Description
822 defaultValue = schema.Default
823 default:
824 var ok bool
825 paramType, paramFormat, ok = primitiveSchema(pt)
826 if !ok {
827 return fmt.Errorf("unknown field type %v", pt)
828 }
829
830 schema := schemaOfField(parameter.Target, reg, customRefs)
831 desc = schema.Description
832 defaultValue = schema.Default
833 }
834
835 if parameter.IsRepeated() {
836 core := schemaCore{Type: paramType, Format: paramFormat}
837 if parameter.IsEnum() {
838 var s []string
839 core.Enum = enumNames
840 enumNames = s
841 }
842 items = (*swaggerItemsObject)(&core)
843 paramType = "array"
844 paramFormat = ""
845 collectionFormat = reg.GetRepeatedPathParamSeparatorName()
846 minItems = new(int)
847 *minItems = 1
848 }
849
850 if desc == "" {
851 desc = fieldProtoComments(reg, parameter.Target.Message, parameter.Target)
852 }
853 parameterString := parameter.String()
854 if reg.GetUseJSONNamesForFields() {
855 parameterString = lowerCamelCase(parameterString, meth.RequestType.Fields, msgs)
856 }
857 parameters = append(parameters, swaggerParameterObject{
858 Name: parameterString,
859 Description: desc,
860 In: "path",
861 Required: true,
862 Default: defaultValue,
863
864 Type: paramType,
865 Format: paramFormat,
866 Enum: enumNames,
867 Items: items,
868 CollectionFormat: collectionFormat,
869 MinItems: minItems,
870 })
871 }
872
873 if b.Body != nil {
874 var schema swaggerSchemaObject
875 desc := ""
876
877 if len(b.Body.FieldPath) == 0 {
878 schema = swaggerSchemaObject{
879 schemaCore: schemaCore{},
880 }
881
882 wknSchemaCore, isWkn := wktSchemas[meth.RequestType.FQMN()]
883 if !isWkn {
884 err := schema.setRefFromFQN(meth.RequestType.FQMN(), reg)
885 if err != nil {
886 return err
887 }
888 } else {
889 schema.schemaCore = wknSchemaCore
890
891
892 if meth.RequestType.FQMN() == ".google.protobuf.Empty" {
893 schema.Properties = &swaggerSchemaObjectProperties{}
894 }
895 }
896 } else {
897 lastField := b.Body.FieldPath[len(b.Body.FieldPath)-1]
898 schema = schemaOfField(lastField.Target, reg, customRefs)
899 if schema.Description != "" {
900 desc = schema.Description
901 } else {
902 desc = fieldProtoComments(reg, lastField.Target.Message, lastField.Target)
903 }
904 }
905
906 if meth.GetClientStreaming() {
907 desc += " (streaming inputs)"
908 }
909 parameters = append(parameters, swaggerParameterObject{
910 Name: "body",
911 Description: desc,
912 In: "body",
913 Required: true,
914 Schema: &schema,
915 })
916
917 queryParams, err := messageToQueryParameters(meth.RequestType, reg, b.PathParams, b.Body)
918 if err != nil {
919 return err
920 }
921 parameters = append(parameters, queryParams...)
922 } else if b.HTTPMethod == "GET" || b.HTTPMethod == "DELETE" {
923
924 queryParams, err := messageToQueryParameters(meth.RequestType, reg, b.PathParams, b.Body)
925 if err != nil {
926 return err
927 }
928 parameters = append(parameters, queryParams...)
929 }
930
931 pathItemObject, ok := paths[templateToSwaggerPath(b.PathTmpl.Template, reg, meth.RequestType.Fields, msgs)]
932 if !ok {
933 pathItemObject = swaggerPathItemObject{}
934 }
935
936 methProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.ServiceDescriptorProto)(nil)), "Method")
937 desc := "A successful response."
938 var responseSchema swaggerSchemaObject
939
940 if b.ResponseBody == nil || len(b.ResponseBody.FieldPath) == 0 {
941 responseSchema = swaggerSchemaObject{
942 schemaCore: schemaCore{},
943 }
944
945
946
947
948
949 wknSchemaCore, isWkn := wktSchemas[meth.ResponseType.FQMN()]
950 if !isWkn {
951 err := responseSchema.setRefFromFQN(meth.ResponseType.FQMN(), reg)
952 if err != nil {
953 return err
954 }
955 } else {
956 responseSchema.schemaCore = wknSchemaCore
957
958
959 if meth.ResponseType.FQMN() == ".google.protobuf.Empty" {
960 responseSchema.Properties = &swaggerSchemaObjectProperties{}
961 }
962 }
963 } else {
964
965 lastField := b.ResponseBody.FieldPath[len(b.ResponseBody.FieldPath)-1]
966 responseSchema = schemaOfField(lastField.Target, reg, customRefs)
967 if responseSchema.Description != "" {
968 desc = responseSchema.Description
969 } else {
970 desc = fieldProtoComments(reg, lastField.Target.Message, lastField.Target)
971 }
972 }
973 if meth.GetServerStreaming() {
974 desc += "(streaming responses)"
975 responseSchema.Type = "object"
976 swgRef, _ := fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)
977 responseSchema.Title = "Stream result of " + swgRef
978
979 props := swaggerSchemaObjectProperties{
980 keyVal{
981 Key: "result",
982 Value: swaggerSchemaObject{
983 schemaCore: schemaCore{
984 Ref: responseSchema.Ref,
985 },
986 },
987 },
988 }
989 streamErrDef, hasStreamError := fullyQualifiedNameToSwaggerName(".grpc.gateway.runtime.StreamError", reg)
990 if hasStreamError {
991 props = append(props, keyVal{
992 Key: "error",
993 Value: swaggerSchemaObject{
994 schemaCore: schemaCore{
995 Ref: fmt.Sprintf("#/definitions/%s", streamErrDef)},
996 },
997 })
998 }
999 responseSchema.Properties = &props
1000 responseSchema.Ref = ""
1001 }
1002
1003 tag := svc.GetName()
1004 if pkg := svc.File.GetPackage(); pkg != "" && reg.IsIncludePackageInTags() {
1005 tag = pkg + "." + tag
1006 }
1007 operationObject := &swaggerOperationObject{
1008 Tags: []string{tag},
1009 Parameters: parameters,
1010 Responses: swaggerResponsesObject{
1011 "200": swaggerResponseObject{
1012 Description: desc,
1013 Schema: responseSchema,
1014 Headers: swaggerHeadersObject{},
1015 },
1016 },
1017 }
1018 if !reg.GetDisableDefaultErrors() {
1019 errDef, hasErrDef := fullyQualifiedNameToSwaggerName(".grpc.gateway.runtime.Error", reg)
1020 if hasErrDef {
1021
1022 operationObject.Responses["default"] = swaggerResponseObject{
1023 Description: "An unexpected error response.",
1024 Schema: swaggerSchemaObject{
1025 schemaCore: schemaCore{
1026 Ref: fmt.Sprintf("#/definitions/%s", errDef),
1027 },
1028 },
1029 }
1030 }
1031 }
1032 operationObject.OperationID = fmt.Sprintf("%s_%s", svc.GetName(), meth.GetName())
1033 if reg.GetSimpleOperationIDs() {
1034 operationObject.OperationID = fmt.Sprintf("%s", meth.GetName())
1035 }
1036 if bIdx != 0 {
1037
1038 operationObject.OperationID += strconv.Itoa(bIdx + 1)
1039 }
1040
1041
1042 for _, param := range operationObject.Parameters {
1043 if param.Schema != nil && param.Schema.Ref != "" {
1044 requestResponseRefs[param.Schema.Ref] = struct{}{}
1045 }
1046 }
1047
1048 methComments := protoComments(reg, svc.File, nil, "Service", int32(svcIdx-svcBaseIdx), methProtoPath, int32(methIdx))
1049 if err := updateSwaggerDataFromComments(reg, operationObject, meth, methComments, false); err != nil {
1050 panic(err)
1051 }
1052
1053 opts, err := extractOperationOptionFromMethodDescriptor(meth.MethodDescriptorProto)
1054 if opts != nil {
1055 if err != nil {
1056 panic(err)
1057 }
1058 operationObject.ExternalDocs = protoExternalDocumentationToSwaggerExternalDocumentation(opts.ExternalDocs, reg, meth)
1059
1060 operationObject.Deprecated = opts.Deprecated
1061
1062 if opts.Summary != "" {
1063 operationObject.Summary = opts.Summary
1064 }
1065 if opts.Description != "" {
1066 operationObject.Description = opts.Description
1067 }
1068 if len(opts.Tags) > 0 {
1069 operationObject.Tags = make([]string, len(opts.Tags))
1070 copy(operationObject.Tags, opts.Tags)
1071 }
1072 if opts.OperationId != "" {
1073 operationObject.OperationID = opts.OperationId
1074 }
1075 if opts.Security != nil {
1076 newSecurity := []swaggerSecurityRequirementObject{}
1077 if operationObject.Security != nil {
1078 newSecurity = *operationObject.Security
1079 }
1080 for _, secReq := range opts.Security {
1081 newSecReq := swaggerSecurityRequirementObject{}
1082 for secReqKey, secReqValue := range secReq.SecurityRequirement {
1083 if secReqValue == nil {
1084 continue
1085 }
1086
1087 newSecReqValue := make([]string, len(secReqValue.Scope))
1088 copy(newSecReqValue, secReqValue.Scope)
1089 newSecReq[secReqKey] = newSecReqValue
1090 }
1091
1092 if len(newSecReq) > 0 {
1093 newSecurity = append(newSecurity, newSecReq)
1094 }
1095 }
1096 operationObject.Security = &newSecurity
1097 }
1098 if opts.Responses != nil {
1099 for name, resp := range opts.Responses {
1100
1101 respObj := operationObject.Responses[name]
1102 if resp.Description != "" {
1103 respObj.Description = resp.Description
1104 }
1105 if resp.Schema != nil {
1106 respObj.Schema = swaggerSchemaFromProtoSchema(resp.Schema, reg, customRefs, meth)
1107 }
1108 if resp.Examples != nil {
1109 respObj.Examples = swaggerExamplesFromProtoExamples(resp.Examples)
1110 }
1111 if resp.Headers != nil {
1112 hdrs, err := processHeaders(resp.Headers)
1113 if err != nil {
1114 return err
1115 }
1116 respObj.Headers = hdrs
1117 }
1118 if resp.Extensions != nil {
1119 exts, err := processExtensions(resp.Extensions)
1120 if err != nil {
1121 return err
1122 }
1123 respObj.extensions = exts
1124 }
1125 operationObject.Responses[name] = respObj
1126 }
1127 }
1128
1129 if opts.Extensions != nil {
1130 exts, err := processExtensions(opts.Extensions)
1131 if err != nil {
1132 return err
1133 }
1134 operationObject.extensions = exts
1135 }
1136
1137 if len(opts.Produces) > 0 {
1138 operationObject.Produces = make([]string, len(opts.Produces))
1139 copy(operationObject.Produces, opts.Produces)
1140 }
1141
1142
1143 }
1144
1145 switch b.HTTPMethod {
1146 case "DELETE":
1147 pathItemObject.Delete = operationObject
1148 break
1149 case "GET":
1150 pathItemObject.Get = operationObject
1151 break
1152 case "POST":
1153 pathItemObject.Post = operationObject
1154 break
1155 case "PUT":
1156 pathItemObject.Put = operationObject
1157 break
1158 case "PATCH":
1159 pathItemObject.Patch = operationObject
1160 break
1161 }
1162 paths[templateToSwaggerPath(b.PathTmpl.Template, reg, meth.RequestType.Fields, msgs)] = pathItemObject
1163 }
1164 }
1165 }
1166
1167
1168 return nil
1169 }
1170
1171
1172 func applyTemplate(p param) (*swaggerObject, error) {
1173
1174
1175 s := swaggerObject{
1176
1177 Swagger: "2.0",
1178 Consumes: []string{"application/json"},
1179 Produces: []string{"application/json"},
1180 Paths: make(swaggerPathsObject),
1181 Definitions: make(swaggerDefinitionsObject),
1182 Info: swaggerInfoObject{
1183 Title: *p.File.Name,
1184 Version: "version not set",
1185 },
1186 }
1187
1188
1189
1190
1191 requestResponseRefs, customRefs := refMap{}, refMap{}
1192 if err := renderServices(p.Services, s.Paths, p.reg, requestResponseRefs, customRefs, p.Messages); err != nil {
1193 panic(err)
1194 }
1195
1196 messages := messageMap{}
1197 streamingMessages := messageMap{}
1198 enums := enumMap{}
1199
1200 if !p.reg.GetDisableDefaultErrors() {
1201
1202 runtimeError, swgRef, err := lookupMsgAndSwaggerName(".grpc.gateway.runtime", "Error", p.reg)
1203 if err == nil {
1204 messages[swgRef] = runtimeError
1205 } else {
1206
1207 glog.Error(err)
1208 }
1209 }
1210
1211
1212
1213 findServicesMessagesAndEnumerations(p.Services, p.reg, messages, streamingMessages, enums, requestResponseRefs)
1214 renderMessagesAsDefinition(messages, s.Definitions, p.reg, customRefs)
1215 renderEnumerationsAsDefinition(enums, s.Definitions, p.reg)
1216
1217
1218 packageProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "Package")
1219 packageComments := protoComments(p.reg, p.File, nil, "Package", packageProtoPath)
1220 if err := updateSwaggerDataFromComments(p.reg, &s, p, packageComments, true); err != nil {
1221 panic(err)
1222 }
1223
1224
1225 spb, err := extractSwaggerOptionFromFileDescriptor(p.FileDescriptorProto)
1226 if err != nil {
1227 panic(err)
1228 }
1229 if spb != nil {
1230 if spb.Swagger != "" {
1231 s.Swagger = spb.Swagger
1232 }
1233 if spb.Info != nil {
1234 if spb.Info.Title != "" {
1235 s.Info.Title = spb.Info.Title
1236 }
1237 if spb.Info.Description != "" {
1238 s.Info.Description = spb.Info.Description
1239 }
1240 if spb.Info.TermsOfService != "" {
1241 s.Info.TermsOfService = spb.Info.TermsOfService
1242 }
1243 if spb.Info.Version != "" {
1244 s.Info.Version = spb.Info.Version
1245 }
1246 if spb.Info.Contact != nil {
1247 if s.Info.Contact == nil {
1248 s.Info.Contact = &swaggerContactObject{}
1249 }
1250 if spb.Info.Contact.Name != "" {
1251 s.Info.Contact.Name = spb.Info.Contact.Name
1252 }
1253 if spb.Info.Contact.Url != "" {
1254 s.Info.Contact.URL = spb.Info.Contact.Url
1255 }
1256 if spb.Info.Contact.Email != "" {
1257 s.Info.Contact.Email = spb.Info.Contact.Email
1258 }
1259 }
1260 if spb.Info.License != nil {
1261 if s.Info.License == nil {
1262 s.Info.License = &swaggerLicenseObject{}
1263 }
1264 if spb.Info.License.Name != "" {
1265 s.Info.License.Name = spb.Info.License.Name
1266 }
1267 if spb.Info.License.Url != "" {
1268 s.Info.License.URL = spb.Info.License.Url
1269 }
1270 }
1271 if spb.Info.Extensions != nil {
1272 exts, err := processExtensions(spb.Info.Extensions)
1273 if err != nil {
1274 return nil, err
1275 }
1276 s.Info.extensions = exts
1277 }
1278 }
1279 if spb.Host != "" {
1280 s.Host = spb.Host
1281 }
1282 if spb.BasePath != "" {
1283 s.BasePath = spb.BasePath
1284 }
1285 if len(spb.Schemes) > 0 {
1286 s.Schemes = make([]string, len(spb.Schemes))
1287 for i, scheme := range spb.Schemes {
1288 s.Schemes[i] = strings.ToLower(scheme.String())
1289 }
1290 }
1291 if len(spb.Consumes) > 0 {
1292 s.Consumes = make([]string, len(spb.Consumes))
1293 copy(s.Consumes, spb.Consumes)
1294 }
1295 if len(spb.Produces) > 0 {
1296 s.Produces = make([]string, len(spb.Produces))
1297 copy(s.Produces, spb.Produces)
1298 }
1299 if spb.SecurityDefinitions != nil && spb.SecurityDefinitions.Security != nil {
1300 if s.SecurityDefinitions == nil {
1301 s.SecurityDefinitions = swaggerSecurityDefinitionsObject{}
1302 }
1303 for secDefKey, secDefValue := range spb.SecurityDefinitions.Security {
1304 var newSecDefValue swaggerSecuritySchemeObject
1305 if oldSecDefValue, ok := s.SecurityDefinitions[secDefKey]; !ok {
1306 newSecDefValue = swaggerSecuritySchemeObject{}
1307 } else {
1308 newSecDefValue = oldSecDefValue
1309 }
1310 if secDefValue.Type != swagger_options.SecurityScheme_TYPE_INVALID {
1311 switch secDefValue.Type {
1312 case swagger_options.SecurityScheme_TYPE_BASIC:
1313 newSecDefValue.Type = "basic"
1314 case swagger_options.SecurityScheme_TYPE_API_KEY:
1315 newSecDefValue.Type = "apiKey"
1316 case swagger_options.SecurityScheme_TYPE_OAUTH2:
1317 newSecDefValue.Type = "oauth2"
1318 }
1319 }
1320 if secDefValue.Description != "" {
1321 newSecDefValue.Description = secDefValue.Description
1322 }
1323 if secDefValue.Name != "" {
1324 newSecDefValue.Name = secDefValue.Name
1325 }
1326 if secDefValue.In != swagger_options.SecurityScheme_IN_INVALID {
1327 switch secDefValue.In {
1328 case swagger_options.SecurityScheme_IN_QUERY:
1329 newSecDefValue.In = "query"
1330 case swagger_options.SecurityScheme_IN_HEADER:
1331 newSecDefValue.In = "header"
1332 }
1333 }
1334 if secDefValue.Flow != swagger_options.SecurityScheme_FLOW_INVALID {
1335 switch secDefValue.Flow {
1336 case swagger_options.SecurityScheme_FLOW_IMPLICIT:
1337 newSecDefValue.Flow = "implicit"
1338 case swagger_options.SecurityScheme_FLOW_PASSWORD:
1339 newSecDefValue.Flow = "password"
1340 case swagger_options.SecurityScheme_FLOW_APPLICATION:
1341 newSecDefValue.Flow = "application"
1342 case swagger_options.SecurityScheme_FLOW_ACCESS_CODE:
1343 newSecDefValue.Flow = "accessCode"
1344 }
1345 }
1346 if secDefValue.AuthorizationUrl != "" {
1347 newSecDefValue.AuthorizationURL = secDefValue.AuthorizationUrl
1348 }
1349 if secDefValue.TokenUrl != "" {
1350 newSecDefValue.TokenURL = secDefValue.TokenUrl
1351 }
1352 if secDefValue.Scopes != nil {
1353 if newSecDefValue.Scopes == nil {
1354 newSecDefValue.Scopes = swaggerScopesObject{}
1355 }
1356 for scopeKey, scopeDesc := range secDefValue.Scopes.Scope {
1357 newSecDefValue.Scopes[scopeKey] = scopeDesc
1358 }
1359 }
1360 if secDefValue.Extensions != nil {
1361 exts, err := processExtensions(secDefValue.Extensions)
1362 if err != nil {
1363 return nil, err
1364 }
1365 newSecDefValue.extensions = exts
1366 }
1367 s.SecurityDefinitions[secDefKey] = newSecDefValue
1368 }
1369 }
1370 if spb.Security != nil {
1371 newSecurity := []swaggerSecurityRequirementObject{}
1372 if s.Security == nil {
1373 newSecurity = []swaggerSecurityRequirementObject{}
1374 } else {
1375 newSecurity = s.Security
1376 }
1377 for _, secReq := range spb.Security {
1378 newSecReq := swaggerSecurityRequirementObject{}
1379 for secReqKey, secReqValue := range secReq.SecurityRequirement {
1380 newSecReqValue := make([]string, len(secReqValue.Scope))
1381 copy(newSecReqValue, secReqValue.Scope)
1382 newSecReq[secReqKey] = newSecReqValue
1383 }
1384 newSecurity = append(newSecurity, newSecReq)
1385 }
1386 s.Security = newSecurity
1387 }
1388 s.ExternalDocs = protoExternalDocumentationToSwaggerExternalDocumentation(spb.ExternalDocs, p.reg, spb)
1389
1390
1391 if spb.Responses != nil {
1392 for _, verbs := range s.Paths {
1393 var maps []swaggerResponsesObject
1394 if verbs.Delete != nil {
1395 maps = append(maps, verbs.Delete.Responses)
1396 }
1397 if verbs.Get != nil {
1398 maps = append(maps, verbs.Get.Responses)
1399 }
1400 if verbs.Post != nil {
1401 maps = append(maps, verbs.Post.Responses)
1402 }
1403 if verbs.Put != nil {
1404 maps = append(maps, verbs.Put.Responses)
1405 }
1406 if verbs.Patch != nil {
1407 maps = append(maps, verbs.Patch.Responses)
1408 }
1409
1410 for k, v := range spb.Responses {
1411 for _, respMap := range maps {
1412 if _, ok := respMap[k]; ok {
1413
1414 continue
1415 }
1416 respMap[k] = swaggerResponseObject{
1417 Description: v.Description,
1418 Schema: swaggerSchemaFromProtoSchema(v.Schema, p.reg, customRefs, nil),
1419 Examples: swaggerExamplesFromProtoExamples(v.Examples),
1420 }
1421 }
1422 }
1423 }
1424 }
1425
1426 if spb.Extensions != nil {
1427 exts, err := processExtensions(spb.Extensions)
1428 if err != nil {
1429 return nil, err
1430 }
1431 s.extensions = exts
1432 }
1433
1434
1435
1436 }
1437
1438
1439
1440 addCustomRefs(s.Definitions, p.reg, customRefs)
1441
1442 return &s, nil
1443 }
1444
1445 func processExtensions(inputExts map[string]*structpb.Value) ([]extension, error) {
1446 exts := []extension{}
1447 for k, v := range inputExts {
1448 if !strings.HasPrefix(k, "x-") {
1449 return nil, fmt.Errorf("Extension keys need to start with \"x-\": %q", k)
1450 }
1451 ext, err := (&jsonpb.Marshaler{Indent: " "}).MarshalToString(v)
1452 if err != nil {
1453 return nil, err
1454 }
1455 exts = append(exts, extension{key: k, value: json.RawMessage(ext)})
1456 }
1457 sort.Slice(exts, func(i, j int) bool { return exts[i].key < exts[j].key })
1458 return exts, nil
1459 }
1460
1461 func validateHeaderTypeAndFormat(headerType string, format string) error {
1462
1463
1464
1465 switch headerType {
1466
1467
1468
1469 case "string":
1470 return nil
1471 case "number":
1472 switch format {
1473 case "uint",
1474 "uint8",
1475 "uint16",
1476 "uint32",
1477 "uint64",
1478 "int",
1479 "int8",
1480 "int16",
1481 "int32",
1482 "int64",
1483 "float",
1484 "float32",
1485 "float64",
1486 "complex64",
1487 "complex128",
1488 "double",
1489 "byte",
1490 "rune",
1491 "uintptr",
1492 "":
1493 return nil
1494 default:
1495 return fmt.Errorf("the provided format %q is not a valid extension of the type %q", format, headerType)
1496 }
1497 case "integer":
1498 switch format {
1499 case "uint",
1500 "uint8",
1501 "uint16",
1502 "uint32",
1503 "uint64",
1504 "int",
1505 "int8",
1506 "int16",
1507 "int32",
1508 "int64",
1509 "":
1510 return nil
1511 default:
1512 return fmt.Errorf("the provided format %q is not a valid extension of the type %q", format, headerType)
1513 }
1514 case "boolean":
1515 return nil
1516 }
1517 return fmt.Errorf("the provided header type %q is not supported", headerType)
1518 }
1519
1520 func validateDefaultValueTypeAndFormat(headerType string, defaultValue string, format string) error {
1521 switch headerType {
1522 case "string":
1523 if !isQuotedString(defaultValue) {
1524 return fmt.Errorf("the provided default value %q does not match provider type %q, or is not properly quoted with escaped quotations", defaultValue, headerType)
1525 }
1526 switch format {
1527 case "date-time":
1528 unquoteTime := strings.Trim(defaultValue, `"`)
1529 _, err := time.Parse(time.RFC3339, unquoteTime)
1530 if err != nil {
1531 return fmt.Errorf("the provided default value %q is not a valid RFC3339 date-time string", defaultValue)
1532 }
1533 case "date":
1534 const (
1535 layoutRFC3339Date = "2006-01-02"
1536 )
1537 unquoteDate := strings.Trim(defaultValue, `"`)
1538 _, err := time.Parse(layoutRFC3339Date, unquoteDate)
1539 if err != nil {
1540 return fmt.Errorf("the provided default value %q is not a valid RFC3339 date-time string", defaultValue)
1541 }
1542 }
1543 case "number":
1544 err := isJSONNumber(defaultValue, headerType)
1545 if err != nil {
1546 return err
1547 }
1548 case "integer":
1549 switch format {
1550 case "int32":
1551 _, err := strconv.ParseInt(defaultValue, 0, 32)
1552 if err != nil {
1553 return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
1554 }
1555 case "uint32":
1556 _, err := strconv.ParseUint(defaultValue, 0, 32)
1557 if err != nil {
1558 return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
1559 }
1560 case "int64":
1561 _, err := strconv.ParseInt(defaultValue, 0, 64)
1562 if err != nil {
1563 return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
1564 }
1565 case "uint64":
1566 _, err := strconv.ParseUint(defaultValue, 0, 64)
1567 if err != nil {
1568 return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
1569 }
1570 default:
1571 _, err := strconv.ParseInt(defaultValue, 0, 64)
1572 if err != nil {
1573 return fmt.Errorf("the provided default value %q does not match provided type %q", defaultValue, headerType)
1574 }
1575 }
1576 case "boolean":
1577 if !isBool(defaultValue) {
1578 return fmt.Errorf("the provided default value %q does not match provider type %q", defaultValue, headerType)
1579 }
1580 }
1581 return nil
1582 }
1583
1584 func isQuotedString(s string) bool {
1585 return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
1586 }
1587
1588 func isJSONNumber(s string, t string) error {
1589 val, err := strconv.ParseFloat(s, 64)
1590 if err != nil {
1591 return fmt.Errorf("the provided default value %q does not match provider type %q", s, t)
1592 }
1593
1594
1595 if math.IsInf(val, 0) || math.IsNaN(val) {
1596 return fmt.Errorf("the provided number %q is not a valid JSON number", s)
1597 }
1598
1599 return nil
1600 }
1601
1602 func isBool(s string) bool {
1603
1604
1605
1606 return s == "true" || s == "false"
1607 }
1608
1609 func processHeaders(inputHdrs map[string]*swagger_options.Header) (swaggerHeadersObject, error) {
1610 hdrs := map[string]swaggerHeaderObject{}
1611 for k, v := range inputHdrs {
1612 header := textproto.CanonicalMIMEHeaderKey(k)
1613 ret := swaggerHeaderObject{
1614 Description: v.Description,
1615 Format: v.Format,
1616 Pattern: v.Pattern,
1617 }
1618 err := validateHeaderTypeAndFormat(v.Type, v.Format)
1619 if err != nil {
1620 return nil, err
1621 }
1622 ret.Type = v.Type
1623 if v.Default != "" {
1624 err := validateDefaultValueTypeAndFormat(v.Type, v.Default, v.Format)
1625 if err != nil {
1626 return nil, err
1627 }
1628 ret.Default = json.RawMessage(v.Default)
1629 }
1630 hdrs[header] = ret
1631 }
1632 return hdrs, nil
1633 }
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647 func updateSwaggerDataFromComments(reg *descriptor.Registry, swaggerObject interface{}, data interface{}, comment string, isPackageObject bool) error {
1648 if len(comment) == 0 {
1649 return nil
1650 }
1651
1652
1653 if reg.GetUseGoTemplate() {
1654 comment = goTemplateComments(comment, data, reg)
1655 }
1656
1657
1658 swaggerObjectValue := reflect.ValueOf(swaggerObject)
1659 infoObjectValue := swaggerObjectValue.Elem().FieldByName("Info")
1660 if !infoObjectValue.CanSet() {
1661
1662
1663 infoObjectValue = swaggerObjectValue.Elem()
1664 }
1665
1666
1667 summaryValue := infoObjectValue.FieldByName("Summary")
1668 descriptionValue := infoObjectValue.FieldByName("Description")
1669 readOnlyValue := infoObjectValue.FieldByName("ReadOnly")
1670
1671 if readOnlyValue.Kind() == reflect.Bool && readOnlyValue.CanSet() && strings.Contains(comment, "Output only.") {
1672 readOnlyValue.Set(reflect.ValueOf(true))
1673 }
1674
1675 usingTitle := false
1676 if !summaryValue.CanSet() {
1677 summaryValue = infoObjectValue.FieldByName("Title")
1678 usingTitle = true
1679 }
1680
1681 paragraphs := strings.Split(comment, "\n\n")
1682
1683
1684
1685 if summaryValue.CanSet() {
1686 summary := strings.TrimSpace(paragraphs[0])
1687 description := strings.TrimSpace(strings.Join(paragraphs[1:], "\n\n"))
1688 if !usingTitle || (len(summary) > 0 && summary[len(summary)-1] != '.') {
1689
1690
1691 if summaryValue.Len() == 0 || isPackageObject {
1692 summaryValue.Set(reflect.ValueOf(summary))
1693 }
1694 if len(description) > 0 {
1695 if !descriptionValue.CanSet() {
1696 return fmt.Errorf("Encountered object type with a summary, but no description")
1697 }
1698
1699
1700 if descriptionValue.Len() == 0 || isPackageObject {
1701 descriptionValue.Set(reflect.ValueOf(description))
1702 }
1703 }
1704 return nil
1705 }
1706 }
1707
1708
1709
1710 if descriptionValue.CanSet() {
1711 if descriptionValue.Len() == 0 || isPackageObject {
1712 descriptionValue.Set(reflect.ValueOf(strings.Join(paragraphs, "\n\n")))
1713 }
1714 return nil
1715 }
1716
1717 return fmt.Errorf("no description nor summary property")
1718 }
1719
1720 func fieldProtoComments(reg *descriptor.Registry, msg *descriptor.Message, field *descriptor.Field) string {
1721 protoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil)), "Field")
1722 for i, f := range msg.Fields {
1723 if f == field {
1724 return protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index), protoPath, int32(i))
1725 }
1726 }
1727 return ""
1728 }
1729
1730 func enumValueProtoComments(reg *descriptor.Registry, enum *descriptor.Enum) string {
1731 protoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.EnumDescriptorProto)(nil)), "Value")
1732 var comments []string
1733 for idx, value := range enum.GetValue() {
1734 name := value.GetName()
1735 if reg.GetEnumsAsInts() {
1736 name = strconv.Itoa(int(value.GetNumber()))
1737 }
1738 str := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index), protoPath, int32(idx))
1739 if str != "" {
1740 comments = append(comments, name+": "+str)
1741 }
1742 }
1743 if len(comments) > 0 {
1744 return "- " + strings.Join(comments, "\n - ")
1745 }
1746 return ""
1747 }
1748
1749 func protoComments(reg *descriptor.Registry, file *descriptor.File, outers []string, typeName string, typeIndex int32, fieldPaths ...int32) string {
1750 if file.SourceCodeInfo == nil {
1751 fmt.Fprintln(os.Stderr, "descriptor.File should not contain nil SourceCodeInfo")
1752 return ""
1753 }
1754
1755 outerPaths := make([]int32, len(outers))
1756 for i := range outers {
1757 location := ""
1758 if file.Package != nil {
1759 location = file.GetPackage()
1760 }
1761
1762 msg, err := reg.LookupMsg(location, strings.Join(outers[:i+1], "."))
1763 if err != nil {
1764 panic(err)
1765 }
1766 outerPaths[i] = int32(msg.Index)
1767 }
1768
1769 for _, loc := range file.SourceCodeInfo.Location {
1770 if !isProtoPathMatches(loc.Path, outerPaths, typeName, typeIndex, fieldPaths) {
1771 continue
1772 }
1773 comments := ""
1774 if loc.LeadingComments != nil {
1775 comments = strings.TrimRight(*loc.LeadingComments, "\n")
1776 comments = strings.TrimSpace(comments)
1777
1778
1779
1780
1781
1782
1783 comments = strings.Replace(comments, "\n ", "\n", -1)
1784 }
1785 return comments
1786 }
1787 return ""
1788 }
1789
1790 func goTemplateComments(comment string, data interface{}, reg *descriptor.Registry) string {
1791 var temp bytes.Buffer
1792 tpl, err := template.New("").Funcs(template.FuncMap{
1793
1794 "import": func(name string) string {
1795 file, err := ioutil.ReadFile(name)
1796 if err != nil {
1797 return err.Error()
1798 }
1799
1800 return goTemplateComments(string(file), data, reg)
1801 },
1802
1803 "fieldcomments": func(msg *descriptor.Message, field *descriptor.Field) string {
1804 return strings.Replace(fieldProtoComments(reg, msg, field), "\n", "<br>", -1)
1805 },
1806 }).Parse(comment)
1807 if err != nil {
1808
1809
1810 return err.Error()
1811 }
1812 err = tpl.Execute(&temp, data)
1813 if err != nil {
1814
1815
1816 return err.Error()
1817 }
1818 return temp.String()
1819 }
1820
1821 var messageProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "MessageType")
1822 var nestedProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil)), "NestedType")
1823 var packageProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "Package")
1824 var serviceProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "Service")
1825 var methodProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.ServiceDescriptorProto)(nil)), "Method")
1826
1827 func isProtoPathMatches(paths []int32, outerPaths []int32, typeName string, typeIndex int32, fieldPaths []int32) bool {
1828 if typeName == "Package" && typeIndex == packageProtoPath {
1829
1830
1831 if len(paths) == 0 || typeIndex != paths[0] {
1832 return false
1833 }
1834 return true
1835 }
1836
1837 if len(paths) != len(outerPaths)*2+2+len(fieldPaths) {
1838 return false
1839 }
1840
1841 if typeName == "Method" {
1842 if paths[0] != serviceProtoPath || paths[2] != methodProtoPath {
1843 return false
1844 }
1845 paths = paths[2:]
1846 } else {
1847 typeNameDescriptor := reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil))
1848
1849 if len(outerPaths) > 0 {
1850 if paths[0] != messageProtoPath || paths[1] != outerPaths[0] {
1851 return false
1852 }
1853 paths = paths[2:]
1854 outerPaths = outerPaths[1:]
1855
1856 for i, v := range outerPaths {
1857 if paths[i*2] != nestedProtoPath || paths[i*2+1] != v {
1858 return false
1859 }
1860 }
1861 paths = paths[len(outerPaths)*2:]
1862
1863 if typeName == "MessageType" {
1864 typeName = "NestedType"
1865 }
1866 typeNameDescriptor = reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil))
1867 }
1868
1869 if paths[0] != protoPathIndex(typeNameDescriptor, typeName) || paths[1] != typeIndex {
1870 return false
1871 }
1872 paths = paths[2:]
1873 }
1874
1875 for i, v := range fieldPaths {
1876 if paths[i] != v {
1877 return false
1878 }
1879 }
1880 return true
1881 }
1882
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907 func protoPathIndex(descriptorType reflect.Type, what string) int32 {
1908 field, ok := descriptorType.Elem().FieldByName(what)
1909 if !ok {
1910 panic(fmt.Errorf("could not find protobuf descriptor type id for %s", what))
1911 }
1912 pbtag := field.Tag.Get("protobuf")
1913 if pbtag == "" {
1914 panic(fmt.Errorf("no Go tag 'protobuf' on protobuf descriptor for %s", what))
1915 }
1916 path, err := strconv.Atoi(strings.Split(pbtag, ",")[1])
1917 if err != nil {
1918 panic(fmt.Errorf("protobuf descriptor id for %s cannot be converted to a number: %s", what, err.Error()))
1919 }
1920
1921 return int32(path)
1922 }
1923
1924
1925
1926 func extractOperationOptionFromMethodDescriptor(meth *pbdescriptor.MethodDescriptorProto) (*swagger_options.Operation, error) {
1927 if meth.Options == nil {
1928 return nil, nil
1929 }
1930 if !proto.HasExtension(meth.Options, swagger_options.E_Openapiv2Operation) {
1931 return nil, nil
1932 }
1933 ext, err := proto.GetExtension(meth.Options, swagger_options.E_Openapiv2Operation)
1934 if err != nil {
1935 return nil, err
1936 }
1937 opts, ok := ext.(*swagger_options.Operation)
1938 if !ok {
1939 return nil, fmt.Errorf("extension is %T; want an Operation", ext)
1940 }
1941 return opts, nil
1942 }
1943
1944
1945
1946 func extractSchemaOptionFromMessageDescriptor(msg *pbdescriptor.DescriptorProto) (*swagger_options.Schema, error) {
1947 if msg.Options == nil {
1948 return nil, nil
1949 }
1950 if !proto.HasExtension(msg.Options, swagger_options.E_Openapiv2Schema) {
1951 return nil, nil
1952 }
1953 ext, err := proto.GetExtension(msg.Options, swagger_options.E_Openapiv2Schema)
1954 if err != nil {
1955 return nil, err
1956 }
1957 opts, ok := ext.(*swagger_options.Schema)
1958 if !ok {
1959 return nil, fmt.Errorf("extension is %T; want a Schema", ext)
1960 }
1961 return opts, nil
1962 }
1963
1964
1965
1966 func extractSwaggerOptionFromFileDescriptor(file *pbdescriptor.FileDescriptorProto) (*swagger_options.Swagger, error) {
1967 if file.Options == nil {
1968 return nil, nil
1969 }
1970 if !proto.HasExtension(file.Options, swagger_options.E_Openapiv2Swagger) {
1971 return nil, nil
1972 }
1973 ext, err := proto.GetExtension(file.Options, swagger_options.E_Openapiv2Swagger)
1974 if err != nil {
1975 return nil, err
1976 }
1977 opts, ok := ext.(*swagger_options.Swagger)
1978 if !ok {
1979 return nil, fmt.Errorf("extension is %T; want a Swagger object", ext)
1980 }
1981 return opts, nil
1982 }
1983
1984 func extractJSONSchemaFromFieldDescriptor(fd *pbdescriptor.FieldDescriptorProto) (*swagger_options.JSONSchema, error) {
1985 if fd.Options == nil {
1986 return nil, nil
1987 }
1988 if !proto.HasExtension(fd.Options, swagger_options.E_Openapiv2Field) {
1989 return nil, nil
1990 }
1991 ext, err := proto.GetExtension(fd.Options, swagger_options.E_Openapiv2Field)
1992 if err != nil {
1993 return nil, err
1994 }
1995 opts, ok := ext.(*swagger_options.JSONSchema)
1996 if !ok {
1997 return nil, fmt.Errorf("extension is %T; want a JSONSchema object", ext)
1998 }
1999 return opts, nil
2000 }
2001
2002 func protoJSONSchemaToSwaggerSchemaCore(j *swagger_options.JSONSchema, reg *descriptor.Registry, refs refMap) schemaCore {
2003 ret := schemaCore{}
2004
2005 if j.GetRef() != "" {
2006 swaggerName, ok := fullyQualifiedNameToSwaggerName(j.GetRef(), reg)
2007 if ok {
2008 ret.Ref = "#/definitions/" + swaggerName
2009 if refs != nil {
2010 refs[j.GetRef()] = struct{}{}
2011 }
2012 } else {
2013 ret.Ref += j.GetRef()
2014 }
2015 } else {
2016 f, t := protoJSONSchemaTypeToFormat(j.GetType())
2017 ret.Format = f
2018 ret.Type = t
2019 }
2020
2021 return ret
2022 }
2023
2024 func updateSwaggerObjectFromJSONSchema(s *swaggerSchemaObject, j *swagger_options.JSONSchema, reg *descriptor.Registry, data interface{}) {
2025 s.Title = j.GetTitle()
2026 s.Description = j.GetDescription()
2027 if reg.GetUseGoTemplate() {
2028 s.Title = goTemplateComments(s.Title, data, reg)
2029 s.Description = goTemplateComments(s.Description, data, reg)
2030 }
2031
2032 s.ReadOnly = j.GetReadOnly()
2033 s.MultipleOf = j.GetMultipleOf()
2034 s.Maximum = j.GetMaximum()
2035 s.ExclusiveMaximum = j.GetExclusiveMaximum()
2036 s.Minimum = j.GetMinimum()
2037 s.ExclusiveMinimum = j.GetExclusiveMinimum()
2038 s.MaxLength = j.GetMaxLength()
2039 s.MinLength = j.GetMinLength()
2040 s.Pattern = j.GetPattern()
2041 s.Default = j.GetDefault()
2042 s.MaxItems = j.GetMaxItems()
2043 s.MinItems = j.GetMinItems()
2044 s.UniqueItems = j.GetUniqueItems()
2045 s.MaxProperties = j.GetMaxProperties()
2046 s.MinProperties = j.GetMinProperties()
2047 s.Required = j.GetRequired()
2048 s.Enum = j.GetEnum()
2049 if overrideType := j.GetType(); len(overrideType) > 0 {
2050 s.Type = strings.ToLower(overrideType[0].String())
2051 }
2052 if j != nil && j.GetExample() != "" {
2053 s.Example = json.RawMessage(j.GetExample())
2054 }
2055 if j != nil && j.GetFormat() != "" {
2056 s.Format = j.GetFormat()
2057 }
2058 }
2059
2060 func swaggerSchemaFromProtoSchema(s *swagger_options.Schema, reg *descriptor.Registry, refs refMap, data interface{}) swaggerSchemaObject {
2061 ret := swaggerSchemaObject{
2062 ExternalDocs: protoExternalDocumentationToSwaggerExternalDocumentation(s.GetExternalDocs(), reg, data),
2063 }
2064
2065 ret.schemaCore = protoJSONSchemaToSwaggerSchemaCore(s.GetJsonSchema(), reg, refs)
2066 updateSwaggerObjectFromJSONSchema(&ret, s.GetJsonSchema(), reg, data)
2067
2068 if s != nil && s.Example != nil {
2069 ret.Example = json.RawMessage(s.Example.Value)
2070 }
2071 if s != nil && s.ExampleString != "" {
2072 ret.Example = json.RawMessage(s.ExampleString)
2073 }
2074
2075 return ret
2076 }
2077
2078 func swaggerExamplesFromProtoExamples(in map[string]string) map[string]interface{} {
2079 if len(in) == 0 {
2080 return nil
2081 }
2082 out := make(map[string]interface{})
2083 for mimeType, exampleStr := range in {
2084 switch mimeType {
2085 case "application/json":
2086
2087 out[mimeType] = json.RawMessage(exampleStr)
2088 default:
2089
2090 out[mimeType] = exampleStr
2091 }
2092 }
2093 return out
2094 }
2095
2096 func protoJSONSchemaTypeToFormat(in []swagger_options.JSONSchema_JSONSchemaSimpleTypes) (string, string) {
2097 if len(in) == 0 {
2098 return "", ""
2099 }
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109 switch in[0] {
2110 case swagger_options.JSONSchema_UNKNOWN, swagger_options.JSONSchema_NULL:
2111 return "", ""
2112 case swagger_options.JSONSchema_OBJECT:
2113 return "object", ""
2114 case swagger_options.JSONSchema_ARRAY:
2115 return "array", ""
2116 case swagger_options.JSONSchema_BOOLEAN:
2117
2118 return "boolean", ""
2119 case swagger_options.JSONSchema_INTEGER:
2120 return "integer", "int32"
2121 case swagger_options.JSONSchema_NUMBER:
2122 return "number", "double"
2123 case swagger_options.JSONSchema_STRING:
2124
2125 return "string", ""
2126 default:
2127
2128 return "", ""
2129 }
2130 }
2131
2132 func protoExternalDocumentationToSwaggerExternalDocumentation(in *swagger_options.ExternalDocumentation, reg *descriptor.Registry, data interface{}) *swaggerExternalDocumentationObject {
2133 if in == nil {
2134 return nil
2135 }
2136
2137 if reg.GetUseGoTemplate() {
2138 in.Description = goTemplateComments(in.Description, data, reg)
2139 }
2140
2141 return &swaggerExternalDocumentationObject{
2142 Description: in.Description,
2143 URL: in.Url,
2144 }
2145 }
2146
2147 func addCustomRefs(d swaggerDefinitionsObject, reg *descriptor.Registry, refs refMap) {
2148 if len(refs) == 0 {
2149 return
2150 }
2151 msgMap := make(messageMap)
2152 enumMap := make(enumMap)
2153 for ref := range refs {
2154 swgName, swgOk := fullyQualifiedNameToSwaggerName(ref, reg)
2155 if !swgOk {
2156 glog.Errorf("can't resolve swagger name from CustomRef '%v'", ref)
2157 continue
2158 }
2159 if _, ok := d[swgName]; ok {
2160
2161 delete(refs, ref)
2162 continue
2163 }
2164 msg, err := reg.LookupMsg("", ref)
2165 if err == nil {
2166 msgMap[swgName] = msg
2167 continue
2168 }
2169 enum, err := reg.LookupEnum("", ref)
2170 if err == nil {
2171 enumMap[swgName] = enum
2172 continue
2173 }
2174
2175
2176 }
2177 renderMessagesAsDefinition(msgMap, d, reg, refs)
2178 renderEnumerationsAsDefinition(enumMap, d, reg)
2179
2180
2181 addCustomRefs(d, reg, refs)
2182 }
2183
2184 func lowerCamelCase(fieldName string, fields []*descriptor.Field, msgs []*descriptor.Message) string {
2185 for _, oneField := range fields {
2186 if oneField.GetName() == fieldName {
2187 return oneField.GetJsonName()
2188 }
2189 }
2190 messageNameToFieldsToJSONName := make(map[string]map[string]string, 0)
2191 fieldNameToType := make(map[string]string, 0)
2192 for _, msg := range msgs {
2193 fieldNameToJSONName := make(map[string]string, 0)
2194 for _, oneField := range msg.GetField() {
2195 fieldNameToJSONName[oneField.GetName()] = oneField.GetJsonName()
2196 fieldNameToType[oneField.GetName()] = oneField.GetTypeName()
2197 }
2198 messageNameToFieldsToJSONName[msg.GetName()] = fieldNameToJSONName
2199 }
2200 if strings.Contains(fieldName, ".") {
2201 fieldNames := strings.Split(fieldName, ".")
2202 fieldNamesWithCamelCase := make([]string, 0)
2203 for i := 0; i < len(fieldNames)-1; i++ {
2204 fieldNamesWithCamelCase = append(fieldNamesWithCamelCase, doCamelCase(string(fieldNames[i])))
2205 }
2206 prefix := strings.Join(fieldNamesWithCamelCase, ".")
2207 reservedJSONName := getReservedJSONName(fieldName, messageNameToFieldsToJSONName, fieldNameToType)
2208 if reservedJSONName != "" {
2209 return prefix + "." + reservedJSONName
2210 }
2211 }
2212 return doCamelCase(fieldName)
2213 }
2214
2215 func doCamelCase(input string) string {
2216 parameterString := casing.Camel(input)
2217 builder := &strings.Builder{}
2218 builder.WriteString(strings.ToLower(string(parameterString[0])))
2219 builder.WriteString(parameterString[1:])
2220 return builder.String()
2221 }
2222
2223 func getReservedJSONName(fieldName string, messageNameToFieldsToJSONName map[string]map[string]string, fieldNameToType map[string]string) string {
2224 if len(strings.Split(fieldName, ".")) == 2 {
2225 fieldNames := strings.Split(fieldName, ".")
2226 firstVariable := fieldNames[0]
2227 firstType := fieldNameToType[firstVariable]
2228 firstTypeShortNames := strings.Split(firstType, ".")
2229 firstTypeShortName := firstTypeShortNames[len(firstTypeShortNames)-1]
2230 return messageNameToFieldsToJSONName[firstTypeShortName][fieldNames[1]]
2231 }
2232 fieldNames := strings.Split(fieldName, ".")
2233 return getReservedJSONName(strings.Join(fieldNames[1:], "."), messageNameToFieldsToJSONName, fieldNameToType)
2234 }
2235
View as plain text