Moved to google.golang.org/genproto/googleapis/api/annotations
Fixes #52
This commit is contained in:
2
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/doc.go
generated
vendored
Normal file
2
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/doc.go
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
// Package genswagger provides a code generator for swagger.
|
||||
package genswagger
|
58
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/generator.go
generated
vendored
Normal file
58
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/generator.go
generated
vendored
Normal file
@@ -0,0 +1,58 @@
|
||||
package genswagger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/golang/protobuf/proto"
|
||||
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
|
||||
gen "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/generator"
|
||||
)
|
||||
|
||||
var (
|
||||
errNoTargetService = errors.New("no target service defined in the file")
|
||||
)
|
||||
|
||||
type generator struct {
|
||||
reg *descriptor.Registry
|
||||
}
|
||||
|
||||
// New returns a new generator which generates grpc gateway files.
|
||||
func New(reg *descriptor.Registry) gen.Generator {
|
||||
return &generator{reg: reg}
|
||||
}
|
||||
|
||||
func (g *generator) Generate(targets []*descriptor.File) ([]*plugin.CodeGeneratorResponse_File, error) {
|
||||
var files []*plugin.CodeGeneratorResponse_File
|
||||
for _, file := range targets {
|
||||
glog.V(1).Infof("Processing %s", file.GetName())
|
||||
code, err := applyTemplate(param{File: file, reg: g.reg})
|
||||
if err == errNoTargetService {
|
||||
glog.V(1).Infof("%s: %v", file.GetName(), err)
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var formatted bytes.Buffer
|
||||
json.Indent(&formatted, []byte(code), "", " ")
|
||||
|
||||
name := file.GetName()
|
||||
ext := filepath.Ext(name)
|
||||
base := strings.TrimSuffix(name, ext)
|
||||
output := fmt.Sprintf("%s.swagger.json", base)
|
||||
files = append(files, &plugin.CodeGeneratorResponse_File{
|
||||
Name: proto.String(output),
|
||||
Content: proto.String(formatted.String()),
|
||||
})
|
||||
glog.V(1).Infof("Will emit %s", output)
|
||||
}
|
||||
return files, nil
|
||||
}
|
835
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/template.go
generated
vendored
Normal file
835
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/template.go
generated
vendored
Normal file
@@ -0,0 +1,835 @@
|
||||
package genswagger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
pbdescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
|
||||
)
|
||||
|
||||
func listEnumNames(enum *descriptor.Enum) (names []string) {
|
||||
for _, value := range enum.GetValue() {
|
||||
names = append(names, value.GetName())
|
||||
}
|
||||
return names
|
||||
}
|
||||
|
||||
func getEnumDefault(enum *descriptor.Enum) string {
|
||||
for _, value := range enum.GetValue() {
|
||||
if value.GetNumber() == 0 {
|
||||
return value.GetName()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// messageToQueryParameters converts a message to a list of swagger query parameters.
|
||||
func messageToQueryParameters(message *descriptor.Message, reg *descriptor.Registry, pathParams []descriptor.Parameter) (params []swaggerParameterObject, err error) {
|
||||
for _, field := range message.Fields {
|
||||
p, err := queryParams(message, field, "", reg, pathParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = append(params, p...)
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// queryParams converts a field to a list of swagger query parameters recuresively.
|
||||
func queryParams(message *descriptor.Message, field *descriptor.Field, prefix string, reg *descriptor.Registry, pathParams []descriptor.Parameter) (params []swaggerParameterObject, err error) {
|
||||
// make sure the parameter is not already listed as a path parameter
|
||||
for _, pathParam := range pathParams {
|
||||
if pathParam.Target == field {
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
schema := schemaOfField(field, reg)
|
||||
fieldType := field.GetTypeName()
|
||||
if message.File != nil {
|
||||
comments := fieldProtoComments(reg, message, field)
|
||||
if err := updateSwaggerDataFromComments(&schema, comments); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
isEnum := field.GetType() == pbdescriptor.FieldDescriptorProto_TYPE_ENUM
|
||||
items := schema.Items
|
||||
if schema.Type != "" || isEnum {
|
||||
if schema.Type == "object" {
|
||||
return nil, nil // TODO: currently, mapping object in query parameter is not supported
|
||||
}
|
||||
if items != nil && (items.Type == "" || items.Type == "object") && !isEnum {
|
||||
return nil, nil // TODO: currently, mapping object in query parameter is not supported
|
||||
}
|
||||
desc := schema.Description
|
||||
if schema.Title != "" { // merge title because title of parameter object will be ignored
|
||||
desc = strings.TrimSpace(schema.Title + ". " + schema.Description)
|
||||
}
|
||||
param := swaggerParameterObject{
|
||||
Name: prefix + field.GetName(),
|
||||
Description: desc,
|
||||
In: "query",
|
||||
Type: schema.Type,
|
||||
Items: schema.Items,
|
||||
Format: schema.Format,
|
||||
}
|
||||
if isEnum {
|
||||
enum, err := reg.LookupEnum("", fieldType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unknown enum type %s", fieldType)
|
||||
}
|
||||
if items != nil { // array
|
||||
param.Items = &swaggerItemsObject{
|
||||
Type: "string",
|
||||
Enum: listEnumNames(enum),
|
||||
}
|
||||
} else {
|
||||
param.Type = "string"
|
||||
param.Enum = listEnumNames(enum)
|
||||
param.Default = getEnumDefault(enum)
|
||||
}
|
||||
valueComments := enumValueProtoComments(reg, enum)
|
||||
if valueComments != "" {
|
||||
param.Description = strings.TrimLeft(param.Description+"\n\n "+valueComments, "\n")
|
||||
}
|
||||
}
|
||||
return []swaggerParameterObject{param}, nil
|
||||
}
|
||||
|
||||
// nested type, recurse
|
||||
msg, err := reg.LookupMsg("", fieldType)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unknown message type %s", fieldType)
|
||||
}
|
||||
for _, nestedField := range msg.Fields {
|
||||
p, err := queryParams(msg, nestedField, prefix+field.GetName()+".", reg, pathParams)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params = append(params, p...)
|
||||
}
|
||||
return params, nil
|
||||
}
|
||||
|
||||
// findServicesMessagesAndEnumerations discovers all messages and enums defined in the RPC methods of the service.
|
||||
func findServicesMessagesAndEnumerations(s []*descriptor.Service, reg *descriptor.Registry, m messageMap, e enumMap) {
|
||||
for _, svc := range s {
|
||||
for _, meth := range svc.Methods {
|
||||
m[fullyQualifiedNameToSwaggerName(meth.RequestType.FQMN(), reg)] = meth.RequestType
|
||||
findNestedMessagesAndEnumerations(meth.RequestType, reg, m, e)
|
||||
m[fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)] = meth.ResponseType
|
||||
findNestedMessagesAndEnumerations(meth.ResponseType, reg, m, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// findNestedMessagesAndEnumerations those can be generated by the services.
|
||||
func findNestedMessagesAndEnumerations(message *descriptor.Message, reg *descriptor.Registry, m messageMap, e enumMap) {
|
||||
// Iterate over all the fields that
|
||||
for _, t := range message.Fields {
|
||||
fieldType := t.GetTypeName()
|
||||
// If the type is an empty string then it is a proto primitive
|
||||
if fieldType != "" {
|
||||
if _, ok := m[fieldType]; !ok {
|
||||
msg, err := reg.LookupMsg("", fieldType)
|
||||
if err != nil {
|
||||
enum, err := reg.LookupEnum("", fieldType)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
e[fieldType] = enum
|
||||
continue
|
||||
}
|
||||
m[fieldType] = msg
|
||||
findNestedMessagesAndEnumerations(msg, reg, m, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func renderMessagesAsDefinition(messages messageMap, d swaggerDefinitionsObject, reg *descriptor.Registry) {
|
||||
for name, msg := range messages {
|
||||
switch name {
|
||||
case ".google.protobuf.Timestamp":
|
||||
continue
|
||||
}
|
||||
if opt := msg.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
|
||||
continue
|
||||
}
|
||||
schema := swaggerSchemaObject{
|
||||
schemaCore: schemaCore{
|
||||
Type: "object",
|
||||
},
|
||||
}
|
||||
msgComments := protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index))
|
||||
if err := updateSwaggerDataFromComments(&schema, msgComments); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, f := range msg.Fields {
|
||||
fieldValue := schemaOfField(f, reg)
|
||||
comments := fieldProtoComments(reg, msg, f)
|
||||
if err := updateSwaggerDataFromComments(&fieldValue, comments); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
schema.Properties = append(schema.Properties, keyVal{f.GetName(), fieldValue})
|
||||
}
|
||||
d[fullyQualifiedNameToSwaggerName(msg.FQMN(), reg)] = schema
|
||||
}
|
||||
}
|
||||
|
||||
// schemaOfField returns a swagger Schema Object for a protobuf field.
|
||||
func schemaOfField(f *descriptor.Field, reg *descriptor.Registry) swaggerSchemaObject {
|
||||
const (
|
||||
singular = 0
|
||||
array = 1
|
||||
object = 2
|
||||
)
|
||||
var (
|
||||
core schemaCore
|
||||
aggregate int
|
||||
)
|
||||
|
||||
fd := f.FieldDescriptorProto
|
||||
if m, err := reg.LookupMsg("", f.GetTypeName()); err == nil {
|
||||
if opt := m.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
|
||||
fd = m.GetField()[1]
|
||||
aggregate = object
|
||||
}
|
||||
}
|
||||
if fd.GetLabel() == pbdescriptor.FieldDescriptorProto_LABEL_REPEATED {
|
||||
aggregate = array
|
||||
}
|
||||
|
||||
switch ft := fd.GetType(); ft {
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_ENUM, pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE, pbdescriptor.FieldDescriptorProto_TYPE_GROUP:
|
||||
if fd.GetTypeName() == ".google.protobuf.Timestamp" && pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE == ft {
|
||||
core = schemaCore{
|
||||
Type: "string",
|
||||
Format: "date-time",
|
||||
}
|
||||
} else {
|
||||
core = schemaCore{
|
||||
Ref: "#/definitions/" + fullyQualifiedNameToSwaggerName(fd.GetTypeName(), reg),
|
||||
}
|
||||
}
|
||||
default:
|
||||
ftype, format, ok := primitiveSchema(ft)
|
||||
if ok {
|
||||
core = schemaCore{Type: ftype, Format: format}
|
||||
} else {
|
||||
core = schemaCore{Type: ft.String(), Format: "UNKNOWN"}
|
||||
}
|
||||
}
|
||||
switch aggregate {
|
||||
case array:
|
||||
return swaggerSchemaObject{
|
||||
schemaCore: schemaCore{
|
||||
Type: "array",
|
||||
Items: (*swaggerItemsObject)(&core),
|
||||
},
|
||||
}
|
||||
case object:
|
||||
return swaggerSchemaObject{
|
||||
schemaCore: schemaCore{
|
||||
Type: "object",
|
||||
},
|
||||
AdditionalProperties: &swaggerSchemaObject{schemaCore: core},
|
||||
}
|
||||
default:
|
||||
return swaggerSchemaObject{schemaCore: core}
|
||||
}
|
||||
}
|
||||
|
||||
// primitiveSchema returns a pair of "Type" and "Format" in JSON Schema for
|
||||
// the given primitive field type.
|
||||
// The last return parameter is true iff the field type is actually primitive.
|
||||
func primitiveSchema(t pbdescriptor.FieldDescriptorProto_Type) (ftype, format string, ok bool) {
|
||||
switch t {
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_DOUBLE:
|
||||
return "number", "double", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_FLOAT:
|
||||
return "number", "float", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_INT64:
|
||||
return "string", "int64", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_UINT64:
|
||||
// 64bit integer types are marshaled as string in the default JSONPb marshaler.
|
||||
// TODO(yugui) Add an option to declare 64bit integers as int64.
|
||||
//
|
||||
// NOTE: uint64 is not a predefined format of integer type in Swagger spec.
|
||||
// So we cannot expect that uint64 is commonly supported by swagger processor.
|
||||
return "string", "uint64", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_INT32:
|
||||
return "integer", "int32", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_FIXED64:
|
||||
// Ditto.
|
||||
return "string", "uint64", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_FIXED32:
|
||||
// Ditto.
|
||||
return "integer", "int64", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_BOOL:
|
||||
return "boolean", "boolean", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_STRING:
|
||||
// NOTE: in swagger specifition, format should be empty on string type
|
||||
return "string", "", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_BYTES:
|
||||
return "string", "byte", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_UINT32:
|
||||
// Ditto.
|
||||
return "integer", "int64", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED32:
|
||||
return "integer", "int32", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_SFIXED64:
|
||||
return "string", "int64", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_SINT32:
|
||||
return "integer", "int32", true
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_SINT64:
|
||||
return "string", "int64", true
|
||||
default:
|
||||
return "", "", false
|
||||
}
|
||||
}
|
||||
|
||||
// renderEnumerationsAsDefinition inserts enums into the definitions object.
|
||||
func renderEnumerationsAsDefinition(enums enumMap, d swaggerDefinitionsObject, reg *descriptor.Registry) {
|
||||
for _, enum := range enums {
|
||||
enumComments := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index))
|
||||
|
||||
// it may be necessary to sort the result of the GetValue function.
|
||||
enumNames := listEnumNames(enum)
|
||||
defaultValue := getEnumDefault(enum)
|
||||
valueComments := enumValueProtoComments(reg, enum)
|
||||
if valueComments != "" {
|
||||
enumComments = strings.TrimLeft(enumComments+"\n\n "+valueComments, "\n")
|
||||
}
|
||||
enumSchemaObject := swaggerSchemaObject{
|
||||
schemaCore: schemaCore{
|
||||
Type: "string",
|
||||
Enum: enumNames,
|
||||
Default: defaultValue,
|
||||
},
|
||||
}
|
||||
if err := updateSwaggerDataFromComments(&enumSchemaObject, enumComments); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
d[fullyQualifiedNameToSwaggerName(enum.FQEN(), reg)] = enumSchemaObject
|
||||
}
|
||||
}
|
||||
|
||||
// Take in a FQMN or FQEN and return a swagger safe version of the FQMN
|
||||
func fullyQualifiedNameToSwaggerName(fqn string, reg *descriptor.Registry) string {
|
||||
return resolveFullyQualifiedNameToSwaggerName(fqn, append(reg.GetAllFQMNs(), reg.GetAllFQENs()...))
|
||||
}
|
||||
|
||||
// Take the names of every proto and "uniq-ify" them. The idea is to produce a
|
||||
// set of names that meet a couple of conditions. They must be stable, they
|
||||
// must be unique, and they must be shorter than the FQN.
|
||||
//
|
||||
// This likely could be made better. This will always generate the same names
|
||||
// but may not always produce optimal names. This is a reasonably close
|
||||
// approximation of what they should look like in most cases.
|
||||
func resolveFullyQualifiedNameToSwaggerName(fqn string, messages []string) string {
|
||||
packagesByDepth := make(map[int][][]string)
|
||||
uniqueNames := make(map[string]string)
|
||||
|
||||
hierarchy := func(pkg string) []string {
|
||||
return strings.Split(pkg, ".")
|
||||
}
|
||||
|
||||
for _, p := range messages {
|
||||
h := hierarchy(p)
|
||||
for depth := range h {
|
||||
if _, ok := packagesByDepth[depth]; !ok {
|
||||
packagesByDepth[depth] = make([][]string, 0)
|
||||
}
|
||||
packagesByDepth[depth] = append(packagesByDepth[depth], h[len(h)-depth:])
|
||||
}
|
||||
}
|
||||
|
||||
count := func(list [][]string, item []string) int {
|
||||
i := 0
|
||||
for _, element := range list {
|
||||
if reflect.DeepEqual(element, item) {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
for _, p := range messages {
|
||||
h := hierarchy(p)
|
||||
for depth := 0; depth < len(h); depth++ {
|
||||
if count(packagesByDepth[depth], h[len(h)-depth:]) == 1 {
|
||||
uniqueNames[p] = strings.Join(h[len(h)-depth-1:], "")
|
||||
break
|
||||
}
|
||||
if depth == len(h)-1 {
|
||||
uniqueNames[p] = strings.Join(h, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
return uniqueNames[fqn]
|
||||
}
|
||||
|
||||
// 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.
|
||||
func templateToSwaggerPath(path string) string {
|
||||
// It seems like the right thing to do here is to just use
|
||||
// strings.Split(path, "/") but that breaks badly when you hit a url like
|
||||
// /{my_field=prefix/*}/ and end up with 2 sections representing my_field.
|
||||
// Instead do the right thing and write a small pushdown (counter) automata
|
||||
// for it.
|
||||
var parts []string
|
||||
depth := 0
|
||||
buffer := ""
|
||||
for _, char := range path {
|
||||
switch char {
|
||||
case '{':
|
||||
// Push on the stack
|
||||
depth++
|
||||
buffer += string(char)
|
||||
break
|
||||
case '}':
|
||||
if depth == 0 {
|
||||
panic("Encountered } without matching { before it.")
|
||||
}
|
||||
// Pop from the stack
|
||||
depth--
|
||||
buffer += "}"
|
||||
case '/':
|
||||
if depth == 0 {
|
||||
parts = append(parts, buffer)
|
||||
buffer = ""
|
||||
// Since the stack was empty when we hit the '/' we are done with this
|
||||
// section.
|
||||
continue
|
||||
}
|
||||
default:
|
||||
buffer += string(char)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Now append the last element to parts
|
||||
parts = append(parts, buffer)
|
||||
|
||||
// Parts is now an array of segments of the path. Interestingly, since the
|
||||
// syntax for this subsection CAN be handled by a regexp since it has no
|
||||
// memory.
|
||||
re := regexp.MustCompile("{([a-zA-Z][a-zA-Z0-9_.]*).*}")
|
||||
for index, part := range parts {
|
||||
parts[index] = re.ReplaceAllString(part, "{$1}")
|
||||
}
|
||||
|
||||
return strings.Join(parts, "/")
|
||||
}
|
||||
|
||||
func renderServices(services []*descriptor.Service, paths swaggerPathsObject, reg *descriptor.Registry) error {
|
||||
// Correctness of svcIdx and methIdx depends on 'services' containing the services in the same order as the 'file.Service' array.
|
||||
for svcIdx, svc := range services {
|
||||
for methIdx, meth := range svc.Methods {
|
||||
for _, b := range meth.Bindings {
|
||||
// Iterate over all the swagger parameters
|
||||
parameters := swaggerParametersObject{}
|
||||
for _, parameter := range b.PathParams {
|
||||
|
||||
var paramType, paramFormat string
|
||||
switch pt := parameter.Target.GetType(); pt {
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_GROUP, pbdescriptor.FieldDescriptorProto_TYPE_MESSAGE:
|
||||
return fmt.Errorf("only primitive types are allowed in path parameters")
|
||||
case pbdescriptor.FieldDescriptorProto_TYPE_ENUM:
|
||||
paramType = fullyQualifiedNameToSwaggerName(parameter.Target.GetTypeName(), reg)
|
||||
paramFormat = ""
|
||||
default:
|
||||
var ok bool
|
||||
paramType, paramFormat, ok = primitiveSchema(pt)
|
||||
if !ok {
|
||||
return fmt.Errorf("unknown field type %v", pt)
|
||||
}
|
||||
}
|
||||
|
||||
parameters = append(parameters, swaggerParameterObject{
|
||||
Name: parameter.String(),
|
||||
In: "path",
|
||||
Required: true,
|
||||
// Parameters in gRPC-Gateway can only be strings?
|
||||
Type: paramType,
|
||||
Format: paramFormat,
|
||||
})
|
||||
}
|
||||
// Now check if there is a body parameter
|
||||
if b.Body != nil {
|
||||
var schema swaggerSchemaObject
|
||||
|
||||
if len(b.Body.FieldPath) == 0 {
|
||||
schema = swaggerSchemaObject{
|
||||
schemaCore: schemaCore{
|
||||
Ref: fmt.Sprintf("#/definitions/%s", fullyQualifiedNameToSwaggerName(meth.RequestType.FQMN(), reg)),
|
||||
},
|
||||
}
|
||||
} else {
|
||||
lastField := b.Body.FieldPath[len(b.Body.FieldPath)-1]
|
||||
schema = schemaOfField(lastField.Target, reg)
|
||||
}
|
||||
|
||||
desc := ""
|
||||
if meth.GetClientStreaming() {
|
||||
desc = "(streaming inputs)"
|
||||
}
|
||||
parameters = append(parameters, swaggerParameterObject{
|
||||
Name: "body",
|
||||
Description: desc,
|
||||
In: "body",
|
||||
Required: true,
|
||||
Schema: &schema,
|
||||
})
|
||||
} else if b.HTTPMethod == "GET" {
|
||||
// add the parameters to the query string
|
||||
queryParams, err := messageToQueryParameters(meth.RequestType, reg, b.PathParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
parameters = append(parameters, queryParams...)
|
||||
}
|
||||
|
||||
pathItemObject, ok := paths[templateToSwaggerPath(b.PathTmpl.Template)]
|
||||
if !ok {
|
||||
pathItemObject = swaggerPathItemObject{}
|
||||
}
|
||||
|
||||
methProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.ServiceDescriptorProto)(nil)), "Method")
|
||||
desc := ""
|
||||
if meth.GetServerStreaming() {
|
||||
desc += "(streaming responses)"
|
||||
}
|
||||
operationObject := &swaggerOperationObject{
|
||||
Tags: []string{svc.GetName()},
|
||||
OperationID: fmt.Sprintf("%s", meth.GetName()),
|
||||
Parameters: parameters,
|
||||
Responses: swaggerResponsesObject{
|
||||
"200": swaggerResponseObject{
|
||||
Description: desc,
|
||||
Schema: swaggerSchemaObject{
|
||||
schemaCore: schemaCore{
|
||||
Ref: fmt.Sprintf("#/definitions/%s", fullyQualifiedNameToSwaggerName(meth.ResponseType.FQMN(), reg)),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
methComments := protoComments(reg, svc.File, nil, "Service", int32(svcIdx), methProtoPath, int32(methIdx))
|
||||
if err := updateSwaggerDataFromComments(operationObject, methComments); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
switch b.HTTPMethod {
|
||||
case "DELETE":
|
||||
pathItemObject.Delete = operationObject
|
||||
break
|
||||
case "GET":
|
||||
pathItemObject.Get = operationObject
|
||||
break
|
||||
case "POST":
|
||||
pathItemObject.Post = operationObject
|
||||
break
|
||||
case "PUT":
|
||||
pathItemObject.Put = operationObject
|
||||
break
|
||||
case "PATCH":
|
||||
pathItemObject.Patch = operationObject
|
||||
break
|
||||
}
|
||||
paths[templateToSwaggerPath(b.PathTmpl.Template)] = pathItemObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Success! return nil on the error object
|
||||
return nil
|
||||
}
|
||||
|
||||
// This function is called with a param which contains the entire definition of a method.
|
||||
func applyTemplate(p param) (string, error) {
|
||||
// Create the basic template object. This is the object that everything is
|
||||
// defined off of.
|
||||
s := swaggerObject{
|
||||
// Swagger 2.0 is the version of this document
|
||||
Swagger: "2.0",
|
||||
Schemes: []string{"http", "https"},
|
||||
Consumes: []string{"application/json"},
|
||||
Produces: []string{"application/json"},
|
||||
Paths: make(swaggerPathsObject),
|
||||
Definitions: make(swaggerDefinitionsObject),
|
||||
Info: swaggerInfoObject{
|
||||
Title: *p.File.Name,
|
||||
Version: "version not set",
|
||||
},
|
||||
}
|
||||
|
||||
// Loops through all the services and their exposed GET/POST/PUT/DELETE definitions
|
||||
// and create entries for all of them.
|
||||
if err := renderServices(p.Services, s.Paths, p.reg); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Find all the service's messages and enumerations that are defined (recursively) and then
|
||||
// write their request and response types out as definition objects.
|
||||
m := messageMap{}
|
||||
e := enumMap{}
|
||||
findServicesMessagesAndEnumerations(p.Services, p.reg, m, e)
|
||||
renderMessagesAsDefinition(m, s.Definitions, p.reg)
|
||||
renderEnumerationsAsDefinition(e, s.Definitions, p.reg)
|
||||
|
||||
// File itself might have some comments and metadata.
|
||||
packageProtoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "Package")
|
||||
packageComments := protoComments(p.reg, p.File, nil, "Package", packageProtoPath)
|
||||
if err := updateSwaggerDataFromComments(&s, packageComments); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// We now have rendered the entire swagger object. Write the bytes out to a
|
||||
// string so it can be written to disk.
|
||||
var w bytes.Buffer
|
||||
enc := json.NewEncoder(&w)
|
||||
enc.Encode(&s)
|
||||
|
||||
return w.String(), nil
|
||||
}
|
||||
|
||||
// updateSwaggerDataFromComments updates a Swagger object based on a comment
|
||||
// from the proto file.
|
||||
//
|
||||
// First paragraph of a comment is used for summary. Remaining paragraphs of a
|
||||
// comment are used for description. If 'Summary' field is not present on the
|
||||
// passed swaggerObject, the summary and description are joined by \n\n.
|
||||
//
|
||||
// If there is a field named 'Info', its 'Summary' and 'Description' fields
|
||||
// will be updated instead.
|
||||
//
|
||||
// If there is no 'Summary', the same behavior will be attempted on 'Title',
|
||||
// but only if the last character is not a period.
|
||||
func updateSwaggerDataFromComments(swaggerObject interface{}, comment string) error {
|
||||
if len(comment) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Figure out what to apply changes to.
|
||||
swaggerObjectValue := reflect.ValueOf(swaggerObject)
|
||||
infoObjectValue := swaggerObjectValue.Elem().FieldByName("Info")
|
||||
if !infoObjectValue.CanSet() {
|
||||
// No such field? Apply summary and description directly to
|
||||
// passed object.
|
||||
infoObjectValue = swaggerObjectValue.Elem()
|
||||
}
|
||||
|
||||
// Figure out which properties to update.
|
||||
summaryValue := infoObjectValue.FieldByName("Summary")
|
||||
descriptionValue := infoObjectValue.FieldByName("Description")
|
||||
usingTitle := false
|
||||
if !summaryValue.CanSet() {
|
||||
summaryValue = infoObjectValue.FieldByName("Title")
|
||||
usingTitle = true
|
||||
}
|
||||
|
||||
// If there is a summary (or summary-equivalent), use the first
|
||||
// paragraph as summary, and the rest as description.
|
||||
if summaryValue.CanSet() {
|
||||
paragraphs := strings.Split(comment, "\n\n")
|
||||
|
||||
summary := strings.TrimSpace(paragraphs[0])
|
||||
description := strings.TrimSpace(strings.Join(paragraphs[1:], "\n\n"))
|
||||
if !usingTitle || summary == "" || summary[len(summary)-1] != '.' {
|
||||
if len(summary) > 0 {
|
||||
summaryValue.Set(reflect.ValueOf(summary))
|
||||
}
|
||||
if len(description) > 0 {
|
||||
if !descriptionValue.CanSet() {
|
||||
return fmt.Errorf("Encountered object type with a summary, but no description")
|
||||
}
|
||||
descriptionValue.Set(reflect.ValueOf(description))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// There was no summary field on the swaggerObject. Try to apply the
|
||||
// whole comment into description.
|
||||
if descriptionValue.CanSet() {
|
||||
descriptionValue.Set(reflect.ValueOf(comment))
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("no description nor summary property")
|
||||
}
|
||||
|
||||
func fieldProtoComments(reg *descriptor.Registry, msg *descriptor.Message, field *descriptor.Field) string {
|
||||
protoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil)), "Field")
|
||||
for i, f := range msg.Fields {
|
||||
if f == field {
|
||||
return protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index), protoPath, int32(i))
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func enumValueProtoComments(reg *descriptor.Registry, enum *descriptor.Enum) string {
|
||||
protoPath := protoPathIndex(reflect.TypeOf((*pbdescriptor.EnumDescriptorProto)(nil)), "Value")
|
||||
var comments []string
|
||||
for idx, value := range enum.GetValue() {
|
||||
name := value.GetName()
|
||||
str := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index), protoPath, int32(idx))
|
||||
if str != "" {
|
||||
comments = append(comments, name+": "+str)
|
||||
}
|
||||
}
|
||||
if len(comments) > 0 {
|
||||
return "- " + strings.Join(comments, "\n - ")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func protoComments(reg *descriptor.Registry, file *descriptor.File, outers []string, typeName string, typeIndex int32, fieldPaths ...int32) string {
|
||||
if file.SourceCodeInfo == nil {
|
||||
// Curious! A file without any source code info.
|
||||
// This could be a test that's providing incomplete
|
||||
// descriptor.File information.
|
||||
//
|
||||
// We could simply return no comments, but panic
|
||||
// could make debugging easier.
|
||||
panic("descriptor.File should not contain nil SourceCodeInfo")
|
||||
}
|
||||
|
||||
outerPaths := make([]int32, len(outers))
|
||||
for i := range outers {
|
||||
location := ""
|
||||
if file.Package != nil {
|
||||
location = file.GetPackage()
|
||||
}
|
||||
|
||||
msg, err := reg.LookupMsg(location, strings.Join(outers[:i+1], "."))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
outerPaths[i] = int32(msg.Index)
|
||||
}
|
||||
|
||||
for _, loc := range file.SourceCodeInfo.Location {
|
||||
if !isProtoPathMatches(loc.Path, outerPaths, typeName, typeIndex, fieldPaths) {
|
||||
continue
|
||||
}
|
||||
comments := ""
|
||||
if loc.LeadingComments != nil {
|
||||
comments = strings.TrimRight(*loc.LeadingComments, "\n")
|
||||
comments = strings.TrimSpace(comments)
|
||||
// TODO(ivucica): this is a hack to fix "// " being interpreted as "//".
|
||||
// perhaps we should:
|
||||
// - split by \n
|
||||
// - determine if every (but first and last) line begins with " "
|
||||
// - trim every line only if that is the case
|
||||
// - join by \n
|
||||
comments = strings.Replace(comments, "\n ", "\n", -1)
|
||||
}
|
||||
return comments
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var messageProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "MessageType")
|
||||
var nestedProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil)), "NestedType")
|
||||
var packageProtoPath = protoPathIndex(reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil)), "Package")
|
||||
|
||||
func isProtoPathMatches(paths []int32, outerPaths []int32, typeName string, typeIndex int32, fieldPaths []int32) bool {
|
||||
if typeName == "Package" && typeIndex == packageProtoPath {
|
||||
// path for package comments is just [2], and all the other processing
|
||||
// is too complex for it.
|
||||
if len(paths) == 0 || typeIndex != paths[0] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
if len(paths) != len(outerPaths)*2+2+len(fieldPaths) {
|
||||
return false
|
||||
}
|
||||
|
||||
typeNameDescriptor := reflect.TypeOf((*pbdescriptor.FileDescriptorProto)(nil))
|
||||
if len(outerPaths) > 0 {
|
||||
if paths[0] != messageProtoPath || paths[1] != outerPaths[0] {
|
||||
return false
|
||||
}
|
||||
paths = paths[2:]
|
||||
outerPaths = outerPaths[1:]
|
||||
|
||||
for i, v := range outerPaths {
|
||||
if paths[i*2] != nestedProtoPath || paths[i*2+1] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
paths = paths[len(outerPaths)*2:]
|
||||
|
||||
if typeName == "MessageType" {
|
||||
typeName = "NestedType"
|
||||
}
|
||||
typeNameDescriptor = reflect.TypeOf((*pbdescriptor.DescriptorProto)(nil))
|
||||
}
|
||||
|
||||
if paths[0] != protoPathIndex(typeNameDescriptor, typeName) || paths[1] != typeIndex {
|
||||
return false
|
||||
}
|
||||
paths = paths[2:]
|
||||
|
||||
for i, v := range fieldPaths {
|
||||
if paths[i] != v {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// protoPathIndex returns a path component for google.protobuf.descriptor.SourceCode_Location.
|
||||
//
|
||||
// Specifically, it returns an id as generated from descriptor proto which
|
||||
// can be used to determine what type the id following it in the path is.
|
||||
// For example, if we are trying to locate comments related to a field named
|
||||
// `Address` in a message named `Person`, the path will be:
|
||||
//
|
||||
// [4, a, 2, b]
|
||||
//
|
||||
// While `a` gets determined by the order in which the messages appear in
|
||||
// the proto file, and `b` is the field index specified in the proto
|
||||
// file itself, the path actually needs to specify that `a` refers to a
|
||||
// message and not, say, a service; and that `b` refers to a field and not
|
||||
// an option.
|
||||
//
|
||||
// protoPathIndex figures out the values 4 and 2 in the above example. Because
|
||||
// messages are top level objects, the value of 4 comes from field id for
|
||||
// `MessageType` inside `google.protobuf.descriptor.FileDescriptor` message.
|
||||
// This field has a message type `google.protobuf.descriptor.DescriptorProto`.
|
||||
// And inside message `DescriptorProto`, there is a field named `Field` with id
|
||||
// 2.
|
||||
//
|
||||
// Some code generators seem to be hardcoding these values; this method instead
|
||||
// interprets them from `descriptor.proto`-derived Go source as necessary.
|
||||
func protoPathIndex(descriptorType reflect.Type, what string) int32 {
|
||||
field, ok := descriptorType.Elem().FieldByName(what)
|
||||
if !ok {
|
||||
panic(fmt.Errorf("could not find protobuf descriptor type id for %s", what))
|
||||
}
|
||||
pbtag := field.Tag.Get("protobuf")
|
||||
if pbtag == "" {
|
||||
panic(fmt.Errorf("no Go tag 'protobuf' on protobuf descriptor for %s", what))
|
||||
}
|
||||
path, err := strconv.Atoi(strings.Split(pbtag, ",")[1])
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("protobuf descriptor id for %s cannot be converted to a number: %s", what, err.Error()))
|
||||
}
|
||||
|
||||
return int32(path)
|
||||
}
|
661
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/template_test.go
generated
vendored
Normal file
661
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/template_test.go
generated
vendored
Normal file
@@ -0,0 +1,661 @@
|
||||
package genswagger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/golang/protobuf/proto"
|
||||
protodescriptor "github.com/golang/protobuf/protoc-gen-go/descriptor"
|
||||
plugin "github.com/golang/protobuf/protoc-gen-go/plugin"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
|
||||
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/httprule"
|
||||
)
|
||||
|
||||
func crossLinkFixture(f *descriptor.File) *descriptor.File {
|
||||
for _, m := range f.Messages {
|
||||
m.File = f
|
||||
}
|
||||
for _, svc := range f.Services {
|
||||
svc.File = f
|
||||
for _, m := range svc.Methods {
|
||||
m.Service = svc
|
||||
for _, b := range m.Bindings {
|
||||
b.Method = m
|
||||
for _, param := range b.PathParams {
|
||||
param.Method = m
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func TestMessageToQueryParameters(t *testing.T) {
|
||||
type test struct {
|
||||
MsgDescs []*protodescriptor.DescriptorProto
|
||||
Message string
|
||||
Params []swaggerParameterObject
|
||||
}
|
||||
|
||||
tests := []test{
|
||||
{
|
||||
MsgDescs: []*protodescriptor.DescriptorProto{
|
||||
&protodescriptor.DescriptorProto{
|
||||
Name: proto.String("ExampleMessage"),
|
||||
Field: []*protodescriptor.FieldDescriptorProto{
|
||||
{
|
||||
Name: proto.String("a"),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
|
||||
Number: proto.Int32(1),
|
||||
},
|
||||
{
|
||||
Name: proto.String("b"),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
|
||||
Number: proto.Int32(2),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Message: "ExampleMessage",
|
||||
Params: []swaggerParameterObject{
|
||||
swaggerParameterObject{
|
||||
Name: "a",
|
||||
In: "query",
|
||||
Required: false,
|
||||
Type: "string",
|
||||
},
|
||||
swaggerParameterObject{
|
||||
Name: "b",
|
||||
In: "query",
|
||||
Required: false,
|
||||
Type: "number",
|
||||
Format: "double",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
MsgDescs: []*protodescriptor.DescriptorProto{
|
||||
&protodescriptor.DescriptorProto{
|
||||
Name: proto.String("ExampleMessage"),
|
||||
Field: []*protodescriptor.FieldDescriptorProto{
|
||||
{
|
||||
Name: proto.String("nested"),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
|
||||
TypeName: proto.String(".example.Nested"),
|
||||
Number: proto.Int32(1),
|
||||
},
|
||||
},
|
||||
},
|
||||
&protodescriptor.DescriptorProto{
|
||||
Name: proto.String("Nested"),
|
||||
Field: []*protodescriptor.FieldDescriptorProto{
|
||||
{
|
||||
Name: proto.String("a"),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
|
||||
Number: proto.Int32(1),
|
||||
},
|
||||
{
|
||||
Name: proto.String("deep"),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
|
||||
TypeName: proto.String(".example.Nested.DeepNested"),
|
||||
Number: proto.Int32(2),
|
||||
},
|
||||
},
|
||||
NestedType: []*protodescriptor.DescriptorProto{{
|
||||
Name: proto.String("DeepNested"),
|
||||
Field: []*protodescriptor.FieldDescriptorProto{
|
||||
{
|
||||
Name: proto.String("b"),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_STRING.Enum(),
|
||||
Number: proto.Int32(1),
|
||||
},
|
||||
{
|
||||
Name: proto.String("c"),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_ENUM.Enum(),
|
||||
TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
|
||||
Number: proto.Int32(2),
|
||||
},
|
||||
},
|
||||
EnumType: []*protodescriptor.EnumDescriptorProto{
|
||||
{
|
||||
Name: proto.String("DeepEnum"),
|
||||
Value: []*protodescriptor.EnumValueDescriptorProto{
|
||||
{Name: proto.String("FALSE"), Number: proto.Int32(0)},
|
||||
{Name: proto.String("TRUE"), Number: proto.Int32(1)},
|
||||
},
|
||||
},
|
||||
},
|
||||
}},
|
||||
},
|
||||
},
|
||||
Message: "ExampleMessage",
|
||||
Params: []swaggerParameterObject{
|
||||
swaggerParameterObject{
|
||||
Name: "nested.a",
|
||||
In: "query",
|
||||
Required: false,
|
||||
Type: "string",
|
||||
},
|
||||
swaggerParameterObject{
|
||||
Name: "nested.deep.b",
|
||||
In: "query",
|
||||
Required: false,
|
||||
Type: "string",
|
||||
},
|
||||
swaggerParameterObject{
|
||||
Name: "nested.deep.c",
|
||||
In: "query",
|
||||
Required: false,
|
||||
Type: "string",
|
||||
Enum: []string{"FALSE", "TRUE"},
|
||||
Default: "FALSE",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
reg := descriptor.NewRegistry()
|
||||
msgs := []*descriptor.Message{}
|
||||
for _, msgdesc := range test.MsgDescs {
|
||||
msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
|
||||
}
|
||||
file := descriptor.File{
|
||||
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
|
||||
SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
|
||||
Name: proto.String("example.proto"),
|
||||
Package: proto.String("example"),
|
||||
Dependency: []string{},
|
||||
MessageType: test.MsgDescs,
|
||||
Service: []*protodescriptor.ServiceDescriptorProto{},
|
||||
},
|
||||
GoPkg: descriptor.GoPackage{
|
||||
Path: "example.com/path/to/example/example.pb",
|
||||
Name: "example_pb",
|
||||
},
|
||||
Messages: msgs,
|
||||
}
|
||||
reg.Load(&plugin.CodeGeneratorRequest{
|
||||
ProtoFile: []*protodescriptor.FileDescriptorProto{file.FileDescriptorProto},
|
||||
})
|
||||
|
||||
message, err := reg.LookupMsg("", ".example."+test.Message)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to lookup message: %s", err)
|
||||
}
|
||||
params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{})
|
||||
if err != nil {
|
||||
t.Fatalf("failed to convert message to query parameters: %s", err)
|
||||
}
|
||||
if !reflect.DeepEqual(params, test.Params) {
|
||||
t.Errorf("expected %v, got %v", test.Params, params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyTemplateSimple(t *testing.T) {
|
||||
msgdesc := &protodescriptor.DescriptorProto{
|
||||
Name: proto.String("ExampleMessage"),
|
||||
}
|
||||
meth := &protodescriptor.MethodDescriptorProto{
|
||||
Name: proto.String("Example"),
|
||||
InputType: proto.String("ExampleMessage"),
|
||||
OutputType: proto.String("ExampleMessage"),
|
||||
}
|
||||
svc := &protodescriptor.ServiceDescriptorProto{
|
||||
Name: proto.String("ExampleService"),
|
||||
Method: []*protodescriptor.MethodDescriptorProto{meth},
|
||||
}
|
||||
msg := &descriptor.Message{
|
||||
DescriptorProto: msgdesc,
|
||||
}
|
||||
file := descriptor.File{
|
||||
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
|
||||
SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
|
||||
Name: proto.String("example.proto"),
|
||||
Package: proto.String("example"),
|
||||
Dependency: []string{"a.example/b/c.proto", "a.example/d/e.proto"},
|
||||
MessageType: []*protodescriptor.DescriptorProto{msgdesc},
|
||||
Service: []*protodescriptor.ServiceDescriptorProto{svc},
|
||||
},
|
||||
GoPkg: descriptor.GoPackage{
|
||||
Path: "example.com/path/to/example/example.pb",
|
||||
Name: "example_pb",
|
||||
},
|
||||
Messages: []*descriptor.Message{msg},
|
||||
Services: []*descriptor.Service{
|
||||
{
|
||||
ServiceDescriptorProto: svc,
|
||||
Methods: []*descriptor.Method{
|
||||
{
|
||||
MethodDescriptorProto: meth,
|
||||
RequestType: msg,
|
||||
ResponseType: msg,
|
||||
Bindings: []*descriptor.Binding{
|
||||
{
|
||||
HTTPMethod: "GET",
|
||||
Body: &descriptor.Body{FieldPath: nil},
|
||||
PathTmpl: httprule.Template{
|
||||
Version: 1,
|
||||
OpCodes: []int{0, 0},
|
||||
Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: descriptor.NewRegistry()})
|
||||
if err != nil {
|
||||
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
|
||||
return
|
||||
}
|
||||
got := new(swaggerObject)
|
||||
err = json.Unmarshal([]byte(result), got)
|
||||
if err != nil {
|
||||
t.Errorf("json.Unmarshal(%s) failed with %v; want success", result, err)
|
||||
return
|
||||
}
|
||||
if want, is, name := "2.0", got.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
|
||||
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
|
||||
}
|
||||
if want, is, name := "", got.BasePath, "BasePath"; !reflect.DeepEqual(is, want) {
|
||||
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
|
||||
}
|
||||
if want, is, name := []string{"http", "https"}, got.Schemes, "Schemes"; !reflect.DeepEqual(is, want) {
|
||||
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
|
||||
}
|
||||
if want, is, name := []string{"application/json"}, got.Consumes, "Consumes"; !reflect.DeepEqual(is, want) {
|
||||
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
|
||||
}
|
||||
if want, is, name := []string{"application/json"}, got.Produces, "Produces"; !reflect.DeepEqual(is, want) {
|
||||
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
|
||||
}
|
||||
|
||||
// If there was a failure, print out the input and the json result for debugging.
|
||||
if t.Failed() {
|
||||
t.Errorf("had: %s", file)
|
||||
t.Errorf("got: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) {
|
||||
t.Skip()
|
||||
msgdesc := &protodescriptor.DescriptorProto{
|
||||
Name: proto.String("ExampleMessage"),
|
||||
Field: []*protodescriptor.FieldDescriptorProto{
|
||||
{
|
||||
Name: proto.String("nested"),
|
||||
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
|
||||
TypeName: proto.String("NestedMessage"),
|
||||
Number: proto.Int32(1),
|
||||
},
|
||||
},
|
||||
}
|
||||
nesteddesc := &protodescriptor.DescriptorProto{
|
||||
Name: proto.String("NestedMessage"),
|
||||
Field: []*protodescriptor.FieldDescriptorProto{
|
||||
{
|
||||
Name: proto.String("int32"),
|
||||
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
|
||||
Number: proto.Int32(1),
|
||||
},
|
||||
{
|
||||
Name: proto.String("bool"),
|
||||
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
|
||||
Number: proto.Int32(2),
|
||||
},
|
||||
},
|
||||
}
|
||||
meth := &protodescriptor.MethodDescriptorProto{
|
||||
Name: proto.String("Echo"),
|
||||
InputType: proto.String("ExampleMessage"),
|
||||
OutputType: proto.String("ExampleMessage"),
|
||||
ClientStreaming: proto.Bool(false),
|
||||
}
|
||||
svc := &protodescriptor.ServiceDescriptorProto{
|
||||
Name: proto.String("ExampleService"),
|
||||
Method: []*protodescriptor.MethodDescriptorProto{meth},
|
||||
}
|
||||
|
||||
meth.ServerStreaming = proto.Bool(false)
|
||||
|
||||
msg := &descriptor.Message{
|
||||
DescriptorProto: msgdesc,
|
||||
}
|
||||
nested := &descriptor.Message{
|
||||
DescriptorProto: nesteddesc,
|
||||
}
|
||||
|
||||
nestedField := &descriptor.Field{
|
||||
Message: msg,
|
||||
FieldDescriptorProto: msg.GetField()[0],
|
||||
}
|
||||
intField := &descriptor.Field{
|
||||
Message: nested,
|
||||
FieldDescriptorProto: nested.GetField()[0],
|
||||
}
|
||||
boolField := &descriptor.Field{
|
||||
Message: nested,
|
||||
FieldDescriptorProto: nested.GetField()[1],
|
||||
}
|
||||
file := descriptor.File{
|
||||
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
|
||||
SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
|
||||
Name: proto.String("example.proto"),
|
||||
Package: proto.String("example"),
|
||||
MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
|
||||
Service: []*protodescriptor.ServiceDescriptorProto{svc},
|
||||
},
|
||||
GoPkg: descriptor.GoPackage{
|
||||
Path: "example.com/path/to/example/example.pb",
|
||||
Name: "example_pb",
|
||||
},
|
||||
Messages: []*descriptor.Message{msg, nested},
|
||||
Services: []*descriptor.Service{
|
||||
{
|
||||
ServiceDescriptorProto: svc,
|
||||
Methods: []*descriptor.Method{
|
||||
{
|
||||
MethodDescriptorProto: meth,
|
||||
RequestType: msg,
|
||||
ResponseType: msg,
|
||||
Bindings: []*descriptor.Binding{
|
||||
{
|
||||
HTTPMethod: "POST",
|
||||
PathTmpl: httprule.Template{
|
||||
Version: 1,
|
||||
OpCodes: []int{0, 0},
|
||||
Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be
|
||||
},
|
||||
PathParams: []descriptor.Parameter{
|
||||
{
|
||||
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
|
||||
{
|
||||
Name: "nested",
|
||||
Target: nestedField,
|
||||
},
|
||||
{
|
||||
Name: "int32",
|
||||
Target: intField,
|
||||
},
|
||||
}),
|
||||
Target: intField,
|
||||
},
|
||||
},
|
||||
Body: &descriptor.Body{
|
||||
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
|
||||
{
|
||||
Name: "nested",
|
||||
Target: nestedField,
|
||||
},
|
||||
{
|
||||
Name: "bool",
|
||||
Target: boolField,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
result, err := applyTemplate(param{File: crossLinkFixture(&file)})
|
||||
if err != nil {
|
||||
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
|
||||
return
|
||||
}
|
||||
var obj swaggerObject
|
||||
err = json.Unmarshal([]byte(result), &obj)
|
||||
if err != nil {
|
||||
t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
|
||||
return
|
||||
}
|
||||
if want, got := "2.0", obj.Swagger; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("applyTemplate(%#v).Swagger = %s want to be %s", file, got, want)
|
||||
}
|
||||
if want, got := "", obj.BasePath; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("applyTemplate(%#v).BasePath = %s want to be %s", file, got, want)
|
||||
}
|
||||
if want, got := []string{"http", "https"}, obj.Schemes; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("applyTemplate(%#v).Schemes = %s want to be %s", file, got, want)
|
||||
}
|
||||
if want, got := []string{"application/json"}, obj.Consumes; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("applyTemplate(%#v).Consumes = %s want to be %s", file, got, want)
|
||||
}
|
||||
if want, got := []string{"application/json"}, obj.Produces; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("applyTemplate(%#v).Produces = %s want to be %s", file, got, want)
|
||||
}
|
||||
if want, got, name := "Generated for ExampleService.Echo - ", obj.Paths["/v1/echo"].Post.Summary, "Paths[/v1/echo].Post.Summary"; !reflect.DeepEqual(got, want) {
|
||||
t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
|
||||
}
|
||||
|
||||
// If there was a failure, print out the input and the json result for debugging.
|
||||
if t.Failed() {
|
||||
t.Errorf("had: %s", file)
|
||||
t.Errorf("got: %s", result)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyTemplateRequestWithClientStreaming(t *testing.T) {
|
||||
t.Skip()
|
||||
msgdesc := &protodescriptor.DescriptorProto{
|
||||
Name: proto.String("ExampleMessage"),
|
||||
Field: []*protodescriptor.FieldDescriptorProto{
|
||||
{
|
||||
Name: proto.String("nested"),
|
||||
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
|
||||
TypeName: proto.String("NestedMessage"),
|
||||
Number: proto.Int32(1),
|
||||
},
|
||||
},
|
||||
}
|
||||
nesteddesc := &protodescriptor.DescriptorProto{
|
||||
Name: proto.String("NestedMessage"),
|
||||
Field: []*protodescriptor.FieldDescriptorProto{
|
||||
{
|
||||
Name: proto.String("int32"),
|
||||
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_INT32.Enum(),
|
||||
Number: proto.Int32(1),
|
||||
},
|
||||
{
|
||||
Name: proto.String("bool"),
|
||||
Label: protodescriptor.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
|
||||
Type: protodescriptor.FieldDescriptorProto_TYPE_BOOL.Enum(),
|
||||
Number: proto.Int32(2),
|
||||
},
|
||||
},
|
||||
}
|
||||
meth := &protodescriptor.MethodDescriptorProto{
|
||||
Name: proto.String("Echo"),
|
||||
InputType: proto.String("ExampleMessage"),
|
||||
OutputType: proto.String("ExampleMessage"),
|
||||
ClientStreaming: proto.Bool(true),
|
||||
ServerStreaming: proto.Bool(true),
|
||||
}
|
||||
svc := &protodescriptor.ServiceDescriptorProto{
|
||||
Name: proto.String("ExampleService"),
|
||||
Method: []*protodescriptor.MethodDescriptorProto{meth},
|
||||
}
|
||||
|
||||
msg := &descriptor.Message{
|
||||
DescriptorProto: msgdesc,
|
||||
}
|
||||
nested := &descriptor.Message{
|
||||
DescriptorProto: nesteddesc,
|
||||
}
|
||||
|
||||
nestedField := &descriptor.Field{
|
||||
Message: msg,
|
||||
FieldDescriptorProto: msg.GetField()[0],
|
||||
}
|
||||
intField := &descriptor.Field{
|
||||
Message: nested,
|
||||
FieldDescriptorProto: nested.GetField()[0],
|
||||
}
|
||||
boolField := &descriptor.Field{
|
||||
Message: nested,
|
||||
FieldDescriptorProto: nested.GetField()[1],
|
||||
}
|
||||
file := descriptor.File{
|
||||
FileDescriptorProto: &protodescriptor.FileDescriptorProto{
|
||||
SourceCodeInfo: &protodescriptor.SourceCodeInfo{},
|
||||
Name: proto.String("example.proto"),
|
||||
Package: proto.String("example"),
|
||||
MessageType: []*protodescriptor.DescriptorProto{msgdesc, nesteddesc},
|
||||
Service: []*protodescriptor.ServiceDescriptorProto{svc},
|
||||
},
|
||||
GoPkg: descriptor.GoPackage{
|
||||
Path: "example.com/path/to/example/example.pb",
|
||||
Name: "example_pb",
|
||||
},
|
||||
Messages: []*descriptor.Message{msg, nested},
|
||||
Services: []*descriptor.Service{
|
||||
{
|
||||
ServiceDescriptorProto: svc,
|
||||
Methods: []*descriptor.Method{
|
||||
{
|
||||
MethodDescriptorProto: meth,
|
||||
RequestType: msg,
|
||||
ResponseType: msg,
|
||||
Bindings: []*descriptor.Binding{
|
||||
{
|
||||
HTTPMethod: "POST",
|
||||
PathTmpl: httprule.Template{
|
||||
Version: 1,
|
||||
OpCodes: []int{0, 0},
|
||||
Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be
|
||||
},
|
||||
PathParams: []descriptor.Parameter{
|
||||
{
|
||||
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
|
||||
{
|
||||
Name: "nested",
|
||||
Target: nestedField,
|
||||
},
|
||||
{
|
||||
Name: "int32",
|
||||
Target: intField,
|
||||
},
|
||||
}),
|
||||
Target: intField,
|
||||
},
|
||||
},
|
||||
Body: &descriptor.Body{
|
||||
FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
|
||||
{
|
||||
Name: "nested",
|
||||
Target: nestedField,
|
||||
},
|
||||
{
|
||||
Name: "bool",
|
||||
Target: boolField,
|
||||
},
|
||||
}),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
_, err := applyTemplate(param{File: crossLinkFixture(&file)})
|
||||
if err == nil {
|
||||
t.Errorf("applyTemplate(%#v) should have failed cause swagger doesn't support streaming", file)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func TestTemplateToSwaggerPath(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"/test", "/test"},
|
||||
{"/{test}", "/{test}"},
|
||||
{"/{test=prefix/*}", "/{test}"},
|
||||
{"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
|
||||
{"/{test1}/{test2}", "/{test1}/{test2}"},
|
||||
{"/{test1}/{test2}/", "/{test1}/{test2}/"},
|
||||
}
|
||||
|
||||
for _, data := range tests {
|
||||
actual := templateToSwaggerPath(data.input)
|
||||
if data.expected != actual {
|
||||
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveFullyQualifiedNameToSwaggerName(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
output string
|
||||
listOfFQMNs []string
|
||||
}{
|
||||
{
|
||||
".a.b.C",
|
||||
"C",
|
||||
[]string{
|
||||
".a.b.C",
|
||||
},
|
||||
},
|
||||
{
|
||||
".a.b.C",
|
||||
"abC",
|
||||
[]string{
|
||||
".a.C",
|
||||
".a.b.C",
|
||||
},
|
||||
},
|
||||
{
|
||||
".a.b.C",
|
||||
"abC",
|
||||
[]string{
|
||||
".C",
|
||||
".a.C",
|
||||
".a.b.C",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, data := range tests {
|
||||
output := resolveFullyQualifiedNameToSwaggerName(data.input, data.listOfFQMNs)
|
||||
if output != data.output {
|
||||
t.Errorf("Expected fullyQualifiedNameToSwaggerName(%v) to be %s but got %s",
|
||||
data.input, data.output, output)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFQMNtoSwaggerName(t *testing.T) {
|
||||
var tests = []struct {
|
||||
input string
|
||||
expected string
|
||||
}{
|
||||
{"/test", "/test"},
|
||||
{"/{test}", "/{test}"},
|
||||
{"/{test=prefix/*}", "/{test}"},
|
||||
{"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
|
||||
{"/{test1}/{test2}", "/{test1}/{test2}"},
|
||||
{"/{test1}/{test2}/", "/{test1}/{test2}/"},
|
||||
}
|
||||
|
||||
for _, data := range tests {
|
||||
actual := templateToSwaggerPath(data.input)
|
||||
if data.expected != actual {
|
||||
t.Errorf("Expected templateToSwaggerPath(%v) = %v, actual: %v", data.input, data.expected, actual)
|
||||
}
|
||||
}
|
||||
}
|
189
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/types.go
generated
vendored
Normal file
189
vendor/github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger/genswagger/types.go
generated
vendored
Normal file
@@ -0,0 +1,189 @@
|
||||
package genswagger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor"
|
||||
)
|
||||
|
||||
type param struct {
|
||||
*descriptor.File
|
||||
reg *descriptor.Registry
|
||||
}
|
||||
|
||||
type binding struct {
|
||||
*descriptor.Binding
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#infoObject
|
||||
type swaggerInfoObject struct {
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description,omitempty"`
|
||||
TermsOfService string `json:"termsOfService,omitempty"`
|
||||
Version string `json:"version"`
|
||||
|
||||
Contact *swaggerContactObject `json:"contact,omitempty"`
|
||||
License *swaggerLicenseObject `json:"license,omitempty"`
|
||||
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#contactObject
|
||||
type swaggerContactObject struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Email string `json:"email,omitempty"`
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#licenseObject
|
||||
type swaggerLicenseObject struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#externalDocumentationObject
|
||||
type swaggerExternalDocumentationObject struct {
|
||||
Description string `json:"description,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#swaggerObject
|
||||
type swaggerObject struct {
|
||||
Swagger string `json:"swagger"`
|
||||
Info swaggerInfoObject `json:"info"`
|
||||
Host string `json:"host,omitempty"`
|
||||
BasePath string `json:"basePath,omitempty"`
|
||||
Schemes []string `json:"schemes"`
|
||||
Consumes []string `json:"consumes"`
|
||||
Produces []string `json:"produces"`
|
||||
Paths swaggerPathsObject `json:"paths"`
|
||||
Definitions swaggerDefinitionsObject `json:"definitions"`
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#pathsObject
|
||||
type swaggerPathsObject map[string]swaggerPathItemObject
|
||||
|
||||
// http://swagger.io/specification/#pathItemObject
|
||||
type swaggerPathItemObject struct {
|
||||
Get *swaggerOperationObject `json:"get,omitempty"`
|
||||
Delete *swaggerOperationObject `json:"delete,omitempty"`
|
||||
Post *swaggerOperationObject `json:"post,omitempty"`
|
||||
Put *swaggerOperationObject `json:"put,omitempty"`
|
||||
Patch *swaggerOperationObject `json:"patch,omitempty"`
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#operationObject
|
||||
type swaggerOperationObject struct {
|
||||
Summary string `json:"summary,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
OperationID string `json:"operationId"`
|
||||
Responses swaggerResponsesObject `json:"responses"`
|
||||
Parameters swaggerParametersObject `json:"parameters,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
|
||||
ExternalDocs *swaggerExternalDocumentationObject `json:"externalDocs,omitempty"`
|
||||
}
|
||||
|
||||
type swaggerParametersObject []swaggerParameterObject
|
||||
|
||||
// http://swagger.io/specification/#parameterObject
|
||||
type swaggerParameterObject struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description,omitempty"`
|
||||
In string `json:"in,omitempty"`
|
||||
Required bool `json:"required"`
|
||||
Type string `json:"type,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
Items *swaggerItemsObject `json:"items,omitempty"`
|
||||
Enum []string `json:"enum,omitempty"`
|
||||
Default string `json:"default,omitempty"`
|
||||
|
||||
// Or you can explicitly refer to another type. If this is defined all
|
||||
// other fields should be empty
|
||||
Schema *swaggerSchemaObject `json:"schema,omitempty"`
|
||||
}
|
||||
|
||||
// core part of schema, which is common to itemsObject and schemaObject.
|
||||
// http://swagger.io/specification/#itemsObject
|
||||
type schemaCore struct {
|
||||
Type string `json:"type,omitempty"`
|
||||
Format string `json:"format,omitempty"`
|
||||
Ref string `json:"$ref,omitempty"`
|
||||
|
||||
Items *swaggerItemsObject `json:"items,omitempty"`
|
||||
|
||||
// If the item is an enumeration include a list of all the *NAMES* of the
|
||||
// enum values. I'm not sure how well this will work but assuming all enums
|
||||
// start from 0 index it will be great. I don't think that is a good assumption.
|
||||
Enum []string `json:"enum,omitempty"`
|
||||
Default string `json:"default,omitempty"`
|
||||
}
|
||||
|
||||
type swaggerItemsObject schemaCore
|
||||
|
||||
// http://swagger.io/specification/#responsesObject
|
||||
type swaggerResponsesObject map[string]swaggerResponseObject
|
||||
|
||||
// http://swagger.io/specification/#responseObject
|
||||
type swaggerResponseObject struct {
|
||||
Description string `json:"description"`
|
||||
Schema swaggerSchemaObject `json:"schema"`
|
||||
}
|
||||
|
||||
type keyVal struct {
|
||||
Key string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
type swaggerSchemaObjectProperties []keyVal
|
||||
|
||||
func (op swaggerSchemaObjectProperties) MarshalJSON() ([]byte, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("{")
|
||||
for i, kv := range op {
|
||||
if i != 0 {
|
||||
buf.WriteString(",")
|
||||
}
|
||||
key, err := json.Marshal(kv.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(key)
|
||||
buf.WriteString(":")
|
||||
val, err := json.Marshal(kv.Value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buf.Write(val)
|
||||
}
|
||||
|
||||
buf.WriteString("}")
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#schemaObject
|
||||
type swaggerSchemaObject struct {
|
||||
schemaCore
|
||||
// Properties can be recursively defined
|
||||
Properties swaggerSchemaObjectProperties `json:"properties,omitempty"`
|
||||
AdditionalProperties *swaggerSchemaObject `json:"additionalProperties,omitempty"`
|
||||
|
||||
Description string `json:"description,omitempty"`
|
||||
Title string `json:"title,omitempty"`
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#referenceObject
|
||||
type swaggerReferenceObject struct {
|
||||
Ref string `json:"$ref"`
|
||||
}
|
||||
|
||||
// http://swagger.io/specification/#definitionsObject
|
||||
type swaggerDefinitionsObject map[string]swaggerSchemaObject
|
||||
|
||||
// Internal type mapping from FQMN to descriptor.Message. Used as a set by the
|
||||
// findServiceMessages function.
|
||||
type messageMap map[string]*descriptor.Message
|
||||
|
||||
// Internal type mapping from FQEN to descriptor.Enum. Used as a set by the
|
||||
// findServiceMessages function.
|
||||
type enumMap map[string]*descriptor.Enum
|
Reference in New Issue
Block a user