github.com/cbroglie/openapi2proto@v0.0.0-20171004221549-76b8501da882/openapi.go (about)

     1  package openapi2proto
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"log"
     7  	"net/url"
     8  	"path"
     9  	"regexp"
    10  	"strings"
    11  )
    12  
    13  // APIDefinition is the base struct for containing OpenAPI spec
    14  // declarations.
    15  type APIDefinition struct {
    16  	FileName string // internal use to pass file path
    17  	Swagger  string `yaml:"swagger" json:"swagger"`
    18  	Info     struct {
    19  		Title       string `yaml:"title" json:"title"`
    20  		Description string `yaml:"description" json:"description"`
    21  		Version     string `yaml:"version" json:"version"`
    22  	} `yaml:"info" json:"info"`
    23  	Host        string            `yaml:"host" json:"host"`
    24  	Schemes     []string          `yaml:"schemes" json:"schemes"`
    25  	BasePath    string            `yaml:"basePath" json:"basePath"`
    26  	Produces    []string          `yaml:"produces" json:"produces"`
    27  	Paths       map[string]*Path  `yaml:"paths" json:"paths"`
    28  	Definitions map[string]*Items `yaml:"definitions" json:"definitions"`
    29  	Parameters  map[string]*Items `yaml:"parameters" json:"parameters"`
    30  }
    31  
    32  // Path represents all of the endpoints and parameters available for a single
    33  // path.
    34  type Path struct {
    35  	Get        *Endpoint  `yaml:"get" json:"get"`
    36  	Put        *Endpoint  `yaml:"put" json:"put"`
    37  	Post       *Endpoint  `yaml:"post" json:"post"`
    38  	Delete     *Endpoint  `yaml:"delete" json:"delete"`
    39  	Parameters Parameters `yaml:"parameters" json:"parameters"`
    40  }
    41  
    42  // Parameters is a slice of request parameters for a single endpoint.
    43  type Parameters []*Items
    44  
    45  // Response represents the response object in an OpenAPI spec.
    46  type Response struct {
    47  	Description string `yaml:"description" json:"description"`
    48  	Schema      *Items `yaml:"schema" json:"schema"`
    49  }
    50  
    51  // Endpoint represents an endpoint for a path in an OpenAPI spec.
    52  type Endpoint struct {
    53  	Summary     string               `yaml:"summary" json:"summary"`
    54  	Description string               `yaml:"description" json:"description"`
    55  	Parameters  Parameters           `yaml:"parameters" json:"parameters"`
    56  	Tags        []string             `yaml:"tags" json:"tags"`
    57  	Responses   map[string]*Response `yaml:"responses" json:"responses"`
    58  }
    59  
    60  // Model represents a model definition from an OpenAPI spec.
    61  type Model struct {
    62  	Properties map[string]*Items `yaml:"properties" json:"properties"`
    63  	Name       string
    64  	Depth      int
    65  }
    66  
    67  // Items represent Model properties in an OpenAPI spec.
    68  type Items struct {
    69  	Description string `yaml:"description,omitempty" json:"description,omitempty"`
    70  	// scalar
    71  	Type   interface{} `yaml:"type" json:"type"`
    72  	Format interface{} `yaml:"format,omitempty" json:"format,omitempty"`
    73  	Enum   []string    `yaml:"enum,omitempty" json:"enum,omitempty"`
    74  
    75  	ProtoTag int `yaml:"x-proto-tag" json:"x-proto-tag"`
    76  
    77  	// Map type
    78  	AdditionalProperties *Items `yaml:"additionalProperties" json:"additionalProperties"`
    79  
    80  	// ref another Model
    81  	Ref string `yaml:"$ref"json:"$ref"`
    82  
    83  	// is an array
    84  	Items *Items `yaml:"items" json:"items"`
    85  
    86  	// for request parameters
    87  	In     string `yaml:"in" json:"in"`
    88  	Schema *Items `yaml:"schema" json:"schema"`
    89  
    90  	// is an other Model
    91  	Model `yaml:",inline"`
    92  }
    93  
    94  func (i Items) Comment() string {
    95  	return prepComment(i.Description, "    ")
    96  }
    97  
    98  func (i Items) HasComment() bool {
    99  	return i.Description != ""
   100  }
   101  
   102  func protoScalarType(name string, typ, frmt interface{}, indx int) string {
   103  	frmat := format(frmt)
   104  	switch typ.(type) {
   105  	case string:
   106  		switch typ.(string) {
   107  		case "string":
   108  			return fmt.Sprintf("string %s = %d", name, indx)
   109  		case "bytes":
   110  			return fmt.Sprintf("bytes %s = %d", name, indx)
   111  		case "number":
   112  			if frmat == "" {
   113  				frmat = "double"
   114  			}
   115  			return fmt.Sprintf("%s %s = %d", frmat, name, indx)
   116  		case "integer":
   117  			if frmat == "" {
   118  				frmat = "int32"
   119  			}
   120  			return fmt.Sprintf("%s %s = %d", frmat, name, indx)
   121  		case "boolean":
   122  			return fmt.Sprintf("bool %s = %d", name, indx)
   123  		case "null":
   124  			return fmt.Sprintf("google.protobuf.NullValue %s = %d", name, indx)
   125  		}
   126  	}
   127  
   128  	return ""
   129  }
   130  
   131  func refDatas(ref string) (string, string) {
   132  	// split on '#/'
   133  	refDatas := strings.SplitN(ref, "#/", 2)
   134  	// check for references outside of this spec
   135  	if len(refDatas) > 1 {
   136  		return refDatas[0], refDatas[1]
   137  	}
   138  	return ref, ""
   139  }
   140  
   141  // $ref should be in the format of:
   142  // {import path}#/{definitions|parameters}/{typeName}
   143  // this will produce
   144  func refType(ref string, defs map[string]*Items) (string, string) {
   145  	var (
   146  		rawPkg   string
   147  		pkg      string
   148  		itemType string
   149  	)
   150  
   151  	rawPkg, itemType = refDatas(ref)
   152  
   153  	if rawPkg != "" ||
   154  		strings.HasSuffix(ref, ".json") ||
   155  		strings.HasSuffix(ref, ".yaml") {
   156  		if rawPkg == "" {
   157  			rawPkg = ref
   158  		}
   159  		// if URL, parse it
   160  		if strings.HasPrefix(rawPkg, "http") {
   161  			u, err := url.Parse(rawPkg)
   162  			if err != nil {
   163  				log.Fatalf("invalid external reference URL: %s: %s", ref, err)
   164  			}
   165  			rawPkg = u.Path
   166  		}
   167  
   168  		rawPkg = path.Clean(rawPkg)
   169  		rawPkg = strings.TrimPrefix(rawPkg, "/")
   170  		rawPkg = strings.ToLower(rawPkg)
   171  		// take out possible file types
   172  		rawPkg = strings.TrimSuffix(rawPkg, path.Ext(rawPkg))
   173  		rawPkg = strings.TrimLeft(rawPkg, "/.")
   174  	}
   175  
   176  	// in case it's a nested reference
   177  	itemType = strings.TrimPrefix(itemType, "definitions/")
   178  	itemType = strings.TrimPrefix(itemType, "parameters/")
   179  	itemType = strings.TrimPrefix(itemType, "responses/")
   180  	itemType = strings.TrimSuffix(itemType, ".yaml")
   181  	itemType = strings.TrimSuffix(itemType, ".json")
   182  	itemType = strings.TrimSuffix(itemType, ".proto")
   183  	if i, ok := defs[itemType]; ok {
   184  		if i.Type != "object" && !(i.Type == "string" && len(i.Enum) > 0) {
   185  			typ, ok := i.Type.(string)
   186  			if !ok {
   187  				log.Fatalf("invalid $ref object referenced with a type of %s", i.Type)
   188  			}
   189  			itemType = typ
   190  		}
   191  	}
   192  	if rawPkg != "" {
   193  		pkg = rawPkg + ".proto"
   194  		if itemType != "" {
   195  			rawPkg = rawPkg + "/" + itemType
   196  		}
   197  		dir, name := path.Split(rawPkg)
   198  		if !strings.Contains(name, ".") {
   199  			itemType = strings.Replace(dir, "/", ".", -1) + strings.Title(name)
   200  		}
   201  	}
   202  	return itemType, pkg
   203  }
   204  
   205  func refDef(name, ref string, index int, defs map[string]*Items) string {
   206  	itemType, _ := refType(ref, defs)
   207  	return fmt.Sprintf("%s %s = %d", itemType, name, index)
   208  }
   209  
   210  // ProtoMessage will generate a set of fields for a protobuf v3 schema given the
   211  // current Items and information.
   212  func (i *Items) ProtoMessage(msgName, name string, defs map[string]*Items, indx *int, depth int) string {
   213  	*indx++
   214  	if i.ProtoTag != 0 {
   215  		*indx = i.ProtoTag
   216  	}
   217  	index := *indx
   218  	name = strings.Replace(name, "-", "_", -1)
   219  
   220  	if i.Ref != "" {
   221  		return refDef(name, i.Ref, index, defs)
   222  	}
   223  
   224  	// for parameters
   225  	if i.Schema != nil {
   226  		if i.Schema.Ref != "" {
   227  			return refDef(name, i.Schema.Ref, index, defs)
   228  		}
   229  		return protoComplex(i.Schema, i.Schema.Type.(string), msgName, name, defs, indx, depth)
   230  	}
   231  
   232  	switch i.Type.(type) {
   233  	case string:
   234  		return protoComplex(i, i.Type.(string), msgName, name, defs, indx, depth)
   235  	case []interface{}:
   236  		types := i.Type.([]interface{})
   237  		hasNull := false
   238  		var otherTypes []string
   239  		for _, itp := range types {
   240  			tp := itp.(string)
   241  			if strings.ToLower(tp) == "null" {
   242  				hasNull = true
   243  				continue
   244  			}
   245  			otherTypes = append(otherTypes, tp)
   246  		}
   247  		// non-nullable fields with multiple types? Make it an Any.
   248  		if !hasNull || len(otherTypes) > 1 {
   249  			if depth >= 0 {
   250  				return fmt.Sprintf("google.protobuf.Any %s = %d", name, *indx)
   251  			}
   252  			return ""
   253  		}
   254  
   255  		if depth < 0 {
   256  			return ""
   257  		}
   258  
   259  		switch otherTypes[0] {
   260  		case "string":
   261  			return fmt.Sprintf("google.protobuf.StringValue %s = %d", name, *indx)
   262  		case "number":
   263  			frmat := format(i.Format)
   264  			if frmat == "" {
   265  				frmat = "Double"
   266  			} else {
   267  				frmat = strings.Title(frmat)
   268  			}
   269  			return fmt.Sprintf("google.protobuf.%sValue %s = %d", frmat, name, *indx)
   270  		case "integer":
   271  			frmat := format(i.Format)
   272  			if frmat == "" {
   273  				frmat = "Int32"
   274  			}
   275  			frmat = strings.Title(frmat)
   276  			// unsigned ints :\
   277  			if strings.HasPrefix(frmat, "Ui") {
   278  				frmat = strings.TrimPrefix(frmat, "Ui")
   279  				frmat = "UI" + frmat
   280  			}
   281  			return fmt.Sprintf("google.protobuf.%sValue %s = %d", frmat, name, *indx)
   282  		case "bytes":
   283  			return fmt.Sprintf("google.protobuf.BytesValue %s = %d", name, *indx)
   284  		case "boolean":
   285  			return fmt.Sprintf("google.protobuf.BoolValue %s = %d", name, *indx)
   286  		default:
   287  			if depth >= 0 {
   288  				return fmt.Sprintf("google.protobuf.Any %s = %d", name, *indx)
   289  			}
   290  		}
   291  	}
   292  
   293  	if depth >= 0 {
   294  		return protoScalarType(name, i.Type, i.Format, index)
   295  	}
   296  	return ""
   297  }
   298  
   299  func protoComplex(i *Items, typ, msgName, name string, defs map[string]*Items, index *int, depth int) string {
   300  	switch typ {
   301  	case "object":
   302  		// check for map declaration
   303  		if i.AdditionalProperties != nil {
   304  			var itemType string
   305  			switch {
   306  			case i.AdditionalProperties.Ref != "":
   307  				itemType, _ = refType(i.AdditionalProperties.Ref, defs)
   308  			case i.AdditionalProperties.Type != nil:
   309  				itemType = i.AdditionalProperties.Type.(string)
   310  			}
   311  			return fmt.Sprintf("map<string, %s> %s = %d", itemType, name, *index)
   312  		}
   313  
   314  		// check for referenced schema object (parameters/fields)
   315  		if i.Schema != nil {
   316  			if i.Schema.Ref != "" {
   317  				return refDef(indent(depth+1)+name, i.Schema.Ref, *index, defs)
   318  			}
   319  		}
   320  
   321  		// otherwise, normal object model
   322  		i.Model.Name = strings.Title(name)
   323  		msgStr := i.Model.ProtoModel(i.Model.Name, depth+1, defs)
   324  		if depth < 0 {
   325  			return msgStr
   326  		}
   327  		return fmt.Sprintf("%s\n%s%s %s = %d", msgStr, indent(depth+1), i.Model.Name, name, *index)
   328  	case "array":
   329  		if i.Items != nil {
   330  			// CHECK FOR SCALAR
   331  			pt := protoScalarType(name, i.Items.Type, i.Items.Format, *index)
   332  			if pt != "" {
   333  				return fmt.Sprintf("repeated %s", pt)
   334  			}
   335  
   336  			// CHECK FOR REF
   337  			if i.Items.Ref != "" {
   338  				return "repeated " + refDef(name, i.Items.Ref, *index, defs)
   339  			}
   340  
   341  			// breaks on 'Class' :\
   342  			if !strings.HasSuffix(name, "ss") {
   343  				i.Items.Model.Name = strings.Title(strings.TrimSuffix(name, "s"))
   344  			} else {
   345  				i.Items.Model.Name = strings.Title(name)
   346  			}
   347  			msgStr := i.Items.Model.ProtoModel(i.Items.Model.Name, depth+1, defs)
   348  			return fmt.Sprintf("%s\n%srepeated %s %s = %d", msgStr, indent(depth+1), i.Items.Model.Name, name, *index)
   349  		}
   350  
   351  	case "string":
   352  		if len(i.Enum) > 0 {
   353  			var eName string
   354  			// breaks on 'Class' :\
   355  			if !strings.HasSuffix(name, "ss") {
   356  				eName = strings.TrimSuffix(name, "s")
   357  			} else {
   358  				eName = name
   359  			}
   360  
   361  			eName = strings.Title(eName)
   362  
   363  			if msgName != "" {
   364  				eName = strings.Title(msgName) + "_" + eName
   365  			}
   366  
   367  			msgStr := ProtoEnum(eName, i.Enum, depth+1)
   368  			if depth < 0 {
   369  				return msgStr
   370  			}
   371  			return fmt.Sprintf("%s\n%s%s %s = %d", msgStr, indent(depth+1), eName, name, *index)
   372  		}
   373  		if depth >= 0 {
   374  			return protoScalarType(name, i.Type, i.Format, *index)
   375  		}
   376  	default:
   377  		if depth >= 0 {
   378  			return protoScalarType(name, i.Type, i.Format, *index)
   379  		}
   380  	}
   381  	return ""
   382  }
   383  
   384  // ProtoEnum will generate a protobuf v3 enum declaration from
   385  // the given info.
   386  func ProtoEnum(name string, enums []string, depth int) string {
   387  	s := struct {
   388  		Name  string
   389  		Enum  []string
   390  		Depth int
   391  	}{
   392  		name, enums, depth,
   393  	}
   394  	var b bytes.Buffer
   395  	err := protoEnumTmpl.Execute(&b, s)
   396  	if err != nil {
   397  		log.Fatal("unable to protobuf model: ", err)
   398  	}
   399  	return b.String()
   400  }
   401  
   402  func PathMethodToName(path, method string) string {
   403  	var name string
   404  	path = strings.TrimSuffix(path, ".json")
   405  	path = strings.Replace(path, "-", " ", -1)
   406  	path = strings.Replace(path, ".", " ", -1)
   407  	path = strings.Replace(path, "/", " ", -1)
   408  	// Strip out illegal-for-identifier characters in the path, including any query string.
   409  	// Note that query strings are illegal in swagger paths, but some tooling seems to tolerate them.
   410  	re := regexp.MustCompile(`[\{\}\[\]()/\.]|\?.*`)
   411  	path = re.ReplaceAllString(path, "")
   412  	for _, nme := range strings.Fields(path) {
   413  		name += strings.Title(nme)
   414  	}
   415  	return strings.Title(method) + name
   416  }
   417  
   418  // ProtoMessage will return a protobuf message declaration
   419  // based on the response schema. If the response is an array
   420  // type, it will get wrapped in a generic message with a single
   421  // 'items' field to contain the array.
   422  func (r *Response) ProtoMessage(endpointName string, defs map[string]*Items) string {
   423  	name := endpointName + "Response"
   424  	if r.Schema == nil {
   425  		return ""
   426  	}
   427  	switch r.Schema.Type {
   428  	case "object":
   429  		return r.Schema.Model.ProtoModel(name, 0, defs)
   430  	case "array":
   431  		model := &Model{Properties: map[string]*Items{"items": r.Schema}}
   432  		return model.ProtoModel(name, 0, defs)
   433  	default:
   434  		return ""
   435  	}
   436  }
   437  
   438  func (r *Response) responseName(endpointName string) string {
   439  	if r.Schema == nil {
   440  		return "google.protobuf.Empty"
   441  	}
   442  	switch r.Schema.Type {
   443  	case "object", "array":
   444  		return endpointName + "Response"
   445  	default:
   446  		switch r.Schema.Ref {
   447  		case "":
   448  			return "google.protobuf.Empty"
   449  		default:
   450  			return strings.Title(
   451  				strings.TrimSuffix(
   452  					path.Base(r.Schema.Ref),
   453  					path.Ext(r.Schema.Ref),
   454  				))
   455  		}
   456  	}
   457  }
   458  
   459  func includeBody(parent, child Parameters) string {
   460  	params := append(parent, child...)
   461  	for _, param := range params {
   462  		if param.In == "body" {
   463  			return param.Name
   464  		}
   465  	}
   466  	return ""
   467  }
   468  
   469  var lineStart = regexp.MustCompile(`^`)
   470  var newLine = regexp.MustCompile(`\n`)
   471  
   472  func prepComment(comment, space string) string {
   473  	if comment == "" {
   474  		return ""
   475  	}
   476  	comment = lineStart.ReplaceAllString(comment, space+"// ")
   477  	comment = newLine.ReplaceAllString(comment, "\n"+space+"// ")
   478  	comment = strings.TrimRight(comment, "/ ")
   479  	if !strings.HasSuffix(comment, "\n") {
   480  		comment += "\n"
   481  	}
   482  	return comment
   483  }
   484  
   485  func (e *Endpoint) protoEndpoint(annotate bool, parentParams Parameters, base, path, method string) string {
   486  	reqName := "google.protobuf.Empty"
   487  	endpointName := PathMethodToName(path, method)
   488  	path = base + path
   489  
   490  	var bodyAttr string
   491  	if len(parentParams)+len(e.Parameters) > 0 {
   492  		bodyAttr = includeBody(parentParams, e.Parameters)
   493  		reqName = endpointName + "Request"
   494  	}
   495  
   496  	respName := "google.protobuf.Empty"
   497  	if resp, ok := e.Responses["200"]; ok {
   498  		respName = resp.responseName(endpointName)
   499  	} else if resp, ok := e.Responses["201"]; ok {
   500  		respName = resp.responseName(endpointName)
   501  	}
   502  
   503  	comment := e.Summary
   504  	if comment != "" && e.Description != "" {
   505  		if !strings.HasSuffix(comment, "\n") {
   506  			comment += "\n"
   507  		}
   508  		comment += "\n"
   509  	}
   510  
   511  	if e.Description != "" {
   512  		comment += e.Description
   513  	}
   514  
   515  	comment = prepComment(comment, "    ")
   516  
   517  	tData := struct {
   518  		Annotate     bool
   519  		Method       string
   520  		Name         string
   521  		RequestName  string
   522  		ResponseName string
   523  		Path         string
   524  		IncludeBody  bool
   525  		BodyAttr     string
   526  		Comment      string
   527  		HasComment   bool
   528  	}{
   529  		annotate,
   530  		method,
   531  		endpointName,
   532  		reqName,
   533  		respName,
   534  		path,
   535  		(bodyAttr != ""),
   536  		bodyAttr,
   537  		comment,
   538  		(comment != ""),
   539  	}
   540  
   541  	var b bytes.Buffer
   542  	err := protoEndpointTmpl.Execute(&b, tData)
   543  	if err != nil {
   544  		log.Fatal("unable to protobuf model: ", err)
   545  	}
   546  	return b.String()
   547  }
   548  
   549  func (e *Endpoint) protoMessages(parentParams Parameters, endpointName string, defs map[string]*Items) string {
   550  	var out bytes.Buffer
   551  	msg := e.Parameters.ProtoMessage(parentParams, endpointName, defs)
   552  	if msg != "" {
   553  		out.WriteString(msg + "\n\n")
   554  	}
   555  
   556  	if resp, ok := e.Responses["200"]; ok {
   557  		msg := resp.ProtoMessage(endpointName, defs)
   558  		if msg != "" {
   559  			out.WriteString(msg + "\n\n")
   560  		}
   561  	} else if resp, ok := e.Responses["201"]; ok {
   562  		msg := resp.ProtoMessage(endpointName, defs)
   563  		if msg != "" {
   564  			out.WriteString(msg + "\n\n")
   565  		}
   566  	}
   567  	return out.String()
   568  }
   569  
   570  // ProtoEndpoints will return any protobuf v3 endpoints for gRPC
   571  // service declarations.
   572  func (p *Path) ProtoEndpoints(annotate bool, base, path string) string {
   573  
   574  	var out bytes.Buffer
   575  	if p.Get != nil {
   576  		msg := p.Get.protoEndpoint(annotate, p.Parameters, base, path, "get")
   577  		out.WriteString(msg + "\n")
   578  	}
   579  	if p.Put != nil {
   580  		msg := p.Put.protoEndpoint(annotate, p.Parameters, base, path, "put")
   581  		out.WriteString(msg + "\n")
   582  	}
   583  	if p.Post != nil {
   584  		msg := p.Post.protoEndpoint(annotate, p.Parameters, base, path, "post")
   585  		out.WriteString(msg + "\n")
   586  	}
   587  	if p.Delete != nil {
   588  		msg := p.Delete.protoEndpoint(annotate, p.Parameters, base, path, "delete")
   589  		out.WriteString(msg + "\n")
   590  	}
   591  
   592  	return strings.TrimSuffix(out.String(), "\n")
   593  }
   594  
   595  // ProtoMessages will return protobuf v3 messages that represents
   596  // the request Parameters of the endpoints within this path declaration
   597  // and any custom response messages not listed in the definitions.
   598  func (p *Path) ProtoMessages(path string, defs map[string]*Items) string {
   599  	var out bytes.Buffer
   600  	if p.Get != nil {
   601  		endpointName := PathMethodToName(path, "get")
   602  		msg := p.Get.protoMessages(p.Parameters, endpointName, defs)
   603  		if msg != "" {
   604  			out.WriteString(msg)
   605  		}
   606  	}
   607  	if p.Put != nil {
   608  		endpointName := PathMethodToName(path, "put")
   609  		msg := p.Put.protoMessages(p.Parameters, endpointName, defs)
   610  		if msg != "" {
   611  			out.WriteString(msg)
   612  		}
   613  	}
   614  	if p.Post != nil {
   615  		endpointName := PathMethodToName(path, "post")
   616  		msg := p.Post.protoMessages(p.Parameters, endpointName, defs)
   617  		if msg != "" {
   618  			out.WriteString(msg)
   619  		}
   620  	}
   621  	if p.Delete != nil {
   622  		endpointName := PathMethodToName(path, "delete")
   623  		msg := p.Delete.protoMessages(p.Parameters, endpointName, defs)
   624  		if msg != "" {
   625  			out.WriteString(msg)
   626  		}
   627  	}
   628  
   629  	return strings.TrimSuffix(out.String(), "\n")
   630  }
   631  
   632  func paramsToProps(parent, child Parameters, defs map[string]*Items) map[string]*Items {
   633  	props := map[string]*Items{}
   634  	// combine all parameters for endpoint
   635  	for _, item := range child {
   636  		props[findRefName(item, defs)] = item
   637  	}
   638  	for _, item := range parent {
   639  		props[findRefName(item, defs)] = item
   640  	}
   641  	return props
   642  }
   643  
   644  func findRefName(i *Items, defs map[string]*Items) string {
   645  	if i.Name != "" {
   646  		return i.Name
   647  	}
   648  
   649  	itemType := strings.TrimPrefix(i.Ref, "#/parameters/")
   650  	item, ok := defs[itemType]
   651  
   652  	if !ok {
   653  		return path.Base(itemType)
   654  	}
   655  
   656  	return item.Name
   657  }
   658  
   659  // ProtoMessage will return a protobuf v3 message that represents
   660  // the request Parameters.
   661  func (p Parameters) ProtoMessage(parent Parameters, endpointName string, defs map[string]*Items) string {
   662  	m := &Model{Properties: paramsToProps(parent, p, defs)}
   663  
   664  	// do nothing, no props and should be a google.protobuf.Empty
   665  	if len(m.Properties) == 0 {
   666  		return ""
   667  	}
   668  
   669  	var b bytes.Buffer
   670  	m.Name = endpointName + "Request"
   671  	m.Depth = 0
   672  
   673  	s := struct {
   674  		*Model
   675  		Defs map[string]*Items
   676  	}{m, defs}
   677  	err := protoMsgTmpl.Execute(&b, s)
   678  	if err != nil {
   679  		log.Fatal("unable to protobuf parameters: ", err)
   680  	}
   681  	return b.String()
   682  }
   683  
   684  // ProtoModel will return a protobuf v3 message that represents
   685  // the current Model.
   686  func (m *Model) ProtoModel(name string, depth int, defs map[string]*Items) string {
   687  	var b bytes.Buffer
   688  	m.Name = name
   689  	m.Depth = depth
   690  	s := struct {
   691  		*Model
   692  		Defs map[string]*Items
   693  	}{m, defs}
   694  	err := protoMsgTmpl.Execute(&b, s)
   695  	if err != nil {
   696  		log.Fatal("unable to protobuf model: ", err)
   697  	}
   698  	return b.String()
   699  }
   700  
   701  func format(fmt interface{}) string {
   702  	format := ""
   703  	if fmt != nil {
   704  		format = fmt.(string)
   705  	}
   706  	return format
   707  
   708  }