github.com/grpc-ecosystem/grpc-gateway/v2@v2.19.1/protoc-gen-openapiv2/internal/genopenapi/template_test.go (about)

     1  package genopenapi
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"math"
     9  	"os"
    10  	"reflect"
    11  	"strings"
    12  	"testing"
    13  
    14  	"github.com/google/go-cmp/cmp"
    15  	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor"
    16  	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig"
    17  	"github.com/grpc-ecosystem/grpc-gateway/v2/internal/httprule"
    18  	openapi_options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
    19  	"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
    20  	"google.golang.org/genproto/googleapis/api/annotations"
    21  	"google.golang.org/genproto/googleapis/api/visibility"
    22  	"google.golang.org/protobuf/encoding/protojson"
    23  	"google.golang.org/protobuf/proto"
    24  	"google.golang.org/protobuf/reflect/protodesc"
    25  	"google.golang.org/protobuf/reflect/protoreflect"
    26  	"google.golang.org/protobuf/reflect/protoregistry"
    27  	"google.golang.org/protobuf/types/descriptorpb"
    28  	"google.golang.org/protobuf/types/known/anypb"
    29  	"google.golang.org/protobuf/types/known/durationpb"
    30  	"google.golang.org/protobuf/types/known/emptypb"
    31  	field_mask "google.golang.org/protobuf/types/known/fieldmaskpb"
    32  	"google.golang.org/protobuf/types/known/structpb"
    33  	"google.golang.org/protobuf/types/known/timestamppb"
    34  	"google.golang.org/protobuf/types/known/wrapperspb"
    35  	"google.golang.org/protobuf/types/pluginpb"
    36  )
    37  
    38  var marshaler = &runtime.JSONPb{}
    39  
    40  func TestOpenapiExamplesFromProtoExamples(t *testing.T) {
    41  	examples := openapiExamplesFromProtoExamples(map[string]string{
    42  		"application/json": `{"Hello": "Worldr!"}`,
    43  		"plain/text":       "Hello, World!",
    44  	})
    45  
    46  	testCases := map[Format]string{
    47  		FormatJSON: `
    48  		{
    49  			"application/json": {
    50  				"Hello": "Worldr!"
    51  			},
    52  			"plain/text": "Hello, World!"
    53  		}
    54  		`,
    55  		FormatYAML: `
    56  		application/json:
    57  		  Hello: Worldr!
    58  		plain/text: Hello, World!
    59  		`,
    60  	}
    61  
    62  	spaceRemover := strings.NewReplacer(" ", "", "\t", "", "\n", "")
    63  
    64  	for format, expected := range testCases {
    65  		t.Run(string(format), func(t *testing.T) {
    66  			var buf bytes.Buffer
    67  
    68  			encoder, err := format.NewEncoder(&buf)
    69  			if err != nil {
    70  				t.Fatalf("creating encoder: %s", err)
    71  			}
    72  
    73  			err = encoder.Encode(examples)
    74  			if err != nil {
    75  				t.Fatalf("encoding: %s", err)
    76  			}
    77  
    78  			actual := spaceRemover.Replace(buf.String())
    79  			expected = spaceRemover.Replace(expected)
    80  
    81  			if expected != actual {
    82  				t.Fatalf("expected:\n%s\nactual:\n%s", expected, actual)
    83  			}
    84  		})
    85  	}
    86  }
    87  
    88  func crossLinkFixture(f *descriptor.File) *descriptor.File {
    89  	for _, m := range f.Messages {
    90  		m.File = f
    91  	}
    92  	for _, svc := range f.Services {
    93  		svc.File = f
    94  		for _, m := range svc.Methods {
    95  			m.Service = svc
    96  			for _, b := range m.Bindings {
    97  				b.Method = m
    98  				for _, param := range b.PathParams {
    99  					param.Method = m
   100  				}
   101  			}
   102  		}
   103  	}
   104  	return f
   105  }
   106  
   107  func reqFromFile(f *descriptor.File) *pluginpb.CodeGeneratorRequest {
   108  	return &pluginpb.CodeGeneratorRequest{
   109  		ProtoFile: []*descriptorpb.FileDescriptorProto{
   110  			f.FileDescriptorProto,
   111  		},
   112  		FileToGenerate: []string{f.GetName()},
   113  	}
   114  }
   115  
   116  func TestMessageToQueryParametersWithEnumAsInt(t *testing.T) {
   117  	type test struct {
   118  		MsgDescs []*descriptorpb.DescriptorProto
   119  		Message  string
   120  		Params   []openapiParameterObject
   121  	}
   122  
   123  	tests := []test{
   124  		{
   125  			MsgDescs: []*descriptorpb.DescriptorProto{
   126  				{
   127  					Name: proto.String("ExampleMessage"),
   128  					Field: []*descriptorpb.FieldDescriptorProto{
   129  						{
   130  							Name:   proto.String("a"),
   131  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   132  							Number: proto.Int32(1),
   133  						},
   134  						{
   135  							Name:   proto.String("b"),
   136  							Type:   descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
   137  							Number: proto.Int32(2),
   138  						},
   139  						{
   140  							Name:   proto.String("c"),
   141  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   142  							Label:  descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
   143  							Number: proto.Int32(3),
   144  						},
   145  					},
   146  				},
   147  			},
   148  			Message: "ExampleMessage",
   149  			Params: []openapiParameterObject{
   150  				{
   151  					Name:     "a",
   152  					In:       "query",
   153  					Required: false,
   154  					Type:     "string",
   155  				},
   156  				{
   157  					Name:     "b",
   158  					In:       "query",
   159  					Required: false,
   160  					Type:     "number",
   161  					Format:   "double",
   162  				},
   163  				{
   164  					Name:             "c",
   165  					In:               "query",
   166  					Required:         false,
   167  					Type:             "array",
   168  					CollectionFormat: "multi",
   169  				},
   170  			},
   171  		},
   172  		{
   173  			MsgDescs: []*descriptorpb.DescriptorProto{
   174  				{
   175  					Name: proto.String("ExampleMessage"),
   176  					Field: []*descriptorpb.FieldDescriptorProto{
   177  						{
   178  							Name:     proto.String("nested"),
   179  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   180  							TypeName: proto.String(".example.Nested"),
   181  							Number:   proto.Int32(1),
   182  						},
   183  					},
   184  				},
   185  				{
   186  					Name: proto.String("Nested"),
   187  					Field: []*descriptorpb.FieldDescriptorProto{
   188  						{
   189  							Name:   proto.String("a"),
   190  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   191  							Number: proto.Int32(1),
   192  						},
   193  						{
   194  							Name:     proto.String("deep"),
   195  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   196  							TypeName: proto.String(".example.Nested.DeepNested"),
   197  							Number:   proto.Int32(2),
   198  						},
   199  					},
   200  					NestedType: []*descriptorpb.DescriptorProto{{
   201  						Name: proto.String("DeepNested"),
   202  						Field: []*descriptorpb.FieldDescriptorProto{
   203  							{
   204  								Name:   proto.String("b"),
   205  								Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   206  								Number: proto.Int32(1),
   207  							},
   208  							{
   209  								Name:     proto.String("c"),
   210  								Type:     descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
   211  								TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
   212  								Number:   proto.Int32(2),
   213  							},
   214  						},
   215  						EnumType: []*descriptorpb.EnumDescriptorProto{
   216  							{
   217  								Name: proto.String("DeepEnum"),
   218  								Value: []*descriptorpb.EnumValueDescriptorProto{
   219  									{Name: proto.String("FALSE"), Number: proto.Int32(0)},
   220  									{Name: proto.String("TRUE"), Number: proto.Int32(1)},
   221  								},
   222  							},
   223  						},
   224  					}},
   225  				},
   226  			},
   227  			Message: "ExampleMessage",
   228  			Params: []openapiParameterObject{
   229  				{
   230  					Name:     "nested.a",
   231  					In:       "query",
   232  					Required: false,
   233  					Type:     "string",
   234  				},
   235  				{
   236  					Name:     "nested.deep.b",
   237  					In:       "query",
   238  					Required: false,
   239  					Type:     "string",
   240  				},
   241  				{
   242  					Name:     "nested.deep.c",
   243  					In:       "query",
   244  					Required: false,
   245  					Type:     "integer",
   246  					Enum:     []int{0, 1},
   247  					Default:  0,
   248  				},
   249  			},
   250  		},
   251  	}
   252  
   253  	for _, test := range tests {
   254  		reg := descriptor.NewRegistry()
   255  		reg.SetEnumsAsInts(true)
   256  		var msgs []*descriptor.Message
   257  		for _, msgdesc := range test.MsgDescs {
   258  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   259  		}
   260  		file := descriptor.File{
   261  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
   262  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
   263  				Name:           proto.String("example.proto"),
   264  				Package:        proto.String("example"),
   265  				Dependency:     []string{},
   266  				MessageType:    test.MsgDescs,
   267  				Service:        []*descriptorpb.ServiceDescriptorProto{},
   268  				Options: &descriptorpb.FileOptions{
   269  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
   270  				},
   271  			},
   272  			GoPkg: descriptor.GoPackage{
   273  				Path: "example.com/path/to/example/example.pb",
   274  				Name: "example_pb",
   275  			},
   276  			Messages: msgs,
   277  		}
   278  		err := reg.Load(&pluginpb.CodeGeneratorRequest{
   279  			ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
   280  		})
   281  		if err != nil {
   282  			t.Fatalf("failed to load code generator request: %v", err)
   283  		}
   284  
   285  		message, err := reg.LookupMsg("", ".example."+test.Message)
   286  		if err != nil {
   287  			t.Fatalf("failed to lookup message: %s", err)
   288  		}
   289  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
   290  		if err != nil {
   291  			t.Fatalf("failed to convert message to query parameters: %s", err)
   292  		}
   293  		// avoid checking Items for array types
   294  		for i := range params {
   295  			params[i].Items = nil
   296  		}
   297  		if !reflect.DeepEqual(params, test.Params) {
   298  			t.Errorf("expected %v, got %v", test.Params, params)
   299  		}
   300  	}
   301  }
   302  
   303  func TestMessageToQueryParametersWithOmitEnumDefaultValue(t *testing.T) {
   304  	type test struct {
   305  		MsgDescs []*descriptorpb.DescriptorProto
   306  		Message  string
   307  		Params   []openapiParameterObject
   308  	}
   309  
   310  	tests := []test{
   311  		{
   312  			MsgDescs: []*descriptorpb.DescriptorProto{
   313  				{
   314  					Name: proto.String("ExampleMessage"),
   315  					Field: []*descriptorpb.FieldDescriptorProto{
   316  						{
   317  							Name:   proto.String("a"),
   318  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   319  							Number: proto.Int32(1),
   320  						},
   321  						{
   322  							Name:   proto.String("b"),
   323  							Type:   descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
   324  							Number: proto.Int32(2),
   325  						},
   326  						{
   327  							Name:   proto.String("c"),
   328  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   329  							Label:  descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
   330  							Number: proto.Int32(3),
   331  						},
   332  					},
   333  				},
   334  			},
   335  			Message: "ExampleMessage",
   336  			Params: []openapiParameterObject{
   337  				{
   338  					Name:     "a",
   339  					In:       "query",
   340  					Required: false,
   341  					Type:     "string",
   342  				},
   343  				{
   344  					Name:     "b",
   345  					In:       "query",
   346  					Required: false,
   347  					Type:     "number",
   348  					Format:   "double",
   349  				},
   350  				{
   351  					Name:             "c",
   352  					In:               "query",
   353  					Required:         false,
   354  					Type:             "array",
   355  					CollectionFormat: "multi",
   356  				},
   357  			},
   358  		},
   359  		{
   360  			MsgDescs: []*descriptorpb.DescriptorProto{
   361  				{
   362  					Name: proto.String("ExampleMessage"),
   363  					Field: []*descriptorpb.FieldDescriptorProto{
   364  						{
   365  							Name:     proto.String("nested"),
   366  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   367  							TypeName: proto.String(".example.Nested"),
   368  							Number:   proto.Int32(1),
   369  						},
   370  					},
   371  				},
   372  				{
   373  					Name: proto.String("Nested"),
   374  					Field: []*descriptorpb.FieldDescriptorProto{
   375  						{
   376  							Name:   proto.String("a"),
   377  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   378  							Number: proto.Int32(1),
   379  						},
   380  						{
   381  							Name:     proto.String("deep"),
   382  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   383  							TypeName: proto.String(".example.Nested.DeepNested"),
   384  							Number:   proto.Int32(2),
   385  						},
   386  					},
   387  					NestedType: []*descriptorpb.DescriptorProto{{
   388  						Name: proto.String("DeepNested"),
   389  						Field: []*descriptorpb.FieldDescriptorProto{
   390  							{
   391  								Name:   proto.String("b"),
   392  								Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   393  								Number: proto.Int32(1),
   394  							},
   395  							{
   396  								Name:     proto.String("c"),
   397  								Type:     descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
   398  								TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
   399  								Number:   proto.Int32(2),
   400  							},
   401  						},
   402  						EnumType: []*descriptorpb.EnumDescriptorProto{
   403  							{
   404  								Name: proto.String("DeepEnum"),
   405  								Value: []*descriptorpb.EnumValueDescriptorProto{
   406  									{Name: proto.String("FALSE"), Number: proto.Int32(0)},
   407  									{Name: proto.String("TRUE"), Number: proto.Int32(1)},
   408  								},
   409  							},
   410  						},
   411  					}},
   412  				},
   413  			},
   414  			Message: "ExampleMessage",
   415  			Params: []openapiParameterObject{
   416  				{
   417  					Name:     "nested.a",
   418  					In:       "query",
   419  					Required: false,
   420  					Type:     "string",
   421  				},
   422  				{
   423  					Name:     "nested.deep.b",
   424  					In:       "query",
   425  					Required: false,
   426  					Type:     "string",
   427  				},
   428  				{
   429  					Name:     "nested.deep.c",
   430  					In:       "query",
   431  					Required: false,
   432  					Type:     "string",
   433  					Enum:     []string{"TRUE"},
   434  				},
   435  			},
   436  		},
   437  	}
   438  
   439  	for _, test := range tests {
   440  		reg := descriptor.NewRegistry()
   441  		reg.SetOmitEnumDefaultValue(true)
   442  		var msgs []*descriptor.Message
   443  		for _, msgdesc := range test.MsgDescs {
   444  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   445  		}
   446  		file := descriptor.File{
   447  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
   448  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
   449  				Name:           proto.String("example.proto"),
   450  				Package:        proto.String("example"),
   451  				Dependency:     []string{},
   452  				MessageType:    test.MsgDescs,
   453  				Service:        []*descriptorpb.ServiceDescriptorProto{},
   454  				Options: &descriptorpb.FileOptions{
   455  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
   456  				},
   457  			},
   458  			GoPkg: descriptor.GoPackage{
   459  				Path: "example.com/path/to/example/example.pb",
   460  				Name: "example_pb",
   461  			},
   462  			Messages: msgs,
   463  		}
   464  		err := reg.Load(&pluginpb.CodeGeneratorRequest{
   465  			ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
   466  		})
   467  		if err != nil {
   468  			t.Fatalf("failed to load code generator request: %v", err)
   469  		}
   470  
   471  		message, err := reg.LookupMsg("", ".example."+test.Message)
   472  		if err != nil {
   473  			t.Fatalf("failed to lookup message: %s", err)
   474  		}
   475  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
   476  		if err != nil {
   477  			t.Fatalf("failed to convert message to query parameters: %s", err)
   478  		}
   479  		// avoid checking Items for array types
   480  		for i := range params {
   481  			params[i].Items = nil
   482  		}
   483  		if !reflect.DeepEqual(params, test.Params) {
   484  			t.Errorf("expected %v, got %v", test.Params, params)
   485  		}
   486  	}
   487  }
   488  
   489  func TestMessageToQueryParameters(t *testing.T) {
   490  	type test struct {
   491  		MsgDescs []*descriptorpb.DescriptorProto
   492  		Message  string
   493  		Params   []openapiParameterObject
   494  	}
   495  
   496  	tests := []test{
   497  		{
   498  			MsgDescs: []*descriptorpb.DescriptorProto{
   499  				{
   500  					Name: proto.String("ExampleMessage"),
   501  					Field: []*descriptorpb.FieldDescriptorProto{
   502  						{
   503  							Name:   proto.String("a"),
   504  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   505  							Number: proto.Int32(1),
   506  						},
   507  						{
   508  							Name:   proto.String("b"),
   509  							Type:   descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
   510  							Number: proto.Int32(2),
   511  						},
   512  						{
   513  							Name:   proto.String("c"),
   514  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   515  							Label:  descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
   516  							Number: proto.Int32(3),
   517  						},
   518  					},
   519  				},
   520  			},
   521  			Message: "ExampleMessage",
   522  			Params: []openapiParameterObject{
   523  				{
   524  					Name:     "a",
   525  					In:       "query",
   526  					Required: false,
   527  					Type:     "string",
   528  				},
   529  				{
   530  					Name:     "b",
   531  					In:       "query",
   532  					Required: false,
   533  					Type:     "number",
   534  					Format:   "double",
   535  				},
   536  				{
   537  					Name:             "c",
   538  					In:               "query",
   539  					Required:         false,
   540  					Type:             "array",
   541  					CollectionFormat: "multi",
   542  				},
   543  			},
   544  		},
   545  		{
   546  			MsgDescs: []*descriptorpb.DescriptorProto{
   547  				{
   548  					Name: proto.String("ExampleMessage"),
   549  					Field: []*descriptorpb.FieldDescriptorProto{
   550  						{
   551  							Name:     proto.String("nested"),
   552  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   553  							TypeName: proto.String(".example.Nested"),
   554  							Number:   proto.Int32(1),
   555  						},
   556  					},
   557  				},
   558  				{
   559  					Name: proto.String("Nested"),
   560  					Field: []*descriptorpb.FieldDescriptorProto{
   561  						{
   562  							Name:   proto.String("a"),
   563  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   564  							Number: proto.Int32(1),
   565  						},
   566  						{
   567  							Name:     proto.String("deep"),
   568  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   569  							TypeName: proto.String(".example.Nested.DeepNested"),
   570  							Number:   proto.Int32(2),
   571  						},
   572  					},
   573  					NestedType: []*descriptorpb.DescriptorProto{{
   574  						Name: proto.String("DeepNested"),
   575  						Field: []*descriptorpb.FieldDescriptorProto{
   576  							{
   577  								Name:   proto.String("b"),
   578  								Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   579  								Number: proto.Int32(1),
   580  							},
   581  							{
   582  								Name:     proto.String("c"),
   583  								Type:     descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
   584  								TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
   585  								Number:   proto.Int32(2),
   586  							},
   587  						},
   588  						EnumType: []*descriptorpb.EnumDescriptorProto{
   589  							{
   590  								Name: proto.String("DeepEnum"),
   591  								Value: []*descriptorpb.EnumValueDescriptorProto{
   592  									{Name: proto.String("FALSE"), Number: proto.Int32(0)},
   593  									{Name: proto.String("TRUE"), Number: proto.Int32(1)},
   594  								},
   595  							},
   596  						},
   597  					}},
   598  				},
   599  			},
   600  			Message: "ExampleMessage",
   601  			Params: []openapiParameterObject{
   602  				{
   603  					Name:     "nested.a",
   604  					In:       "query",
   605  					Required: false,
   606  					Type:     "string",
   607  				},
   608  				{
   609  					Name:     "nested.deep.b",
   610  					In:       "query",
   611  					Required: false,
   612  					Type:     "string",
   613  				},
   614  				{
   615  					Name:     "nested.deep.c",
   616  					In:       "query",
   617  					Required: false,
   618  					Type:     "string",
   619  					Enum:     []string{"FALSE", "TRUE"},
   620  					Default:  "FALSE",
   621  				},
   622  			},
   623  		},
   624  	}
   625  
   626  	for _, test := range tests {
   627  		reg := descriptor.NewRegistry()
   628  		msgs := []*descriptor.Message{}
   629  		for _, msgdesc := range test.MsgDescs {
   630  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   631  		}
   632  		file := descriptor.File{
   633  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
   634  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
   635  				Name:           proto.String("example.proto"),
   636  				Package:        proto.String("example"),
   637  				Dependency:     []string{},
   638  				MessageType:    test.MsgDescs,
   639  				Service:        []*descriptorpb.ServiceDescriptorProto{},
   640  				Options: &descriptorpb.FileOptions{
   641  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
   642  				},
   643  			},
   644  			GoPkg: descriptor.GoPackage{
   645  				Path: "example.com/path/to/example/example.pb",
   646  				Name: "example_pb",
   647  			},
   648  			Messages: msgs,
   649  		}
   650  		err := reg.Load(&pluginpb.CodeGeneratorRequest{
   651  			ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
   652  		})
   653  		if err != nil {
   654  			t.Fatalf("failed to load code generator request: %v", err)
   655  		}
   656  
   657  		message, err := reg.LookupMsg("", ".example."+test.Message)
   658  		if err != nil {
   659  			t.Fatalf("failed to lookup message: %s", err)
   660  		}
   661  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
   662  		if err != nil {
   663  			t.Fatalf("failed to convert message to query parameters: %s", err)
   664  		}
   665  		// avoid checking Items for array types
   666  		for i := range params {
   667  			params[i].Items = nil
   668  		}
   669  		if !reflect.DeepEqual(params, test.Params) {
   670  			t.Errorf("expected %v, got %v", test.Params, params)
   671  		}
   672  	}
   673  }
   674  
   675  // TestMessagetoQueryParametersNoRecursive, is a check that cyclical references between messages
   676  // are not falsely detected given previous known edge-cases.
   677  func TestMessageToQueryParametersNoRecursive(t *testing.T) {
   678  	type test struct {
   679  		MsgDescs []*descriptorpb.DescriptorProto
   680  		Message  string
   681  	}
   682  
   683  	tests := []test{
   684  		// First test:
   685  		// Here is a message that has two of another message adjacent to one another in a nested message.
   686  		// There is no loop but this was previouly falsely flagged as a cycle.
   687  		// Example proto:
   688  		// message NonRecursiveMessage {
   689  		//      string field = 1;
   690  		// }
   691  		// message BaseMessage {
   692  		//      NonRecursiveMessage first = 1;
   693  		//      NonRecursiveMessage second = 2;
   694  		// }
   695  		// message QueryMessage {
   696  		//      BaseMessage first = 1;
   697  		//      string second = 2;
   698  		// }
   699  		{
   700  			MsgDescs: []*descriptorpb.DescriptorProto{
   701  				{
   702  					Name: proto.String("QueryMessage"),
   703  					Field: []*descriptorpb.FieldDescriptorProto{
   704  						{
   705  							Name:     proto.String("first"),
   706  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   707  							TypeName: proto.String(".example.BaseMessage"),
   708  							Number:   proto.Int32(1),
   709  						},
   710  						{
   711  							Name:   proto.String("second"),
   712  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   713  							Number: proto.Int32(2),
   714  						},
   715  					},
   716  				},
   717  				{
   718  					Name: proto.String("BaseMessage"),
   719  					Field: []*descriptorpb.FieldDescriptorProto{
   720  						{
   721  							Name:     proto.String("first"),
   722  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   723  							TypeName: proto.String(".example.NonRecursiveMessage"),
   724  							Number:   proto.Int32(1),
   725  						},
   726  						{
   727  							Name:     proto.String("second"),
   728  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   729  							TypeName: proto.String(".example.NonRecursiveMessage"),
   730  							Number:   proto.Int32(2),
   731  						},
   732  					},
   733  				},
   734  				// Note there is no recursive nature to this message
   735  				{
   736  					Name: proto.String("NonRecursiveMessage"),
   737  					Field: []*descriptorpb.FieldDescriptorProto{
   738  						{
   739  							Name: proto.String("field"),
   740  							// Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   741  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   742  							Number: proto.Int32(1),
   743  						},
   744  					},
   745  				},
   746  			},
   747  			Message: "QueryMessage",
   748  		},
   749  	}
   750  
   751  	for _, test := range tests {
   752  		reg := descriptor.NewRegistry()
   753  		msgs := []*descriptor.Message{}
   754  		for _, msgdesc := range test.MsgDescs {
   755  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   756  		}
   757  		file := descriptor.File{
   758  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
   759  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
   760  				Name:           proto.String("example.proto"),
   761  				Package:        proto.String("example"),
   762  				Dependency:     []string{},
   763  				MessageType:    test.MsgDescs,
   764  				Service:        []*descriptorpb.ServiceDescriptorProto{},
   765  				Options: &descriptorpb.FileOptions{
   766  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
   767  				},
   768  			},
   769  			GoPkg: descriptor.GoPackage{
   770  				Path: "example.com/path/to/example/example.pb",
   771  				Name: "example_pb",
   772  			},
   773  			Messages: msgs,
   774  		}
   775  		err := reg.Load(&pluginpb.CodeGeneratorRequest{
   776  			ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
   777  		})
   778  		if err != nil {
   779  			t.Fatalf("failed to load code generator request: %v", err)
   780  		}
   781  
   782  		message, err := reg.LookupMsg("", ".example."+test.Message)
   783  		if err != nil {
   784  			t.Fatalf("failed to lookup message: %s", err)
   785  		}
   786  
   787  		_, err = messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
   788  		if err != nil {
   789  			t.Fatalf("No recursion error should be thrown: %s", err)
   790  		}
   791  	}
   792  }
   793  
   794  // TestMessagetoQueryParametersRecursive, is a check that cyclical references between messages
   795  // are handled gracefully. The goal is to insure that attempts to add messages with cyclical
   796  // references to query-parameters returns an error message.
   797  func TestMessageToQueryParametersRecursive(t *testing.T) {
   798  	type test struct {
   799  		MsgDescs []*descriptorpb.DescriptorProto
   800  		Message  string
   801  	}
   802  
   803  	tests := []test{
   804  		// First test:
   805  		// Here we test that a message that references it self through a field will return an error.
   806  		// Example proto:
   807  		// message DirectRecursiveMessage {
   808  		//      DirectRecursiveMessage nested = 1;
   809  		// }
   810  		{
   811  			MsgDescs: []*descriptorpb.DescriptorProto{
   812  				{
   813  					Name: proto.String("DirectRecursiveMessage"),
   814  					Field: []*descriptorpb.FieldDescriptorProto{
   815  						{
   816  							Name:     proto.String("nested"),
   817  							Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   818  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   819  							TypeName: proto.String(".example.DirectRecursiveMessage"),
   820  							Number:   proto.Int32(1),
   821  						},
   822  					},
   823  				},
   824  			},
   825  			Message: "DirectRecursiveMessage",
   826  		},
   827  		// Second test:
   828  		// Here we test that a cycle through multiple messages is detected and that an error is returned.
   829  		// Sample:
   830  		// message Root { NodeMessage nested = 1; }
   831  		// message NodeMessage { CycleMessage nested = 1; }
   832  		// message CycleMessage { Root nested = 1; }
   833  		{
   834  			MsgDescs: []*descriptorpb.DescriptorProto{
   835  				{
   836  					Name: proto.String("RootMessage"),
   837  					Field: []*descriptorpb.FieldDescriptorProto{
   838  						{
   839  							Name:     proto.String("nested"),
   840  							Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   841  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   842  							TypeName: proto.String(".example.NodeMessage"),
   843  							Number:   proto.Int32(1),
   844  						},
   845  					},
   846  				},
   847  				{
   848  					Name: proto.String("NodeMessage"),
   849  					Field: []*descriptorpb.FieldDescriptorProto{
   850  						{
   851  							Name:     proto.String("nested"),
   852  							Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   853  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   854  							TypeName: proto.String(".example.CycleMessage"),
   855  							Number:   proto.Int32(1),
   856  						},
   857  					},
   858  				},
   859  				{
   860  					Name: proto.String("CycleMessage"),
   861  					Field: []*descriptorpb.FieldDescriptorProto{
   862  						{
   863  							Name:     proto.String("nested"),
   864  							Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
   865  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   866  							TypeName: proto.String(".example.RootMessage"),
   867  							Number:   proto.Int32(1),
   868  						},
   869  					},
   870  				},
   871  			},
   872  			Message: "RootMessage",
   873  		},
   874  	}
   875  
   876  	for _, test := range tests {
   877  		reg := descriptor.NewRegistry()
   878  		msgs := []*descriptor.Message{}
   879  		for _, msgdesc := range test.MsgDescs {
   880  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
   881  		}
   882  		file := descriptor.File{
   883  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
   884  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
   885  				Name:           proto.String("example.proto"),
   886  				Package:        proto.String("example"),
   887  				Dependency:     []string{},
   888  				MessageType:    test.MsgDescs,
   889  				Service:        []*descriptorpb.ServiceDescriptorProto{},
   890  				Options: &descriptorpb.FileOptions{
   891  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
   892  				},
   893  			},
   894  			GoPkg: descriptor.GoPackage{
   895  				Path: "example.com/path/to/example/example.pb",
   896  				Name: "example_pb",
   897  			},
   898  			Messages: msgs,
   899  		}
   900  		err := reg.Load(&pluginpb.CodeGeneratorRequest{
   901  			ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
   902  		})
   903  		if err != nil {
   904  			t.Fatalf("failed to load code generator request: %v", err)
   905  		}
   906  
   907  		message, err := reg.LookupMsg("", ".example."+test.Message)
   908  		if err != nil {
   909  			t.Fatalf("failed to lookup message: %s", err)
   910  		}
   911  		_, err = messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
   912  		if err == nil {
   913  			t.Fatalf("It should not be allowed to have recursive query parameters")
   914  		}
   915  	}
   916  }
   917  
   918  func TestMessageToQueryParametersWithJsonName(t *testing.T) {
   919  	type test struct {
   920  		MsgDescs []*descriptorpb.DescriptorProto
   921  		Message  string
   922  		Params   []openapiParameterObject
   923  	}
   924  
   925  	var requiredField = []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
   926  	var requiredFieldOptions = new(descriptorpb.FieldOptions)
   927  	proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, requiredField)
   928  
   929  	messageSchema := &openapi_options.Schema{
   930  		JsonSchema: &openapi_options.JSONSchema{
   931  			Required: []string{"test_field_b"},
   932  		},
   933  	}
   934  	messageOption := &descriptorpb.MessageOptions{}
   935  	proto.SetExtension(messageOption, openapi_options.E_Openapiv2Schema, messageSchema)
   936  
   937  	tests := []test{
   938  		{
   939  			MsgDescs: []*descriptorpb.DescriptorProto{
   940  				{
   941  					Name: proto.String("ExampleMessage"),
   942  					Field: []*descriptorpb.FieldDescriptorProto{
   943  						{
   944  							Name:     proto.String("test_field_a"),
   945  							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   946  							Number:   proto.Int32(1),
   947  							JsonName: proto.String("testFieldA"),
   948  						},
   949  					},
   950  				},
   951  			},
   952  			Message: "ExampleMessage",
   953  			Params: []openapiParameterObject{
   954  				{
   955  					Name:     "testFieldA",
   956  					In:       "query",
   957  					Required: false,
   958  					Type:     "string",
   959  				},
   960  			},
   961  		},
   962  		{
   963  			MsgDescs: []*descriptorpb.DescriptorProto{
   964  				{
   965  					Name: proto.String("SubMessage"),
   966  					Field: []*descriptorpb.FieldDescriptorProto{
   967  						{
   968  							Name:     proto.String("test_field_a"),
   969  							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
   970  							Number:   proto.Int32(1),
   971  							JsonName: proto.String("testFieldA"),
   972  						},
   973  					},
   974  				},
   975  				{
   976  					Name: proto.String("ExampleMessage"),
   977  					Field: []*descriptorpb.FieldDescriptorProto{
   978  						{
   979  							Name:     proto.String("sub_message"),
   980  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
   981  							TypeName: proto.String(".example.SubMessage"),
   982  							Number:   proto.Int32(1),
   983  							JsonName: proto.String("subMessage"),
   984  						},
   985  					},
   986  				},
   987  			},
   988  			Message: "ExampleMessage",
   989  			Params: []openapiParameterObject{
   990  				{
   991  					Name:     "subMessage.testFieldA",
   992  					In:       "query",
   993  					Required: false,
   994  					Type:     "string",
   995  				},
   996  			},
   997  		},
   998  		{
   999  			MsgDescs: []*descriptorpb.DescriptorProto{
  1000  				{
  1001  					Name: proto.String("ExampleMessage"),
  1002  					Field: []*descriptorpb.FieldDescriptorProto{
  1003  						{
  1004  							Name:     proto.String("test_field_a"),
  1005  							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  1006  							Number:   proto.Int32(1),
  1007  							JsonName: proto.String("testFieldACustom"),
  1008  							Options:  requiredFieldOptions,
  1009  						},
  1010  						{
  1011  							Name:     proto.String("test_field_b"),
  1012  							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  1013  							Number:   proto.Int32(2),
  1014  							JsonName: proto.String("testFieldBCustom"),
  1015  						},
  1016  					},
  1017  					Options: messageOption,
  1018  				},
  1019  			},
  1020  			Message: "ExampleMessage",
  1021  			Params: []openapiParameterObject{
  1022  				{
  1023  					Name:     "testFieldACustom",
  1024  					In:       "query",
  1025  					Required: true,
  1026  					Type:     "string",
  1027  				},
  1028  				{
  1029  					Name:     "testFieldBCustom",
  1030  					In:       "query",
  1031  					Required: true,
  1032  					Type:     "string",
  1033  				},
  1034  			},
  1035  		},
  1036  	}
  1037  
  1038  	for _, test := range tests {
  1039  		reg := descriptor.NewRegistry()
  1040  		reg.SetUseJSONNamesForFields(true)
  1041  		msgs := []*descriptor.Message{}
  1042  		for _, msgdesc := range test.MsgDescs {
  1043  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  1044  		}
  1045  		file := descriptor.File{
  1046  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1047  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1048  				Name:           proto.String("example.proto"),
  1049  				Package:        proto.String("example"),
  1050  				Dependency:     []string{},
  1051  				MessageType:    test.MsgDescs,
  1052  				Service:        []*descriptorpb.ServiceDescriptorProto{},
  1053  				Options: &descriptorpb.FileOptions{
  1054  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1055  				},
  1056  			},
  1057  			GoPkg: descriptor.GoPackage{
  1058  				Path: "example.com/path/to/example/example.pb",
  1059  				Name: "example_pb",
  1060  			},
  1061  			Messages: msgs,
  1062  		}
  1063  		err := reg.Load(&pluginpb.CodeGeneratorRequest{
  1064  			ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  1065  		})
  1066  		if err != nil {
  1067  			t.Fatalf("failed to load code generator request: %v", err)
  1068  		}
  1069  
  1070  		message, err := reg.LookupMsg("", ".example."+test.Message)
  1071  		if err != nil {
  1072  			t.Fatalf("failed to lookup message: %s", err)
  1073  		}
  1074  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
  1075  		if err != nil {
  1076  			t.Fatalf("failed to convert message to query parameters: %s", err)
  1077  		}
  1078  		if !reflect.DeepEqual(params, test.Params) {
  1079  			t.Errorf("expected %#v, got %#v", test.Params, params)
  1080  		}
  1081  	}
  1082  }
  1083  
  1084  func TestMessageToQueryParametersWellKnownTypes(t *testing.T) {
  1085  	type test struct {
  1086  		MsgDescs          []*descriptorpb.DescriptorProto
  1087  		WellKnownMsgDescs []*descriptorpb.DescriptorProto
  1088  		Message           string
  1089  		Params            []openapiParameterObject
  1090  	}
  1091  
  1092  	tests := []test{
  1093  		{
  1094  			MsgDescs: []*descriptorpb.DescriptorProto{
  1095  				{
  1096  					Name: proto.String("ExampleMessage"),
  1097  					Field: []*descriptorpb.FieldDescriptorProto{
  1098  						{
  1099  							Name:     proto.String("a_field_mask"),
  1100  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  1101  							TypeName: proto.String(".google.protobuf.FieldMask"),
  1102  							Number:   proto.Int32(1),
  1103  						},
  1104  						{
  1105  							Name:     proto.String("a_timestamp"),
  1106  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  1107  							TypeName: proto.String(".google.protobuf.Timestamp"),
  1108  							Number:   proto.Int32(2),
  1109  						},
  1110  					},
  1111  				},
  1112  			},
  1113  			WellKnownMsgDescs: []*descriptorpb.DescriptorProto{
  1114  				{
  1115  					Name: proto.String("FieldMask"),
  1116  					Field: []*descriptorpb.FieldDescriptorProto{
  1117  						{
  1118  							Name:   proto.String("paths"),
  1119  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  1120  							Label:  descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  1121  							Number: proto.Int32(1),
  1122  						},
  1123  					},
  1124  				},
  1125  				{
  1126  					Name: proto.String("Timestamp"),
  1127  					Field: []*descriptorpb.FieldDescriptorProto{
  1128  						{
  1129  							Name:   proto.String("seconds"),
  1130  							Type:   descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(),
  1131  							Number: proto.Int32(1),
  1132  						},
  1133  						{
  1134  							Name:   proto.String("nanos"),
  1135  							Type:   descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  1136  							Number: proto.Int32(2),
  1137  						},
  1138  					},
  1139  				},
  1140  			},
  1141  			Message: "ExampleMessage",
  1142  			Params: []openapiParameterObject{
  1143  				{
  1144  					Name:     "a_field_mask",
  1145  					In:       "query",
  1146  					Required: false,
  1147  					Type:     "string",
  1148  				},
  1149  				{
  1150  					Name:     "a_timestamp",
  1151  					In:       "query",
  1152  					Required: false,
  1153  					Type:     "string",
  1154  					Format:   "date-time",
  1155  				},
  1156  			},
  1157  		},
  1158  	}
  1159  
  1160  	for _, test := range tests {
  1161  		reg := descriptor.NewRegistry()
  1162  		reg.SetEnumsAsInts(true)
  1163  		err := reg.Load(&pluginpb.CodeGeneratorRequest{
  1164  			ProtoFile: []*descriptorpb.FileDescriptorProto{
  1165  				{
  1166  					SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1167  					Name:           proto.String("google/well_known.proto"),
  1168  					Package:        proto.String("google.protobuf"),
  1169  					Dependency:     []string{},
  1170  					MessageType:    test.WellKnownMsgDescs,
  1171  					Service:        []*descriptorpb.ServiceDescriptorProto{},
  1172  					Options: &descriptorpb.FileOptions{
  1173  						GoPackage: proto.String("google/well_known"),
  1174  					},
  1175  				},
  1176  				{
  1177  					SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1178  					Name:           proto.String("acme/example.proto"),
  1179  					Package:        proto.String("example"),
  1180  					Dependency:     []string{"google/well_known.proto"},
  1181  					MessageType:    test.MsgDescs,
  1182  					Service:        []*descriptorpb.ServiceDescriptorProto{},
  1183  					Options: &descriptorpb.FileOptions{
  1184  						GoPackage: proto.String("acme/example"),
  1185  					},
  1186  				},
  1187  			},
  1188  		})
  1189  		if err != nil {
  1190  			t.Fatalf("failed to load CodeGeneratorRequest: %v", err)
  1191  		}
  1192  
  1193  		message, err := reg.LookupMsg("", ".example."+test.Message)
  1194  		if err != nil {
  1195  			t.Fatalf("failed to lookup message: %s", err)
  1196  		}
  1197  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
  1198  		if err != nil {
  1199  			t.Fatalf("failed to convert message to query parameters: %s", err)
  1200  		}
  1201  		if !reflect.DeepEqual(params, test.Params) {
  1202  			t.Errorf("expected %v, got %v", test.Params, params)
  1203  		}
  1204  	}
  1205  }
  1206  
  1207  func TestMessageToQueryParametersWithRequiredField(t *testing.T) {
  1208  	type test struct {
  1209  		MsgDescs []*descriptorpb.DescriptorProto
  1210  		Message  string
  1211  		Params   []openapiParameterObject
  1212  	}
  1213  
  1214  	messageSchema := &openapi_options.Schema{
  1215  		JsonSchema: &openapi_options.JSONSchema{
  1216  			Required: []string{"a"},
  1217  		},
  1218  	}
  1219  	messageOption := &descriptorpb.MessageOptions{}
  1220  	proto.SetExtension(messageOption, openapi_options.E_Openapiv2Schema, messageSchema)
  1221  
  1222  	fieldSchema := &openapi_options.JSONSchema{Required: []string{"b"}}
  1223  	fieldOption := &descriptorpb.FieldOptions{}
  1224  	proto.SetExtension(fieldOption, openapi_options.E_Openapiv2Field, fieldSchema)
  1225  
  1226  	// TODO(makdon): is nested field's test case necessary here?
  1227  	tests := []test{
  1228  		{
  1229  			MsgDescs: []*descriptorpb.DescriptorProto{
  1230  				{
  1231  					Name: proto.String("ExampleMessage"),
  1232  					Field: []*descriptorpb.FieldDescriptorProto{
  1233  						{
  1234  							Name:   proto.String("a"),
  1235  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  1236  							Number: proto.Int32(1),
  1237  						},
  1238  						{
  1239  							Name:    proto.String("b"),
  1240  							Type:    descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
  1241  							Number:  proto.Int32(2),
  1242  							Options: fieldOption,
  1243  						},
  1244  						{
  1245  							Name:   proto.String("c"),
  1246  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  1247  							Label:  descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  1248  							Number: proto.Int32(3),
  1249  						},
  1250  					},
  1251  					Options: messageOption,
  1252  				},
  1253  			},
  1254  			Message: "ExampleMessage",
  1255  			Params: []openapiParameterObject{
  1256  				{
  1257  					Name:     "a",
  1258  					In:       "query",
  1259  					Required: true,
  1260  					Type:     "string",
  1261  				},
  1262  				{
  1263  					Name:     "b",
  1264  					In:       "query",
  1265  					Required: true,
  1266  					Type:     "number",
  1267  					Format:   "double",
  1268  				},
  1269  				{
  1270  					Name:             "c",
  1271  					In:               "query",
  1272  					Required:         false,
  1273  					Type:             "array",
  1274  					CollectionFormat: "multi",
  1275  				},
  1276  			},
  1277  		},
  1278  	}
  1279  
  1280  	for _, test := range tests {
  1281  		reg := descriptor.NewRegistry()
  1282  		msgs := []*descriptor.Message{}
  1283  		for _, msgdesc := range test.MsgDescs {
  1284  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  1285  		}
  1286  		file := descriptor.File{
  1287  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1288  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1289  				Name:           proto.String("example.proto"),
  1290  				Package:        proto.String("example"),
  1291  				Dependency:     []string{},
  1292  				MessageType:    test.MsgDescs,
  1293  				Service:        []*descriptorpb.ServiceDescriptorProto{},
  1294  				Options: &descriptorpb.FileOptions{
  1295  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1296  				},
  1297  			},
  1298  			GoPkg: descriptor.GoPackage{
  1299  				Path: "example.com/path/to/example/example.pb",
  1300  				Name: "example_pb",
  1301  			},
  1302  			Messages: msgs,
  1303  		}
  1304  		err := reg.Load(&pluginpb.CodeGeneratorRequest{
  1305  			ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  1306  		})
  1307  		if err != nil {
  1308  			t.Fatalf("failed to load code generator request: %v", err)
  1309  		}
  1310  
  1311  		message, err := reg.LookupMsg("", ".example."+test.Message)
  1312  		if err != nil {
  1313  			t.Fatalf("failed to lookup message: %s", err)
  1314  		}
  1315  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
  1316  		if err != nil {
  1317  			t.Fatalf("failed to convert message to query parameters: %s", err)
  1318  		}
  1319  		// avoid checking Items for array types
  1320  		for i := range params {
  1321  			params[i].Items = nil
  1322  		}
  1323  		if !reflect.DeepEqual(params, test.Params) {
  1324  			t.Errorf("expected %v, got %v", test.Params, params)
  1325  		}
  1326  	}
  1327  }
  1328  
  1329  func TestMessageToQueryParametersWithEnumFieldOption(t *testing.T) {
  1330  	type test struct {
  1331  		MsgDescs []*descriptorpb.DescriptorProto
  1332  		Message  string
  1333  		Params   []openapiParameterObject
  1334  	}
  1335  
  1336  	fieldSchema := &openapi_options.JSONSchema{Enum: []string{"enum1", "enum2"}}
  1337  	fieldOption := &descriptorpb.FieldOptions{}
  1338  	proto.SetExtension(fieldOption, openapi_options.E_Openapiv2Field, fieldSchema)
  1339  
  1340  	tests := []test{
  1341  		{
  1342  			MsgDescs: []*descriptorpb.DescriptorProto{
  1343  				{
  1344  					Name: proto.String("ExampleMessage"),
  1345  					Field: []*descriptorpb.FieldDescriptorProto{
  1346  						{
  1347  							Name:    proto.String("a"),
  1348  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  1349  							Number:  proto.Int32(1),
  1350  							Options: fieldOption,
  1351  						},
  1352  						{
  1353  							Name:   proto.String("b"),
  1354  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  1355  							Number: proto.Int32(2),
  1356  						},
  1357  						{
  1358  							Name:     proto.String("c"),
  1359  							Type:     descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
  1360  							TypeName: proto.String(".example.ExampleMessage.EnabledEnum"),
  1361  							Number:   proto.Int32(3),
  1362  						},
  1363  						{
  1364  							Name:     proto.String("d"),
  1365  							Type:     descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
  1366  							TypeName: proto.String(".example.ExampleMessage.EnabledEnum"),
  1367  							Number:   proto.Int32(4),
  1368  							Options:  fieldOption,
  1369  						},
  1370  					},
  1371  					EnumType: []*descriptorpb.EnumDescriptorProto{
  1372  						{
  1373  							Name: proto.String("EnabledEnum"),
  1374  							Value: []*descriptorpb.EnumValueDescriptorProto{
  1375  								{Name: proto.String("FALSE"), Number: proto.Int32(0)},
  1376  								{Name: proto.String("TRUE"), Number: proto.Int32(1)},
  1377  							},
  1378  						},
  1379  					},
  1380  				},
  1381  			},
  1382  			Message: "ExampleMessage",
  1383  			Params: []openapiParameterObject{
  1384  				{
  1385  					Name: "a",
  1386  					In:   "query",
  1387  					Type: "string",
  1388  					Enum: []string{"enum1", "enum2"},
  1389  				},
  1390  				{
  1391  					Name: "b",
  1392  					In:   "query",
  1393  					Type: "string",
  1394  				},
  1395  				{
  1396  					Name:    "c",
  1397  					In:      "query",
  1398  					Type:    "string",
  1399  					Enum:    []string{"FALSE", "TRUE"},
  1400  					Default: "FALSE",
  1401  				},
  1402  				{
  1403  					Name:    "d",
  1404  					In:      "query",
  1405  					Type:    "string",
  1406  					Enum:    []string{"FALSE", "TRUE"},
  1407  					Default: "FALSE",
  1408  				},
  1409  			},
  1410  		},
  1411  	}
  1412  
  1413  	for _, test := range tests {
  1414  		reg := descriptor.NewRegistry()
  1415  		msgs := []*descriptor.Message{}
  1416  		for _, msgdesc := range test.MsgDescs {
  1417  			msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  1418  		}
  1419  		file := descriptor.File{
  1420  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1421  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1422  				Name:           proto.String("example.proto"),
  1423  				Package:        proto.String("example"),
  1424  				Dependency:     []string{},
  1425  				MessageType:    test.MsgDescs,
  1426  				Service:        []*descriptorpb.ServiceDescriptorProto{},
  1427  				Options: &descriptorpb.FileOptions{
  1428  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1429  				},
  1430  			},
  1431  			GoPkg: descriptor.GoPackage{
  1432  				Path: "example.com/path/to/example/example.pb",
  1433  				Name: "example_pb",
  1434  			},
  1435  			Messages: msgs,
  1436  		}
  1437  		err := reg.Load(&pluginpb.CodeGeneratorRequest{
  1438  			ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  1439  		})
  1440  		if err != nil {
  1441  			t.Fatalf("failed to load code generator request: %v", err)
  1442  		}
  1443  
  1444  		message, err := reg.LookupMsg("", ".example."+test.Message)
  1445  		if err != nil {
  1446  			t.Fatalf("failed to lookup message: %s", err)
  1447  		}
  1448  		params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil, "")
  1449  		if err != nil {
  1450  			t.Fatalf("failed to convert message to query parameters: %s", err)
  1451  		}
  1452  		// avoid checking Items for array types
  1453  		for i := range params {
  1454  			params[i].Items = nil
  1455  		}
  1456  		if !reflect.DeepEqual(params, test.Params) {
  1457  			t.Errorf("expected %v, got %v", test.Params, params)
  1458  		}
  1459  	}
  1460  }
  1461  
  1462  func TestApplyTemplateSimple(t *testing.T) {
  1463  	msgdesc := &descriptorpb.DescriptorProto{
  1464  		Name: proto.String("ExampleMessage"),
  1465  	}
  1466  	meth := &descriptorpb.MethodDescriptorProto{
  1467  		Name:       proto.String("Example"),
  1468  		InputType:  proto.String("ExampleMessage"),
  1469  		OutputType: proto.String("ExampleMessage"),
  1470  	}
  1471  	svc := &descriptorpb.ServiceDescriptorProto{
  1472  		Name:   proto.String("ExampleService"),
  1473  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  1474  	}
  1475  	msg := &descriptor.Message{
  1476  		DescriptorProto: msgdesc,
  1477  	}
  1478  	file := descriptor.File{
  1479  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1480  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1481  			Name:           proto.String("example.proto"),
  1482  			Package:        proto.String("example"),
  1483  			MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
  1484  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  1485  			Options: &descriptorpb.FileOptions{
  1486  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1487  			},
  1488  		},
  1489  		GoPkg: descriptor.GoPackage{
  1490  			Path: "example.com/path/to/example/example.pb",
  1491  			Name: "example_pb",
  1492  		},
  1493  		Messages: []*descriptor.Message{msg},
  1494  		Services: []*descriptor.Service{
  1495  			{
  1496  				ServiceDescriptorProto: svc,
  1497  				Methods: []*descriptor.Method{
  1498  					{
  1499  						MethodDescriptorProto: meth,
  1500  						RequestType:           msg,
  1501  						ResponseType:          msg,
  1502  						Bindings: []*descriptor.Binding{
  1503  							{
  1504  								HTTPMethod: "GET",
  1505  								Body:       &descriptor.Body{FieldPath: nil},
  1506  								PathTmpl: httprule.Template{
  1507  									Version:  1,
  1508  									OpCodes:  []int{0, 0},
  1509  									Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  1510  								},
  1511  							},
  1512  						},
  1513  					},
  1514  				},
  1515  			},
  1516  		},
  1517  	}
  1518  	reg := descriptor.NewRegistry()
  1519  	if err := AddErrorDefs(reg); err != nil {
  1520  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  1521  		return
  1522  	}
  1523  	fileCL := crossLinkFixture(&file)
  1524  	err := reg.Load(reqFromFile(fileCL))
  1525  	if err != nil {
  1526  		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  1527  		return
  1528  	}
  1529  	result, err := applyTemplate(param{File: fileCL, reg: reg})
  1530  	if err != nil {
  1531  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1532  		return
  1533  	}
  1534  	if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
  1535  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1536  	}
  1537  	if want, is, name := "", result.BasePath, "BasePath"; !reflect.DeepEqual(is, want) {
  1538  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1539  	}
  1540  	if want, is, name := ([]string)(nil), result.Schemes, "Schemes"; !reflect.DeepEqual(is, want) {
  1541  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1542  	}
  1543  	if want, is, name := []string{"application/json"}, result.Consumes, "Consumes"; !reflect.DeepEqual(is, want) {
  1544  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1545  	}
  1546  	if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
  1547  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1548  	}
  1549  
  1550  	// If there was a failure, print out the input and the json result for debugging.
  1551  	if t.Failed() {
  1552  		t.Errorf("had: %s", file)
  1553  		t.Errorf("got: %s", fmt.Sprint(result))
  1554  	}
  1555  }
  1556  
  1557  func TestApplyTemplateMultiService(t *testing.T) {
  1558  	msgdesc := &descriptorpb.DescriptorProto{
  1559  		Name: proto.String("ExampleMessage"),
  1560  	}
  1561  	meth := &descriptorpb.MethodDescriptorProto{
  1562  		Name:       proto.String("Example"),
  1563  		InputType:  proto.String("ExampleMessage"),
  1564  		OutputType: proto.String("ExampleMessage"),
  1565  	}
  1566  
  1567  	// Create two services that have the same method name. We will test that the
  1568  	// operation IDs are different
  1569  	svc := &descriptorpb.ServiceDescriptorProto{
  1570  		Name:   proto.String("ExampleService"),
  1571  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  1572  	}
  1573  	svc2 := &descriptorpb.ServiceDescriptorProto{
  1574  		Name:   proto.String("OtherService"),
  1575  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  1576  	}
  1577  
  1578  	msg := &descriptor.Message{
  1579  		DescriptorProto: msgdesc,
  1580  	}
  1581  	file := descriptor.File{
  1582  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1583  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1584  			Name:           proto.String("example.proto"),
  1585  			Package:        proto.String("example"),
  1586  			MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
  1587  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  1588  			Options: &descriptorpb.FileOptions{
  1589  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1590  			},
  1591  		},
  1592  		GoPkg: descriptor.GoPackage{
  1593  			Path: "example.com/path/to/example/example.pb",
  1594  			Name: "example_pb",
  1595  		},
  1596  		Messages: []*descriptor.Message{msg},
  1597  		Services: []*descriptor.Service{
  1598  			{
  1599  				ServiceDescriptorProto: svc,
  1600  				Methods: []*descriptor.Method{
  1601  					{
  1602  						MethodDescriptorProto: meth,
  1603  						RequestType:           msg,
  1604  						ResponseType:          msg,
  1605  						Bindings: []*descriptor.Binding{
  1606  							{
  1607  								HTTPMethod: "GET",
  1608  								Body:       &descriptor.Body{FieldPath: nil},
  1609  								PathTmpl: httprule.Template{
  1610  									Version:  1,
  1611  									OpCodes:  []int{0, 0},
  1612  									Template: "/v1/echo",
  1613  								},
  1614  							},
  1615  						},
  1616  					},
  1617  				},
  1618  			},
  1619  			{
  1620  				ServiceDescriptorProto: svc2,
  1621  				Methods: []*descriptor.Method{
  1622  					{
  1623  						MethodDescriptorProto: meth,
  1624  						RequestType:           msg,
  1625  						ResponseType:          msg,
  1626  						Bindings: []*descriptor.Binding{
  1627  							{
  1628  								HTTPMethod: "GET",
  1629  								Body:       &descriptor.Body{FieldPath: nil},
  1630  								PathTmpl: httprule.Template{
  1631  									Version:  1,
  1632  									OpCodes:  []int{0, 0},
  1633  									Template: "/v1/ping",
  1634  								},
  1635  							},
  1636  						},
  1637  					},
  1638  				},
  1639  			},
  1640  		},
  1641  	}
  1642  	reg := descriptor.NewRegistry()
  1643  	if err := AddErrorDefs(reg); err != nil {
  1644  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  1645  		return
  1646  	}
  1647  	fileCL := crossLinkFixture(&file)
  1648  	err := reg.Load(reqFromFile(fileCL))
  1649  	if err != nil {
  1650  		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  1651  		return
  1652  	}
  1653  	result, err := applyTemplate(param{File: fileCL, reg: reg})
  1654  	if err != nil {
  1655  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1656  		return
  1657  	}
  1658  
  1659  	// Check that the two services have unique operation IDs even though they
  1660  	// have the same method name.
  1661  	if want, is := "ExampleService_Example", result.getPathItemObject("/v1/echo").Get.OperationID; !reflect.DeepEqual(is, want) {
  1662  		t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
  1663  	}
  1664  	if want, is := "OtherService_Example", result.getPathItemObject("/v1/ping").Get.OperationID; !reflect.DeepEqual(is, want) {
  1665  		t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
  1666  	}
  1667  
  1668  	// If there was a failure, print out the input and the json result for debugging.
  1669  	if t.Failed() {
  1670  		t.Errorf("had: %s", file)
  1671  		t.Errorf("got: %s", fmt.Sprint(result))
  1672  	}
  1673  }
  1674  
  1675  func TestApplyTemplateOpenAPIConfigFromYAML(t *testing.T) {
  1676  	msgdesc := &descriptorpb.DescriptorProto{
  1677  		Name: proto.String("ExampleMessage"),
  1678  	}
  1679  	meth := &descriptorpb.MethodDescriptorProto{
  1680  		Name:       proto.String("Example"),
  1681  		InputType:  proto.String("ExampleMessage"),
  1682  		OutputType: proto.String("ExampleMessage"),
  1683  	}
  1684  	svc := &descriptorpb.ServiceDescriptorProto{
  1685  		Name:   proto.String("ExampleService"),
  1686  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  1687  	}
  1688  	msg := &descriptor.Message{
  1689  		DescriptorProto: msgdesc,
  1690  	}
  1691  	file := descriptor.File{
  1692  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1693  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1694  			Name:           proto.String("example.proto"),
  1695  			Package:        proto.String("example"),
  1696  			MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
  1697  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  1698  			Options: &descriptorpb.FileOptions{
  1699  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1700  			},
  1701  		},
  1702  		GoPkg: descriptor.GoPackage{
  1703  			Path: "example.com/path/to/example/example.pb",
  1704  			Name: "example_pb",
  1705  		},
  1706  		Messages: []*descriptor.Message{msg},
  1707  		Services: []*descriptor.Service{
  1708  			{
  1709  				ServiceDescriptorProto: svc,
  1710  				Methods: []*descriptor.Method{
  1711  					{
  1712  						MethodDescriptorProto: meth,
  1713  						RequestType:           msg,
  1714  						ResponseType:          msg,
  1715  						Bindings: []*descriptor.Binding{
  1716  							{
  1717  								HTTPMethod: "GET",
  1718  								Body:       &descriptor.Body{FieldPath: nil},
  1719  								PathTmpl: httprule.Template{
  1720  									Version:  1,
  1721  									OpCodes:  []int{0, 0},
  1722  									Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  1723  								},
  1724  							},
  1725  						},
  1726  					},
  1727  				},
  1728  			},
  1729  		},
  1730  	}
  1731  	reg := descriptor.NewRegistry()
  1732  	if err := AddErrorDefs(reg); err != nil {
  1733  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  1734  		return
  1735  	}
  1736  	fileCL := crossLinkFixture(&file)
  1737  	err := reg.Load(reqFromFile(fileCL))
  1738  	if err != nil {
  1739  		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  1740  		return
  1741  	}
  1742  	openapiOptions := &openapiconfig.OpenAPIOptions{
  1743  		Service: []*openapiconfig.OpenAPIServiceOption{
  1744  			{
  1745  				Service: "example.ExampleService",
  1746  				Option: &openapi_options.Tag{
  1747  					Description: "ExampleService description",
  1748  					ExternalDocs: &openapi_options.ExternalDocumentation{
  1749  						Description: "Find out more about ExampleService",
  1750  					},
  1751  				},
  1752  			},
  1753  		},
  1754  	}
  1755  	if err := reg.RegisterOpenAPIOptions(openapiOptions); err != nil {
  1756  		t.Errorf("reg.RegisterOpenAPIOptions for Service %#v failed with %v; want success", openapiOptions.Service, err)
  1757  		return
  1758  	}
  1759  
  1760  	result, err := applyTemplate(param{File: fileCL, reg: reg})
  1761  	if err != nil {
  1762  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1763  		return
  1764  	}
  1765  	if want, is, name := "ExampleService description", result.Tags[0].Description, "Tags[0].Description"; !reflect.DeepEqual(is, want) {
  1766  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1767  	}
  1768  	if want, is, name := "Find out more about ExampleService", result.Tags[0].ExternalDocs.Description, "Tags[0].ExternalDocs.Description"; !reflect.DeepEqual(is, want) {
  1769  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1770  	}
  1771  
  1772  	reg.SetDisableServiceTags(true)
  1773  
  1774  	res, err := applyTemplate(param{File: fileCL, reg: reg})
  1775  	if err != nil {
  1776  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1777  		return
  1778  	}
  1779  
  1780  	if got, want := len(res.Tags), 0; got != want {
  1781  		t.Fatalf("len(applyTemplate(%#v).Tags) = %d want to be %d", file, got, want)
  1782  	}
  1783  
  1784  	// If there was a failure, print out the input and the json result for debugging.
  1785  	if t.Failed() {
  1786  		t.Errorf("had: %s", file)
  1787  		t.Errorf("got: %s", fmt.Sprint(result))
  1788  	}
  1789  }
  1790  
  1791  func TestApplyTemplateOverrideWithOperation(t *testing.T) {
  1792  	newFile := func() *descriptor.File {
  1793  		msgdesc := &descriptorpb.DescriptorProto{
  1794  			Name: proto.String("ExampleMessage"),
  1795  		}
  1796  		meth := &descriptorpb.MethodDescriptorProto{
  1797  			Name:       proto.String("Example"),
  1798  			InputType:  proto.String("ExampleMessage"),
  1799  			OutputType: proto.String("ExampleMessage"),
  1800  			Options:    &descriptorpb.MethodOptions{},
  1801  		}
  1802  		svc := &descriptorpb.ServiceDescriptorProto{
  1803  			Name:   proto.String("ExampleService"),
  1804  			Method: []*descriptorpb.MethodDescriptorProto{meth},
  1805  		}
  1806  		msg := &descriptor.Message{
  1807  			DescriptorProto: msgdesc,
  1808  		}
  1809  		return &descriptor.File{
  1810  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1811  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1812  				Name:           proto.String("example.proto"),
  1813  				Package:        proto.String("example"),
  1814  				MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
  1815  				Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  1816  				Options: &descriptorpb.FileOptions{
  1817  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1818  				},
  1819  			},
  1820  			GoPkg: descriptor.GoPackage{
  1821  				Path: "example.com/path/to/example/example.pb",
  1822  				Name: "example_pb",
  1823  			},
  1824  			Messages: []*descriptor.Message{msg},
  1825  			Services: []*descriptor.Service{
  1826  				{
  1827  					ServiceDescriptorProto: svc,
  1828  					Methods: []*descriptor.Method{
  1829  						{
  1830  							MethodDescriptorProto: meth,
  1831  							RequestType:           msg,
  1832  							ResponseType:          msg,
  1833  							Bindings: []*descriptor.Binding{
  1834  								{
  1835  									HTTPMethod: "GET",
  1836  									Body:       &descriptor.Body{FieldPath: nil},
  1837  									PathTmpl: httprule.Template{
  1838  										Version:  1,
  1839  										OpCodes:  []int{0, 0},
  1840  										Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  1841  									},
  1842  								},
  1843  							},
  1844  						},
  1845  					},
  1846  				},
  1847  			},
  1848  		}
  1849  	}
  1850  
  1851  	verifyTemplateFromReq := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File, opts *openapiconfig.OpenAPIOptions) {
  1852  		if err := AddErrorDefs(reg); err != nil {
  1853  			t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  1854  			return
  1855  		}
  1856  		fileCL := crossLinkFixture(file)
  1857  		err := reg.Load(reqFromFile(fileCL))
  1858  		if err != nil {
  1859  			t.Errorf("reg.Load(%#v) failed with %v; want success", *file, err)
  1860  			return
  1861  		}
  1862  		if opts != nil {
  1863  			if err := reg.RegisterOpenAPIOptions(opts); err != nil {
  1864  				t.Fatalf("failed to register OpenAPI options: %s", err)
  1865  			}
  1866  		}
  1867  		result, err := applyTemplate(param{File: fileCL, reg: reg})
  1868  		if err != nil {
  1869  			t.Errorf("applyTemplate(%#v) failed with %v; want success", *file, err)
  1870  			return
  1871  		}
  1872  
  1873  		if want, is := "MyExample", result.getPathItemObject("/v1/echo").Get.OperationID; !reflect.DeepEqual(is, want) {
  1874  			t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", *file, is, want)
  1875  		}
  1876  		if want, is := []string{"application/xml"}, result.getPathItemObject("/v1/echo").Get.Consumes; !reflect.DeepEqual(is, want) {
  1877  			t.Errorf("applyTemplate(%#v).Paths[0].Get.Consumes = %s want to be %s", *file, is, want)
  1878  		}
  1879  		if want, is := []string{"application/json", "application/xml"}, result.getPathItemObject("/v1/echo").Get.Produces; !reflect.DeepEqual(is, want) {
  1880  			t.Errorf("applyTemplate(%#v).Paths[0].Get.Produces = %s want to be %s", *file, is, want)
  1881  		}
  1882  
  1883  		// If there was a failure, print out the input and the json result for debugging.
  1884  		if t.Failed() {
  1885  			t.Errorf("had: %s", *file)
  1886  			t.Errorf("got: %s", fmt.Sprint(result))
  1887  		}
  1888  	}
  1889  
  1890  	openapiOperation := openapi_options.Operation{
  1891  		OperationId: "MyExample",
  1892  		Consumes:    []string{"application/xml"},
  1893  		Produces:    []string{"application/json", "application/xml"},
  1894  	}
  1895  
  1896  	t.Run("verify override via method option", func(t *testing.T) {
  1897  		file := newFile()
  1898  		proto.SetExtension(proto.Message(file.Services[0].Methods[0].MethodDescriptorProto.Options),
  1899  			openapi_options.E_Openapiv2Operation, &openapiOperation)
  1900  
  1901  		reg := descriptor.NewRegistry()
  1902  		verifyTemplateFromReq(t, reg, file, nil)
  1903  	})
  1904  
  1905  	t.Run("verify override options annotations", func(t *testing.T) {
  1906  		file := newFile()
  1907  		reg := descriptor.NewRegistry()
  1908  		opts := &openapiconfig.OpenAPIOptions{
  1909  			Method: []*openapiconfig.OpenAPIMethodOption{
  1910  				{
  1911  					Method: "example.ExampleService.Example",
  1912  					Option: &openapiOperation,
  1913  				},
  1914  			},
  1915  		}
  1916  		verifyTemplateFromReq(t, reg, file, opts)
  1917  	})
  1918  }
  1919  
  1920  func TestApplyTemplateExtensions(t *testing.T) {
  1921  	newFile := func() *descriptor.File {
  1922  		msgdesc := &descriptorpb.DescriptorProto{
  1923  			Name: proto.String("ExampleMessage"),
  1924  		}
  1925  		meth := &descriptorpb.MethodDescriptorProto{
  1926  			Name:       proto.String("Example"),
  1927  			InputType:  proto.String("ExampleMessage"),
  1928  			OutputType: proto.String("ExampleMessage"),
  1929  			Options:    &descriptorpb.MethodOptions{},
  1930  		}
  1931  		svc := &descriptorpb.ServiceDescriptorProto{
  1932  			Name:   proto.String("ExampleService"),
  1933  			Method: []*descriptorpb.MethodDescriptorProto{meth},
  1934  		}
  1935  		msg := &descriptor.Message{
  1936  			DescriptorProto: msgdesc,
  1937  		}
  1938  		return &descriptor.File{
  1939  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1940  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1941  				Name:           proto.String("example.proto"),
  1942  				Package:        proto.String("example"),
  1943  				MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
  1944  				Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  1945  				Options: &descriptorpb.FileOptions{
  1946  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1947  				},
  1948  			},
  1949  			GoPkg: descriptor.GoPackage{
  1950  				Path: "example.com/path/to/example/example.pb",
  1951  				Name: "example_pb",
  1952  			},
  1953  			Messages: []*descriptor.Message{msg},
  1954  			Services: []*descriptor.Service{
  1955  				{
  1956  					ServiceDescriptorProto: svc,
  1957  					Methods: []*descriptor.Method{
  1958  						{
  1959  							MethodDescriptorProto: meth,
  1960  							RequestType:           msg,
  1961  							ResponseType:          msg,
  1962  							Bindings: []*descriptor.Binding{
  1963  								{
  1964  									HTTPMethod: "GET",
  1965  									Body:       &descriptor.Body{FieldPath: nil},
  1966  									PathTmpl: httprule.Template{
  1967  										Version:  1,
  1968  										OpCodes:  []int{0, 0},
  1969  										Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  1970  									},
  1971  								},
  1972  							},
  1973  						},
  1974  					},
  1975  				},
  1976  			},
  1977  		}
  1978  	}
  1979  	swagger := openapi_options.Swagger{
  1980  		Info: &openapi_options.Info{
  1981  			Title: "test",
  1982  			Extensions: map[string]*structpb.Value{
  1983  				"x-info-extension": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1984  			},
  1985  		},
  1986  		Extensions: map[string]*structpb.Value{
  1987  			"x-foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1988  			"x-bar": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{
  1989  				Values: []*structpb.Value{{Kind: &structpb.Value_StringValue{StringValue: "baz"}}},
  1990  			}}},
  1991  		},
  1992  		SecurityDefinitions: &openapi_options.SecurityDefinitions{
  1993  			Security: map[string]*openapi_options.SecurityScheme{
  1994  				"somescheme": {
  1995  					Extensions: map[string]*structpb.Value{
  1996  						"x-security-baz": {Kind: &structpb.Value_BoolValue{BoolValue: true}},
  1997  					},
  1998  				},
  1999  			},
  2000  		},
  2001  		Tags: []*openapi_options.Tag{
  2002  			{
  2003  				Name:        "test tag",
  2004  				Description: "test tag description",
  2005  				Extensions: map[string]*structpb.Value{
  2006  					"x-traitTag": {Kind: &structpb.Value_BoolValue{BoolValue: true}},
  2007  				},
  2008  			},
  2009  		},
  2010  	}
  2011  	openapiOperation := openapi_options.Operation{
  2012  		Responses: map[string]*openapi_options.Response{
  2013  			"200": {
  2014  				Extensions: map[string]*structpb.Value{
  2015  					"x-resp-id": {Kind: &structpb.Value_StringValue{StringValue: "resp1000"}},
  2016  				},
  2017  			},
  2018  		},
  2019  		Extensions: map[string]*structpb.Value{
  2020  			"x-op-foo": {Kind: &structpb.Value_StringValue{StringValue: "baz"}},
  2021  		},
  2022  	}
  2023  	verifyTemplateExtensions := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File,
  2024  		opts *openapiconfig.OpenAPIOptions) {
  2025  		if err := AddErrorDefs(reg); err != nil {
  2026  			t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  2027  			return
  2028  		}
  2029  		fileCL := crossLinkFixture(file)
  2030  		err := reg.Load(reqFromFile(fileCL))
  2031  		if err != nil {
  2032  			t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  2033  			return
  2034  		}
  2035  		if opts != nil {
  2036  			if err := reg.RegisterOpenAPIOptions(opts); err != nil {
  2037  				t.Fatalf("failed to register OpenAPI annotations: %s", err)
  2038  			}
  2039  		}
  2040  		result, err := applyTemplate(param{File: fileCL, reg: reg})
  2041  		if err != nil {
  2042  			t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2043  			return
  2044  		}
  2045  		if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
  2046  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  2047  		}
  2048  		if got, want := len(result.extensions), 2; got != want {
  2049  			t.Fatalf("len(applyTemplate(%#v).Extensions) = %d want to be %d", file, got, want)
  2050  		}
  2051  		if got, want := result.extensions[0].key, "x-bar"; got != want {
  2052  			t.Errorf("applyTemplate(%#v).Extensions[0].key = %s want to be %s", file, got, want)
  2053  		}
  2054  		if got, want := result.extensions[1].key, "x-foo"; got != want {
  2055  			t.Errorf("applyTemplate(%#v).Extensions[1].key = %s want to be %s", file, got, want)
  2056  		}
  2057  		{
  2058  			var got []string
  2059  			err = marshaler.Unmarshal(result.extensions[0].value, &got)
  2060  			if err != nil {
  2061  				t.Fatalf("marshaler.Unmarshal failed: %v", err)
  2062  			}
  2063  			want := []string{"baz"}
  2064  			if diff := cmp.Diff(got, want); diff != "" {
  2065  				t.Errorf(diff)
  2066  			}
  2067  		}
  2068  		{
  2069  			var got string
  2070  			err = marshaler.Unmarshal(result.extensions[1].value, &got)
  2071  			if err != nil {
  2072  				t.Fatalf("marshaler.Unmarshal failed: %v", err)
  2073  			}
  2074  			want := "bar"
  2075  			if diff := cmp.Diff(got, want); diff != "" {
  2076  				t.Errorf(diff)
  2077  			}
  2078  		}
  2079  
  2080  		var scheme openapiSecuritySchemeObject
  2081  		for _, v := range result.SecurityDefinitions {
  2082  			scheme = v
  2083  		}
  2084  		if want, is, name := []extension{
  2085  			{key: "x-security-baz", value: json.RawMessage("true")},
  2086  		}, scheme.extensions, "SecurityScheme.Extensions"; !reflect.DeepEqual(is, want) {
  2087  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  2088  		}
  2089  
  2090  		if want, is, name := []extension{
  2091  			{key: "x-info-extension", value: json.RawMessage("\"bar\"")},
  2092  		}, result.Info.extensions, "Info.Extensions"; !reflect.DeepEqual(is, want) {
  2093  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  2094  		}
  2095  
  2096  		var operation *openapiOperationObject
  2097  		var response openapiResponseObject
  2098  		for _, v := range result.Paths {
  2099  			operation = v.PathItemObject.Get
  2100  			response = v.PathItemObject.Get.Responses["200"]
  2101  		}
  2102  		if want, is, name := []extension{
  2103  			{key: "x-op-foo", value: json.RawMessage("\"baz\"")},
  2104  		}, operation.extensions, "operation.Extensions"; !reflect.DeepEqual(is, want) {
  2105  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  2106  		}
  2107  		if want, is, name := []extension{
  2108  			{key: "x-resp-id", value: json.RawMessage("\"resp1000\"")},
  2109  		}, response.extensions, "response.Extensions"; !reflect.DeepEqual(is, want) {
  2110  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  2111  		}
  2112  
  2113  		if len(result.Tags) == 0 {
  2114  			t.Errorf("No tags found in result")
  2115  			return
  2116  		}
  2117  		tag := result.Tags[0]
  2118  		if want, is, name := []extension{
  2119  			{key: "x-traitTag", value: json.RawMessage("true")},
  2120  		}, tag.extensions, "Tags[0].Extensions"; !reflect.DeepEqual(is, want) {
  2121  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  2122  		}
  2123  	}
  2124  	t.Run("verify template options set via proto options", func(t *testing.T) {
  2125  		file := newFile()
  2126  		proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), openapi_options.E_Openapiv2Swagger, &swagger)
  2127  		proto.SetExtension(proto.Message(file.Services[0].Methods[0].Options), openapi_options.E_Openapiv2Operation, &openapiOperation)
  2128  		reg := descriptor.NewRegistry()
  2129  		verifyTemplateExtensions(t, reg, file, nil)
  2130  	})
  2131  	t.Run("verify template options set via annotations", func(t *testing.T) {
  2132  		file := newFile()
  2133  		opts := &openapiconfig.OpenAPIOptions{
  2134  			File: []*openapiconfig.OpenAPIFileOption{
  2135  				{
  2136  					File:   "example.proto",
  2137  					Option: &swagger,
  2138  				},
  2139  			},
  2140  			Method: []*openapiconfig.OpenAPIMethodOption{
  2141  				{
  2142  					Method: "example.ExampleService.Example",
  2143  					Option: &openapiOperation,
  2144  				},
  2145  			},
  2146  		}
  2147  		reg := descriptor.NewRegistry()
  2148  		verifyTemplateExtensions(t, reg, file, opts)
  2149  	})
  2150  }
  2151  
  2152  func TestApplyTemplateHeaders(t *testing.T) {
  2153  	newFile := func() *descriptor.File {
  2154  		msgdesc := &descriptorpb.DescriptorProto{
  2155  			Name: proto.String("ExampleMessage"),
  2156  		}
  2157  		meth := &descriptorpb.MethodDescriptorProto{
  2158  			Name:       proto.String("Example"),
  2159  			InputType:  proto.String("ExampleMessage"),
  2160  			OutputType: proto.String("ExampleMessage"),
  2161  			Options:    &descriptorpb.MethodOptions{},
  2162  		}
  2163  		svc := &descriptorpb.ServiceDescriptorProto{
  2164  			Name:   proto.String("ExampleService"),
  2165  			Method: []*descriptorpb.MethodDescriptorProto{meth},
  2166  		}
  2167  		msg := &descriptor.Message{
  2168  			DescriptorProto: msgdesc,
  2169  		}
  2170  		return &descriptor.File{
  2171  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  2172  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  2173  				Name:           proto.String("example.proto"),
  2174  				Package:        proto.String("example"),
  2175  				MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
  2176  				Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  2177  				Options: &descriptorpb.FileOptions{
  2178  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  2179  				},
  2180  			},
  2181  			GoPkg: descriptor.GoPackage{
  2182  				Path: "example.com/path/to/example/example.pb",
  2183  				Name: "example_pb",
  2184  			},
  2185  			Messages: []*descriptor.Message{msg},
  2186  			Services: []*descriptor.Service{
  2187  				{
  2188  					ServiceDescriptorProto: svc,
  2189  					Methods: []*descriptor.Method{
  2190  						{
  2191  							MethodDescriptorProto: meth,
  2192  							RequestType:           msg,
  2193  							ResponseType:          msg,
  2194  							Bindings: []*descriptor.Binding{
  2195  								{
  2196  									HTTPMethod: "GET",
  2197  									Body:       &descriptor.Body{FieldPath: nil},
  2198  									PathTmpl: httprule.Template{
  2199  										Version:  1,
  2200  										OpCodes:  []int{0, 0},
  2201  										Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  2202  									},
  2203  								},
  2204  							},
  2205  						},
  2206  					},
  2207  				},
  2208  			},
  2209  		}
  2210  	}
  2211  	openapiOperation := openapi_options.Operation{
  2212  		Responses: map[string]*openapi_options.Response{
  2213  			"200": {
  2214  				Description: "Testing Headers",
  2215  				Headers: map[string]*openapi_options.Header{
  2216  					"string": {
  2217  						Description: "string header description",
  2218  						Type:        "string",
  2219  						Format:      "uuid",
  2220  						Pattern:     "",
  2221  					},
  2222  					"boolean": {
  2223  						Description: "boolean header description",
  2224  						Type:        "boolean",
  2225  						Default:     "true",
  2226  						Pattern:     "^true|false$",
  2227  					},
  2228  					"integer": {
  2229  						Description: "integer header description",
  2230  						Type:        "integer",
  2231  						Default:     "0",
  2232  						Pattern:     "^[0-9]$",
  2233  					},
  2234  					"number": {
  2235  						Description: "number header description",
  2236  						Type:        "number",
  2237  						Default:     "1.2",
  2238  						Pattern:     "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$",
  2239  					},
  2240  				},
  2241  			},
  2242  		},
  2243  	}
  2244  	verifyTemplateHeaders := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File,
  2245  		opts *openapiconfig.OpenAPIOptions) {
  2246  		if err := AddErrorDefs(reg); err != nil {
  2247  			t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  2248  			return
  2249  		}
  2250  		fileCL := crossLinkFixture(file)
  2251  		err := reg.Load(reqFromFile(fileCL))
  2252  		if err != nil {
  2253  			t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  2254  			return
  2255  		}
  2256  		if opts != nil {
  2257  			if err := reg.RegisterOpenAPIOptions(opts); err != nil {
  2258  				t.Fatalf("failed to register OpenAPI annotations: %s", err)
  2259  			}
  2260  		}
  2261  		result, err := applyTemplate(param{File: fileCL, reg: reg})
  2262  		if err != nil {
  2263  			t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2264  			return
  2265  		}
  2266  		if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
  2267  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  2268  		}
  2269  
  2270  		var response openapiResponseObject
  2271  		for _, v := range result.Paths {
  2272  			response = v.PathItemObject.Get.Responses["200"]
  2273  		}
  2274  		if want, is, name := []openapiHeadersObject{
  2275  			{
  2276  				"String": openapiHeaderObject{
  2277  					Description: "string header description",
  2278  					Type:        "string",
  2279  					Format:      "uuid",
  2280  					Pattern:     "",
  2281  				},
  2282  				"Boolean": openapiHeaderObject{
  2283  					Description: "boolean header description",
  2284  					Type:        "boolean",
  2285  					Default:     RawExample("true"),
  2286  					Pattern:     "^true|false$",
  2287  				},
  2288  				"Integer": openapiHeaderObject{
  2289  					Description: "integer header description",
  2290  					Type:        "integer",
  2291  					Default:     RawExample("0"),
  2292  					Pattern:     "^[0-9]$",
  2293  				},
  2294  				"Number": openapiHeaderObject{
  2295  					Description: "number header description",
  2296  					Type:        "number",
  2297  					Default:     RawExample("1.2"),
  2298  					Pattern:     "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$",
  2299  				},
  2300  			},
  2301  		}[0], response.Headers, "response.Headers"; !reflect.DeepEqual(is, want) {
  2302  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  2303  		}
  2304  
  2305  	}
  2306  	t.Run("verify template options set via proto options", func(t *testing.T) {
  2307  		file := newFile()
  2308  		proto.SetExtension(proto.Message(file.Services[0].Methods[0].Options), openapi_options.E_Openapiv2Operation, &openapiOperation)
  2309  		reg := descriptor.NewRegistry()
  2310  		verifyTemplateHeaders(t, reg, file, nil)
  2311  	})
  2312  }
  2313  
  2314  func TestValidateHeaderType(t *testing.T) {
  2315  	type test struct {
  2316  		Type          string
  2317  		Format        string
  2318  		expectedError error
  2319  	}
  2320  	tests := []test{
  2321  		{
  2322  			"string",
  2323  			"date-time",
  2324  			nil,
  2325  		},
  2326  		{
  2327  			"boolean",
  2328  			"",
  2329  			nil,
  2330  		},
  2331  		{
  2332  			"integer",
  2333  			"uint",
  2334  			nil,
  2335  		},
  2336  		{
  2337  			"integer",
  2338  			"uint8",
  2339  			nil,
  2340  		},
  2341  		{
  2342  			"integer",
  2343  			"uint16",
  2344  			nil,
  2345  		},
  2346  		{
  2347  			"integer",
  2348  			"uint32",
  2349  			nil,
  2350  		},
  2351  		{
  2352  			"integer",
  2353  			"uint64",
  2354  			nil,
  2355  		},
  2356  		{
  2357  			"integer",
  2358  			"int",
  2359  			nil,
  2360  		},
  2361  		{
  2362  			"integer",
  2363  			"int8",
  2364  			nil,
  2365  		},
  2366  		{
  2367  			"integer",
  2368  			"int16",
  2369  			nil,
  2370  		},
  2371  		{
  2372  			"integer",
  2373  			"int32",
  2374  			nil,
  2375  		},
  2376  		{
  2377  			"integer",
  2378  			"int64",
  2379  			nil,
  2380  		},
  2381  		{
  2382  			"integer",
  2383  			"float64",
  2384  			errors.New("the provided format \"float64\" is not a valid extension of the type \"integer\""),
  2385  		},
  2386  		{
  2387  			"integer",
  2388  			"uuid",
  2389  			errors.New("the provided format \"uuid\" is not a valid extension of the type \"integer\""),
  2390  		},
  2391  		{
  2392  			"number",
  2393  			"uint",
  2394  			nil,
  2395  		},
  2396  		{
  2397  			"number",
  2398  			"uint8",
  2399  			nil,
  2400  		},
  2401  		{
  2402  			"number",
  2403  			"uint16",
  2404  			nil,
  2405  		},
  2406  		{
  2407  			"number",
  2408  			"uint32",
  2409  			nil,
  2410  		},
  2411  		{
  2412  			"number",
  2413  			"uint64",
  2414  			nil,
  2415  		},
  2416  		{
  2417  			"number",
  2418  			"int",
  2419  			nil,
  2420  		},
  2421  		{
  2422  			"number",
  2423  			"int8",
  2424  			nil,
  2425  		},
  2426  		{
  2427  			"number",
  2428  			"int16",
  2429  			nil,
  2430  		},
  2431  		{
  2432  			"number",
  2433  			"int32",
  2434  			nil,
  2435  		},
  2436  		{
  2437  			"number",
  2438  			"int64",
  2439  			nil,
  2440  		},
  2441  		{
  2442  			"number",
  2443  			"float",
  2444  			nil,
  2445  		},
  2446  		{
  2447  			"number",
  2448  			"float32",
  2449  			nil,
  2450  		},
  2451  		{
  2452  			"number",
  2453  			"float64",
  2454  			nil,
  2455  		},
  2456  		{
  2457  			"number",
  2458  			"complex64",
  2459  			nil,
  2460  		},
  2461  		{
  2462  			"number",
  2463  			"complex128",
  2464  			nil,
  2465  		},
  2466  		{
  2467  			"number",
  2468  			"double",
  2469  			nil,
  2470  		},
  2471  		{
  2472  			"number",
  2473  			"byte",
  2474  			nil,
  2475  		},
  2476  		{
  2477  			"number",
  2478  			"rune",
  2479  			nil,
  2480  		},
  2481  		{
  2482  			"number",
  2483  			"uintptr",
  2484  			nil,
  2485  		},
  2486  		{
  2487  			"number",
  2488  			"date",
  2489  			errors.New("the provided format \"date\" is not a valid extension of the type \"number\""),
  2490  		},
  2491  		{
  2492  			"array",
  2493  			"",
  2494  			errors.New("the provided header type \"array\" is not supported"),
  2495  		},
  2496  		{
  2497  			"foo",
  2498  			"",
  2499  			errors.New("the provided header type \"foo\" is not supported"),
  2500  		},
  2501  	}
  2502  	for _, v := range tests {
  2503  		err := validateHeaderTypeAndFormat(v.Type, v.Format)
  2504  
  2505  		if v.expectedError == nil {
  2506  			if err != nil {
  2507  				t.Errorf("unexpected error %v", err)
  2508  			}
  2509  		} else {
  2510  			if err == nil {
  2511  				t.Fatal("expected header error not returned")
  2512  			}
  2513  			if err.Error() != v.expectedError.Error() {
  2514  				t.Errorf("expected error malformed, expected %q, got %q", v.expectedError.Error(), err.Error())
  2515  			}
  2516  		}
  2517  	}
  2518  
  2519  }
  2520  
  2521  func TestValidateDefaultValueType(t *testing.T) {
  2522  	type test struct {
  2523  		Type          string
  2524  		Value         string
  2525  		Format        string
  2526  		expectedError error
  2527  	}
  2528  	tests := []test{
  2529  		{
  2530  			"string",
  2531  			`"string"`,
  2532  			"",
  2533  			nil,
  2534  		},
  2535  		{
  2536  			"string",
  2537  			"\"2012-11-01T22:08:41+00:00\"",
  2538  			"date-time",
  2539  			nil,
  2540  		},
  2541  		{
  2542  			"string",
  2543  			"\"2012-11-01\"",
  2544  			"date",
  2545  			nil,
  2546  		},
  2547  		{
  2548  			"string",
  2549  			"0",
  2550  			"",
  2551  			errors.New("the provided default value \"0\" does not match provider type \"string\", or is not properly quoted with escaped quotations"),
  2552  		},
  2553  		{
  2554  			"string",
  2555  			"false",
  2556  			"",
  2557  			errors.New("the provided default value \"false\" does not match provider type \"string\", or is not properly quoted with escaped quotations"),
  2558  		},
  2559  		{
  2560  			"boolean",
  2561  			"true",
  2562  			"",
  2563  			nil,
  2564  		},
  2565  		{
  2566  			"boolean",
  2567  			"0",
  2568  			"",
  2569  			errors.New("the provided default value \"0\" does not match provider type \"boolean\""),
  2570  		},
  2571  		{
  2572  			"boolean",
  2573  			`"string"`,
  2574  			"",
  2575  			errors.New("the provided default value \"\\\"string\\\"\" does not match provider type \"boolean\""),
  2576  		},
  2577  		{
  2578  			"number",
  2579  			"1.2",
  2580  			"",
  2581  			nil,
  2582  		},
  2583  		{
  2584  			"number",
  2585  			"123",
  2586  			"",
  2587  			nil,
  2588  		},
  2589  		{
  2590  			"number",
  2591  			"nan",
  2592  			"",
  2593  			errors.New("the provided number \"nan\" is not a valid JSON number"),
  2594  		},
  2595  		{
  2596  			"number",
  2597  			"NaN",
  2598  			"",
  2599  			errors.New("the provided number \"NaN\" is not a valid JSON number"),
  2600  		},
  2601  		{
  2602  			"number",
  2603  			"-459.67",
  2604  			"",
  2605  			nil,
  2606  		},
  2607  		{
  2608  			"number",
  2609  			"inf",
  2610  			"",
  2611  			errors.New("the provided number \"inf\" is not a valid JSON number"),
  2612  		},
  2613  		{
  2614  			"number",
  2615  			"infinity",
  2616  			"",
  2617  			errors.New("the provided number \"infinity\" is not a valid JSON number"),
  2618  		},
  2619  		{
  2620  			"number",
  2621  			"Inf",
  2622  			"",
  2623  			errors.New("the provided number \"Inf\" is not a valid JSON number"),
  2624  		},
  2625  		{
  2626  			"number",
  2627  			"Infinity",
  2628  			"",
  2629  			errors.New("the provided number \"Infinity\" is not a valid JSON number"),
  2630  		},
  2631  		{
  2632  			"number",
  2633  			"false",
  2634  			"",
  2635  			errors.New("the provided default value \"false\" does not match provider type \"number\""),
  2636  		},
  2637  		{
  2638  			"number",
  2639  			`"string"`,
  2640  			"",
  2641  			errors.New("the provided default value \"\\\"string\\\"\" does not match provider type \"number\""),
  2642  		},
  2643  		{
  2644  			"integer",
  2645  			"2",
  2646  			"",
  2647  			nil,
  2648  		},
  2649  		{
  2650  			"integer",
  2651  			fmt.Sprint(math.MaxInt32),
  2652  			"int32",
  2653  			nil,
  2654  		},
  2655  		{
  2656  			"integer",
  2657  			fmt.Sprint(math.MaxInt32 + 1),
  2658  			"int32",
  2659  			errors.New("the provided default value \"2147483648\" does not match provided format \"int32\""),
  2660  		},
  2661  		{
  2662  			"integer",
  2663  			fmt.Sprint(math.MaxInt64),
  2664  			"int64",
  2665  			nil,
  2666  		},
  2667  		{
  2668  			"integer",
  2669  			"9223372036854775808",
  2670  			"int64",
  2671  			errors.New("the provided default value \"9223372036854775808\" does not match provided format \"int64\""),
  2672  		},
  2673  		{
  2674  			"integer",
  2675  			"18446744073709551615",
  2676  			"uint64",
  2677  			nil,
  2678  		},
  2679  		{
  2680  			"integer",
  2681  			"false",
  2682  			"",
  2683  			errors.New("the provided default value \"false\" does not match provided type \"integer\""),
  2684  		},
  2685  		{
  2686  			"integer",
  2687  			"1.2",
  2688  			"",
  2689  			errors.New("the provided default value \"1.2\" does not match provided type \"integer\""),
  2690  		},
  2691  		{
  2692  			"integer",
  2693  			`"string"`,
  2694  			"",
  2695  			errors.New("the provided default value \"\\\"string\\\"\" does not match provided type \"integer\""),
  2696  		},
  2697  	}
  2698  	for _, v := range tests {
  2699  		err := validateDefaultValueTypeAndFormat(v.Type, v.Value, v.Format)
  2700  
  2701  		if v.expectedError == nil {
  2702  			if err != nil {
  2703  				t.Errorf("unexpected error '%v'", err)
  2704  			}
  2705  		} else {
  2706  			if err == nil {
  2707  				t.Error("expected update error not returned")
  2708  			}
  2709  			if err.Error() != v.expectedError.Error() {
  2710  				t.Errorf("expected error malformed, expected %q, got %q", v.expectedError.Error(), err.Error())
  2711  			}
  2712  		}
  2713  	}
  2714  
  2715  }
  2716  
  2717  func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) {
  2718  	msgdesc := &descriptorpb.DescriptorProto{
  2719  		Name: proto.String("ExampleMessage"),
  2720  		Field: []*descriptorpb.FieldDescriptorProto{
  2721  			{
  2722  				Name:     proto.String("nested"),
  2723  				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2724  				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2725  				TypeName: proto.String("NestedMessage"),
  2726  				Number:   proto.Int32(1),
  2727  			},
  2728  		},
  2729  	}
  2730  	nesteddesc := &descriptorpb.DescriptorProto{
  2731  		Name: proto.String("NestedMessage"),
  2732  		Field: []*descriptorpb.FieldDescriptorProto{
  2733  			{
  2734  				Name:   proto.String("int32"),
  2735  				Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2736  				Type:   descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  2737  				Number: proto.Int32(1),
  2738  			},
  2739  			{
  2740  				Name:   proto.String("bool"),
  2741  				Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2742  				Type:   descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
  2743  				Number: proto.Int32(2),
  2744  			},
  2745  		},
  2746  	}
  2747  	meth := &descriptorpb.MethodDescriptorProto{
  2748  		Name:            proto.String("Echo"),
  2749  		InputType:       proto.String("ExampleMessage"),
  2750  		OutputType:      proto.String("ExampleMessage"),
  2751  		ClientStreaming: proto.Bool(false),
  2752  	}
  2753  	svc := &descriptorpb.ServiceDescriptorProto{
  2754  		Name:   proto.String("ExampleService"),
  2755  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  2756  	}
  2757  
  2758  	meth.ServerStreaming = proto.Bool(false)
  2759  
  2760  	msg := &descriptor.Message{
  2761  		DescriptorProto: msgdesc,
  2762  	}
  2763  	nested := &descriptor.Message{
  2764  		DescriptorProto: nesteddesc,
  2765  	}
  2766  
  2767  	nestedField := &descriptor.Field{
  2768  		Message:              msg,
  2769  		FieldDescriptorProto: msg.GetField()[0],
  2770  	}
  2771  	intField := &descriptor.Field{
  2772  		Message:              nested,
  2773  		FieldDescriptorProto: nested.GetField()[0],
  2774  	}
  2775  	file := descriptor.File{
  2776  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  2777  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  2778  			Name:           proto.String("example.proto"),
  2779  			Package:        proto.String("example"),
  2780  			MessageType:    []*descriptorpb.DescriptorProto{msgdesc, nesteddesc},
  2781  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  2782  			Options: &descriptorpb.FileOptions{
  2783  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  2784  			},
  2785  		},
  2786  		GoPkg: descriptor.GoPackage{
  2787  			Path: "example.com/path/to/example/example.pb",
  2788  			Name: "example_pb",
  2789  		},
  2790  		Messages: []*descriptor.Message{msg, nested},
  2791  		Services: []*descriptor.Service{
  2792  			{
  2793  				ServiceDescriptorProto: svc,
  2794  				Methods: []*descriptor.Method{
  2795  					{
  2796  						MethodDescriptorProto: meth,
  2797  						RequestType:           msg,
  2798  						ResponseType:          msg,
  2799  						Bindings: []*descriptor.Binding{
  2800  							{
  2801  								HTTPMethod: "POST",
  2802  								PathTmpl: httprule.Template{
  2803  									Version:  1,
  2804  									OpCodes:  []int{0, 0},
  2805  									Template: "/v1/echo", // TODO(achew): Figure out what this should really be
  2806  								},
  2807  								PathParams: []descriptor.Parameter{
  2808  									{
  2809  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2810  											{
  2811  												Name:   "nested",
  2812  												Target: nestedField,
  2813  											},
  2814  											{
  2815  												Name:   "int32",
  2816  												Target: intField,
  2817  											},
  2818  										}),
  2819  										Target: intField,
  2820  									},
  2821  								},
  2822  								Body: &descriptor.Body{
  2823  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2824  										{
  2825  											Name:   "nested",
  2826  											Target: nestedField,
  2827  										},
  2828  									}),
  2829  								},
  2830  							},
  2831  						},
  2832  					},
  2833  				},
  2834  			},
  2835  		},
  2836  	}
  2837  	reg := descriptor.NewRegistry()
  2838  	if err := AddErrorDefs(reg); err != nil {
  2839  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  2840  		return
  2841  	}
  2842  	fmt.Fprintln(os.Stderr, "fd", file.FileDescriptorProto)
  2843  	err := reg.Load(&pluginpb.CodeGeneratorRequest{
  2844  		ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  2845  	})
  2846  	if err != nil {
  2847  		t.Fatalf("failed to load code generator request: %v", err)
  2848  	}
  2849  	fmt.Fprintln(os.Stderr, "AllFQMNs", reg.GetAllFQMNs())
  2850  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  2851  	if err != nil {
  2852  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2853  		return
  2854  	}
  2855  	if want, got := "2.0", result.Swagger; !reflect.DeepEqual(got, want) {
  2856  		t.Errorf("applyTemplate(%#v).Swagger = %s want to be %s", file, got, want)
  2857  	}
  2858  	if want, got := "", result.BasePath; !reflect.DeepEqual(got, want) {
  2859  		t.Errorf("applyTemplate(%#v).BasePath = %s want to be %s", file, got, want)
  2860  	}
  2861  	if want, got := ([]string)(nil), result.Schemes; !reflect.DeepEqual(got, want) {
  2862  		t.Errorf("applyTemplate(%#v).Schemes = %s want to be %s", file, got, want)
  2863  	}
  2864  	if want, got := []string{"application/json"}, result.Consumes; !reflect.DeepEqual(got, want) {
  2865  		t.Errorf("applyTemplate(%#v).Consumes = %s want to be %s", file, got, want)
  2866  	}
  2867  	if want, got := []string{"application/json"}, result.Produces; !reflect.DeepEqual(got, want) {
  2868  		t.Errorf("applyTemplate(%#v).Produces = %s want to be %s", file, got, want)
  2869  	}
  2870  
  2871  	// If there was a failure, print out the input and the json result for debugging.
  2872  	if t.Failed() {
  2873  		t.Errorf("had: %s", file)
  2874  		t.Errorf("got: %s", fmt.Sprint(result))
  2875  	}
  2876  }
  2877  
  2878  func TestApplyTemplateRequestWithClientStreaming(t *testing.T) {
  2879  	msgdesc := &descriptorpb.DescriptorProto{
  2880  		Name: proto.String("ExampleMessage"),
  2881  		Field: []*descriptorpb.FieldDescriptorProto{
  2882  			{
  2883  				Name:     proto.String("nested"),
  2884  				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2885  				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2886  				TypeName: proto.String("NestedMessage"),
  2887  				Number:   proto.Int32(1),
  2888  			},
  2889  		},
  2890  	}
  2891  	nesteddesc := &descriptorpb.DescriptorProto{
  2892  		Name: proto.String("NestedMessage"),
  2893  		Field: []*descriptorpb.FieldDescriptorProto{
  2894  			{
  2895  				Name:   proto.String("int32"),
  2896  				Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2897  				Type:   descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  2898  				Number: proto.Int32(1),
  2899  			},
  2900  			{
  2901  				Name:   proto.String("bool"),
  2902  				Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2903  				Type:   descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
  2904  				Number: proto.Int32(2),
  2905  			},
  2906  		},
  2907  	}
  2908  	meth := &descriptorpb.MethodDescriptorProto{
  2909  		Name:            proto.String("Echo"),
  2910  		InputType:       proto.String("ExampleMessage"),
  2911  		OutputType:      proto.String("ExampleMessage"),
  2912  		ClientStreaming: proto.Bool(true),
  2913  		ServerStreaming: proto.Bool(true),
  2914  	}
  2915  	svc := &descriptorpb.ServiceDescriptorProto{
  2916  		Name:   proto.String("ExampleService"),
  2917  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  2918  	}
  2919  
  2920  	msg := &descriptor.Message{
  2921  		DescriptorProto: msgdesc,
  2922  	}
  2923  	nested := &descriptor.Message{
  2924  		DescriptorProto: nesteddesc,
  2925  	}
  2926  
  2927  	nestedField := &descriptor.Field{
  2928  		Message:              msg,
  2929  		FieldDescriptorProto: msg.GetField()[0],
  2930  	}
  2931  	intField := &descriptor.Field{
  2932  		Message:              nested,
  2933  		FieldDescriptorProto: nested.GetField()[0],
  2934  	}
  2935  	file := descriptor.File{
  2936  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  2937  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  2938  			Name:           proto.String("example.proto"),
  2939  			Package:        proto.String("example"),
  2940  			MessageType:    []*descriptorpb.DescriptorProto{msgdesc, nesteddesc},
  2941  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  2942  			Options: &descriptorpb.FileOptions{
  2943  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  2944  			},
  2945  		},
  2946  		GoPkg: descriptor.GoPackage{
  2947  			Path: "example.com/path/to/example/example.pb",
  2948  			Name: "example_pb",
  2949  		},
  2950  		Messages: []*descriptor.Message{msg, nested},
  2951  		Services: []*descriptor.Service{
  2952  			{
  2953  				ServiceDescriptorProto: svc,
  2954  				Methods: []*descriptor.Method{
  2955  					{
  2956  						MethodDescriptorProto: meth,
  2957  						RequestType:           msg,
  2958  						ResponseType:          msg,
  2959  						Bindings: []*descriptor.Binding{
  2960  							{
  2961  								HTTPMethod: "POST",
  2962  								PathTmpl: httprule.Template{
  2963  									Version:  1,
  2964  									OpCodes:  []int{0, 0},
  2965  									Template: "/v1/echo", // TODO(achew): Figure out what this should really be
  2966  								},
  2967  								PathParams: []descriptor.Parameter{
  2968  									{
  2969  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2970  											{
  2971  												Name:   "nested",
  2972  												Target: nestedField,
  2973  											},
  2974  											{
  2975  												Name:   "int32",
  2976  												Target: intField,
  2977  											},
  2978  										}),
  2979  										Target: intField,
  2980  									},
  2981  								},
  2982  								Body: &descriptor.Body{
  2983  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2984  										{
  2985  											Name:   "nested",
  2986  											Target: nestedField,
  2987  										},
  2988  									}),
  2989  								},
  2990  							},
  2991  						},
  2992  					},
  2993  				},
  2994  			},
  2995  		},
  2996  	}
  2997  	reg := descriptor.NewRegistry()
  2998  	if err := AddErrorDefs(reg); err != nil {
  2999  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  3000  		return
  3001  	}
  3002  	err := reg.Load(&pluginpb.CodeGeneratorRequest{
  3003  		ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  3004  	})
  3005  	if err != nil {
  3006  		t.Fatalf("failed to load code generator request: %v", err)
  3007  	}
  3008  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  3009  	if err != nil {
  3010  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  3011  		return
  3012  	}
  3013  
  3014  	// Only ExampleMessage must be present, not NestedMessage
  3015  	if want, got, name := 3, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
  3016  		t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  3017  	}
  3018  	if _, ok := result.getPathItemObject("/v1/echo").Post.Responses["200"]; !ok {
  3019  		t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.getPathItemObject("/v1/echo").Post.Responses["200"]`)
  3020  	} else {
  3021  		if want, got, name := "A successful response.(streaming responses)", result.getPathItemObject("/v1/echo").Post.Responses["200"].Description, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Description`; !reflect.DeepEqual(got, want) {
  3022  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3023  		}
  3024  		streamExampleExampleMessage := result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema
  3025  		if want, got, name := "object", streamExampleExampleMessage.Type, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Type`; !reflect.DeepEqual(got, want) {
  3026  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3027  		}
  3028  		if want, got, name := "Stream result of exampleExampleMessage", streamExampleExampleMessage.Title, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Title`; !reflect.DeepEqual(got, want) {
  3029  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3030  		}
  3031  		streamExampleExampleMessageProperties := *(streamExampleExampleMessage.Properties)
  3032  		if want, got, name := 2, len(streamExampleExampleMessageProperties), `len(StreamDefinitions["exampleExampleMessage"].Properties)`; !reflect.DeepEqual(got, want) {
  3033  			t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  3034  		} else {
  3035  			resultProperty := streamExampleExampleMessageProperties[0]
  3036  			if want, got, name := "result", resultProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
  3037  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3038  			}
  3039  			result := resultProperty.Value.(openapiSchemaObject)
  3040  			if want, got, name := "#/definitions/exampleExampleMessage", result.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(openapiSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
  3041  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3042  			}
  3043  			errorProperty := streamExampleExampleMessageProperties[1]
  3044  			if want, got, name := "error", errorProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
  3045  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3046  			}
  3047  			err := errorProperty.Value.(openapiSchemaObject)
  3048  			if want, got, name := "#/definitions/rpcStatus", err.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(openapiSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
  3049  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3050  			}
  3051  		}
  3052  	}
  3053  
  3054  	// If there was a failure, print out the input and the json result for debugging.
  3055  	if t.Failed() {
  3056  		t.Errorf("had: %s", file)
  3057  		t.Errorf("got: %s", fmt.Sprint(result))
  3058  	}
  3059  }
  3060  
  3061  func TestApplyTemplateRequestWithServerStreamingAndNoStandardErrors(t *testing.T) {
  3062  	msgdesc := &descriptorpb.DescriptorProto{
  3063  		Name: proto.String("ExampleMessage"),
  3064  		Field: []*descriptorpb.FieldDescriptorProto{
  3065  			{
  3066  				Name:     proto.String("nested"),
  3067  				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3068  				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3069  				TypeName: proto.String("NestedMessage"),
  3070  				Number:   proto.Int32(1),
  3071  			},
  3072  		},
  3073  	}
  3074  	nesteddesc := &descriptorpb.DescriptorProto{
  3075  		Name: proto.String("NestedMessage"),
  3076  		Field: []*descriptorpb.FieldDescriptorProto{
  3077  			{
  3078  				Name:   proto.String("int32"),
  3079  				Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3080  				Type:   descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  3081  				Number: proto.Int32(1),
  3082  			},
  3083  			{
  3084  				Name:   proto.String("bool"),
  3085  				Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3086  				Type:   descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
  3087  				Number: proto.Int32(2),
  3088  			},
  3089  		},
  3090  	}
  3091  	meth := &descriptorpb.MethodDescriptorProto{
  3092  		Name:            proto.String("Echo"),
  3093  		InputType:       proto.String("ExampleMessage"),
  3094  		OutputType:      proto.String("ExampleMessage"),
  3095  		ClientStreaming: proto.Bool(false),
  3096  		ServerStreaming: proto.Bool(true),
  3097  	}
  3098  	svc := &descriptorpb.ServiceDescriptorProto{
  3099  		Name:   proto.String("ExampleService"),
  3100  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  3101  	}
  3102  
  3103  	msg := &descriptor.Message{
  3104  		DescriptorProto: msgdesc,
  3105  	}
  3106  	nested := &descriptor.Message{
  3107  		DescriptorProto: nesteddesc,
  3108  	}
  3109  
  3110  	nestedField := &descriptor.Field{
  3111  		Message:              msg,
  3112  		FieldDescriptorProto: msg.GetField()[0],
  3113  	}
  3114  	intField := &descriptor.Field{
  3115  		Message:              nested,
  3116  		FieldDescriptorProto: nested.GetField()[0],
  3117  	}
  3118  	file := descriptor.File{
  3119  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  3120  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  3121  			Name:           proto.String("example.proto"),
  3122  			Package:        proto.String("example"),
  3123  			MessageType:    []*descriptorpb.DescriptorProto{msgdesc, nesteddesc},
  3124  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  3125  			Options: &descriptorpb.FileOptions{
  3126  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  3127  			},
  3128  		},
  3129  		GoPkg: descriptor.GoPackage{
  3130  			Path: "example.com/path/to/example/example.pb",
  3131  			Name: "example_pb",
  3132  		},
  3133  		Messages: []*descriptor.Message{msg, nested},
  3134  		Services: []*descriptor.Service{
  3135  			{
  3136  				ServiceDescriptorProto: svc,
  3137  				Methods: []*descriptor.Method{
  3138  					{
  3139  						MethodDescriptorProto: meth,
  3140  						RequestType:           msg,
  3141  						ResponseType:          msg,
  3142  						Bindings: []*descriptor.Binding{
  3143  							{
  3144  								HTTPMethod: "POST",
  3145  								PathTmpl: httprule.Template{
  3146  									Version:  1,
  3147  									OpCodes:  []int{0, 0},
  3148  									Template: "/v1/echo",
  3149  								},
  3150  								PathParams: []descriptor.Parameter{
  3151  									{
  3152  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  3153  											{
  3154  												Name:   "nested",
  3155  												Target: nestedField,
  3156  											},
  3157  											{
  3158  												Name:   "int32",
  3159  												Target: intField,
  3160  											},
  3161  										}),
  3162  										Target: intField,
  3163  									},
  3164  								},
  3165  								Body: &descriptor.Body{
  3166  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  3167  										{
  3168  											Name:   "nested",
  3169  											Target: nestedField,
  3170  										},
  3171  									}),
  3172  								},
  3173  							},
  3174  						},
  3175  					},
  3176  				},
  3177  			},
  3178  		},
  3179  	}
  3180  	reg := descriptor.NewRegistry()
  3181  	if err := AddErrorDefs(reg); err != nil {
  3182  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  3183  		return
  3184  	}
  3185  	err := reg.Load(&pluginpb.CodeGeneratorRequest{
  3186  		ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  3187  	})
  3188  	if err != nil {
  3189  		t.Fatalf("failed to load code generator request: %v", err)
  3190  	}
  3191  	reg.SetDisableDefaultErrors(true)
  3192  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  3193  	if err != nil {
  3194  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  3195  		return
  3196  	}
  3197  
  3198  	// Should only include the message, no status or any type
  3199  	if want, got, name := 1, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
  3200  		t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  3201  	}
  3202  	if _, ok := result.getPathItemObject("/v1/echo").Post.Responses["200"]; !ok {
  3203  		t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.getPathItemObject("/v1/echo").Post.Responses["200"]`)
  3204  	} else {
  3205  		if want, got, name := "A successful response.(streaming responses)", result.getPathItemObject("/v1/echo").Post.Responses["200"].Description, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Description`; !reflect.DeepEqual(got, want) {
  3206  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3207  		}
  3208  		streamExampleExampleMessage := result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema
  3209  		if want, got, name := "object", streamExampleExampleMessage.Type, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Type`; !reflect.DeepEqual(got, want) {
  3210  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3211  		}
  3212  		if want, got, name := "Stream result of exampleExampleMessage", streamExampleExampleMessage.Title, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Title`; !reflect.DeepEqual(got, want) {
  3213  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3214  		}
  3215  		streamExampleExampleMessageProperties := *(streamExampleExampleMessage.Properties)
  3216  		if want, got, name := 1, len(streamExampleExampleMessageProperties), `len(StreamDefinitions["exampleExampleMessage"].Properties)`; !reflect.DeepEqual(got, want) {
  3217  			t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  3218  		} else {
  3219  			resultProperty := streamExampleExampleMessageProperties[0]
  3220  			if want, got, name := "result", resultProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
  3221  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3222  			}
  3223  			result := resultProperty.Value.(openapiSchemaObject)
  3224  			if want, got, name := "#/definitions/exampleExampleMessage", result.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(openapiSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
  3225  				t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  3226  			}
  3227  		}
  3228  	}
  3229  
  3230  	// If there was a failure, print out the input and the json result for debugging.
  3231  	if t.Failed() {
  3232  		t.Errorf("had: %s", file)
  3233  		t.Errorf("got: %s", fmt.Sprint(result))
  3234  	}
  3235  }
  3236  
  3237  func TestApplyTemplateRequestWithUnusedReferences(t *testing.T) {
  3238  	reqdesc := &descriptorpb.DescriptorProto{
  3239  		Name: proto.String("ExampleMessage"),
  3240  		Field: []*descriptorpb.FieldDescriptorProto{
  3241  			{
  3242  				Name:   proto.String("string"),
  3243  				Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3244  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3245  				Number: proto.Int32(1),
  3246  			},
  3247  		},
  3248  	}
  3249  	respdesc := &descriptorpb.DescriptorProto{
  3250  		Name: proto.String("EmptyMessage"),
  3251  	}
  3252  	meth := &descriptorpb.MethodDescriptorProto{
  3253  		Name:            proto.String("Example"),
  3254  		InputType:       proto.String("ExampleMessage"),
  3255  		OutputType:      proto.String("EmptyMessage"),
  3256  		ClientStreaming: proto.Bool(false),
  3257  		ServerStreaming: proto.Bool(false),
  3258  	}
  3259  	svc := &descriptorpb.ServiceDescriptorProto{
  3260  		Name:   proto.String("ExampleService"),
  3261  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  3262  	}
  3263  
  3264  	req := &descriptor.Message{
  3265  		DescriptorProto: reqdesc,
  3266  	}
  3267  	resp := &descriptor.Message{
  3268  		DescriptorProto: respdesc,
  3269  	}
  3270  	stringField := &descriptor.Field{
  3271  		Message:              req,
  3272  		FieldDescriptorProto: req.GetField()[0],
  3273  	}
  3274  	file := descriptor.File{
  3275  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  3276  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  3277  			Name:           proto.String("example.proto"),
  3278  			Package:        proto.String("example"),
  3279  			MessageType:    []*descriptorpb.DescriptorProto{reqdesc, respdesc},
  3280  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  3281  			Options: &descriptorpb.FileOptions{
  3282  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  3283  			},
  3284  		},
  3285  		GoPkg: descriptor.GoPackage{
  3286  			Path: "example.com/path/to/example/example.pb",
  3287  			Name: "example_pb",
  3288  		},
  3289  		Messages: []*descriptor.Message{req, resp},
  3290  		Services: []*descriptor.Service{
  3291  			{
  3292  				ServiceDescriptorProto: svc,
  3293  				Methods: []*descriptor.Method{
  3294  					{
  3295  						MethodDescriptorProto: meth,
  3296  						RequestType:           req,
  3297  						ResponseType:          resp,
  3298  						Bindings: []*descriptor.Binding{
  3299  							{
  3300  								HTTPMethod: "GET",
  3301  								PathTmpl: httprule.Template{
  3302  									Version:  1,
  3303  									OpCodes:  []int{0, 0},
  3304  									Template: "/v1/example",
  3305  								},
  3306  							},
  3307  							{
  3308  								HTTPMethod: "POST",
  3309  								PathTmpl: httprule.Template{
  3310  									Version:  1,
  3311  									OpCodes:  []int{0, 0},
  3312  									Template: "/v1/example/{string}",
  3313  								},
  3314  								PathParams: []descriptor.Parameter{
  3315  									{
  3316  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  3317  											{
  3318  												Name:   "string",
  3319  												Target: stringField,
  3320  											},
  3321  										}),
  3322  										Target: stringField,
  3323  									},
  3324  								},
  3325  								Body: &descriptor.Body{
  3326  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  3327  										{
  3328  											Name:   "string",
  3329  											Target: stringField,
  3330  										},
  3331  									}),
  3332  								},
  3333  							},
  3334  						},
  3335  					},
  3336  				},
  3337  			},
  3338  		},
  3339  	}
  3340  
  3341  	reg := descriptor.NewRegistry()
  3342  	if err := AddErrorDefs(reg); err != nil {
  3343  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  3344  		return
  3345  	}
  3346  	err := reg.Load(&pluginpb.CodeGeneratorRequest{
  3347  		ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  3348  	})
  3349  	if err != nil {
  3350  		t.Fatalf("failed to load code generator request: %v", err)
  3351  	}
  3352  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  3353  	if err != nil {
  3354  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  3355  		return
  3356  	}
  3357  
  3358  	// Only EmptyMessage must be present, not ExampleMessage (plus error status)
  3359  	if want, got, name := 3, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
  3360  		t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  3361  	}
  3362  
  3363  	// If there was a failure, print out the input and the json result for debugging.
  3364  	if t.Failed() {
  3365  		t.Errorf("had: %s", file)
  3366  		t.Errorf("got: %s", fmt.Sprint(result))
  3367  	}
  3368  }
  3369  
  3370  func TestApplyTemplateRequestWithBodyQueryParameters(t *testing.T) {
  3371  	bookDesc := &descriptorpb.DescriptorProto{
  3372  		Name: proto.String("Book"),
  3373  		Field: []*descriptorpb.FieldDescriptorProto{
  3374  			{
  3375  				Name:   proto.String("name"),
  3376  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3377  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3378  				Number: proto.Int32(1),
  3379  			},
  3380  			{
  3381  				Name:   proto.String("id"),
  3382  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3383  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3384  				Number: proto.Int32(2),
  3385  			},
  3386  		},
  3387  	}
  3388  	createDesc := &descriptorpb.DescriptorProto{
  3389  		Name: proto.String("CreateBookRequest"),
  3390  		Field: []*descriptorpb.FieldDescriptorProto{
  3391  			{
  3392  				Name:   proto.String("parent"),
  3393  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3394  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3395  				Number: proto.Int32(1),
  3396  			},
  3397  			{
  3398  				Name:   proto.String("book"),
  3399  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3400  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3401  				Number: proto.Int32(2),
  3402  			},
  3403  			{
  3404  				Name:   proto.String("book_id"),
  3405  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3406  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3407  				Number: proto.Int32(3),
  3408  			},
  3409  		},
  3410  	}
  3411  	meth := &descriptorpb.MethodDescriptorProto{
  3412  		Name:       proto.String("CreateBook"),
  3413  		InputType:  proto.String("CreateBookRequest"),
  3414  		OutputType: proto.String("Book"),
  3415  	}
  3416  	svc := &descriptorpb.ServiceDescriptorProto{
  3417  		Name:   proto.String("BookService"),
  3418  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  3419  	}
  3420  
  3421  	bookMsg := &descriptor.Message{
  3422  		DescriptorProto: bookDesc,
  3423  	}
  3424  	createMsg := &descriptor.Message{
  3425  		DescriptorProto: createDesc,
  3426  	}
  3427  
  3428  	parentField := &descriptor.Field{
  3429  		Message:              createMsg,
  3430  		FieldDescriptorProto: createMsg.GetField()[0],
  3431  	}
  3432  	bookField := &descriptor.Field{
  3433  		Message:              createMsg,
  3434  		FieldMessage:         bookMsg,
  3435  		FieldDescriptorProto: createMsg.GetField()[1],
  3436  	}
  3437  	bookIDField := &descriptor.Field{
  3438  		Message:              createMsg,
  3439  		FieldDescriptorProto: createMsg.GetField()[2],
  3440  	}
  3441  
  3442  	createMsg.Fields = []*descriptor.Field{parentField, bookField, bookIDField}
  3443  
  3444  	newFile := func() descriptor.File {
  3445  		return descriptor.File{
  3446  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  3447  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  3448  				Name:           proto.String("book.proto"),
  3449  				MessageType:    []*descriptorpb.DescriptorProto{bookDesc, createDesc},
  3450  				Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  3451  				Options: &descriptorpb.FileOptions{
  3452  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  3453  				},
  3454  			},
  3455  			GoPkg: descriptor.GoPackage{
  3456  				Path: "example.com/path/to/book.pb",
  3457  				Name: "book_pb",
  3458  			},
  3459  			Messages: []*descriptor.Message{bookMsg, createMsg},
  3460  			Services: []*descriptor.Service{
  3461  				{
  3462  					ServiceDescriptorProto: svc,
  3463  					Methods: []*descriptor.Method{
  3464  						{
  3465  							MethodDescriptorProto: meth,
  3466  							RequestType:           createMsg,
  3467  							ResponseType:          bookMsg,
  3468  							Bindings: []*descriptor.Binding{
  3469  								{
  3470  									HTTPMethod: "POST",
  3471  									PathTmpl: httprule.Template{
  3472  										Version:  1,
  3473  										OpCodes:  []int{0, 0},
  3474  										Template: "/v1/{parent=publishers/*}/books",
  3475  									},
  3476  									PathParams: []descriptor.Parameter{
  3477  										{
  3478  											FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  3479  												{
  3480  													Name:   "parent",
  3481  													Target: parentField,
  3482  												},
  3483  											}),
  3484  											Target: parentField,
  3485  										},
  3486  									},
  3487  									Body: &descriptor.Body{
  3488  										FieldPath: []descriptor.FieldPathComponent{
  3489  											{
  3490  												Name:   "book",
  3491  												Target: bookField,
  3492  											},
  3493  										},
  3494  									},
  3495  								},
  3496  							},
  3497  						},
  3498  					},
  3499  				},
  3500  			},
  3501  		}
  3502  	}
  3503  	type args struct {
  3504  		file descriptor.File
  3505  	}
  3506  	type paramOut struct {
  3507  		Name     string
  3508  		In       string
  3509  		Required bool
  3510  	}
  3511  	tests := []struct {
  3512  		name string
  3513  		args args
  3514  		want []paramOut
  3515  	}{
  3516  		{
  3517  			name: "book_in_body",
  3518  			args: args{file: newFile()},
  3519  			want: []paramOut{
  3520  				{"parent", "path", true},
  3521  				{"book", "body", true},
  3522  				{"book_id", "query", false},
  3523  			},
  3524  		},
  3525  		{
  3526  			name: "book_in_query",
  3527  			args: args{file: func() descriptor.File {
  3528  				f := newFile()
  3529  				f.Services[0].Methods[0].Bindings[0].Body = nil
  3530  				return f
  3531  			}()},
  3532  			want: []paramOut{
  3533  				{"parent", "path", true},
  3534  				{"book", "query", false},
  3535  				{"book_id", "query", false},
  3536  			},
  3537  		},
  3538  	}
  3539  
  3540  	for _, tt := range tests {
  3541  		tt := tt
  3542  		t.Run(tt.name, func(t *testing.T) {
  3543  			reg := descriptor.NewRegistry()
  3544  			if err := AddErrorDefs(reg); err != nil {
  3545  				t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  3546  				return
  3547  			}
  3548  			err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{tt.args.file.FileDescriptorProto}})
  3549  			if err != nil {
  3550  				t.Errorf("Registry.Load() failed with %v; want success", err)
  3551  				return
  3552  			}
  3553  			result, err := applyTemplate(param{File: crossLinkFixture(&tt.args.file), reg: reg})
  3554  			if err != nil {
  3555  				t.Errorf("applyTemplate(%#v) failed with %v; want success", tt.args.file, err)
  3556  				return
  3557  			}
  3558  
  3559  			if _, ok := result.getPathItemObject("/v1/{parent}/books").Post.Responses["200"]; !ok {
  3560  				t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", tt.args.file, `result.getPathItemObject("/v1/{parent}/books").Post.Responses["200"]`)
  3561  			} else {
  3562  
  3563  				if want, got, name := 3, len(result.getPathItemObject("/v1/{parent}/books").Post.Parameters), `len(result.getPathItemObject("/v1/{parent}/books").Post.Parameters)`; !reflect.DeepEqual(got, want) {
  3564  					t.Errorf("applyTemplate(%#v).%s = %d want to be %d", tt.args.file, name, got, want)
  3565  				}
  3566  
  3567  				for i, want := range tt.want {
  3568  					p := result.getPathItemObject("/v1/{parent}/books").Post.Parameters[i]
  3569  					if got, name := (paramOut{p.Name, p.In, p.Required}), `result.getPathItemObject("/v1/{parent}/books").Post.Parameters[0]`; !reflect.DeepEqual(got, want) {
  3570  						t.Errorf("applyTemplate(%#v).%s = %v want to be %v", tt.args.file, name, got, want)
  3571  					}
  3572  				}
  3573  
  3574  			}
  3575  
  3576  			// If there was a failure, print out the input and the json result for debugging.
  3577  			if t.Failed() {
  3578  				t.Errorf("had: %s", tt.args.file)
  3579  				t.Errorf("got: %s", fmt.Sprint(result))
  3580  			}
  3581  		})
  3582  	}
  3583  
  3584  }
  3585  
  3586  func TestApplyTemplateWithRequestAndBodyParameters(t *testing.T) {
  3587  	bookDesc := &descriptorpb.DescriptorProto{
  3588  		Name: proto.String("Book"),
  3589  		Field: []*descriptorpb.FieldDescriptorProto{
  3590  			{
  3591  				Name:   proto.String("name"),
  3592  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3593  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3594  				Number: proto.Int32(1),
  3595  			},
  3596  			{
  3597  				Name:   proto.String("id"),
  3598  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3599  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3600  				Number: proto.Int32(2),
  3601  			},
  3602  		},
  3603  	}
  3604  	createDesc := &descriptorpb.DescriptorProto{
  3605  		Name: proto.String("CreateBookRequest"),
  3606  		Field: []*descriptorpb.FieldDescriptorProto{
  3607  			{
  3608  				Name:   proto.String("parent"),
  3609  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3610  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3611  				Number: proto.Int32(1),
  3612  			},
  3613  			{
  3614  				Name:   proto.String("book"),
  3615  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3616  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3617  				Number: proto.Int32(2),
  3618  			},
  3619  			{
  3620  				Name:   proto.String("book_id"),
  3621  				Label:  descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  3622  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3623  				Number: proto.Int32(3),
  3624  			},
  3625  		},
  3626  	}
  3627  	meth := &descriptorpb.MethodDescriptorProto{
  3628  		Name:       proto.String("CreateBook"),
  3629  		InputType:  proto.String("CreateBookRequest"),
  3630  		OutputType: proto.String("Book"),
  3631  	}
  3632  	svc := &descriptorpb.ServiceDescriptorProto{
  3633  		Name:   proto.String("BookService"),
  3634  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  3635  	}
  3636  
  3637  	bookMsg := &descriptor.Message{
  3638  		DescriptorProto: bookDesc,
  3639  	}
  3640  	createMsg := &descriptor.Message{
  3641  		DescriptorProto: createDesc,
  3642  	}
  3643  
  3644  	parentField := &descriptor.Field{
  3645  		Message:              createMsg,
  3646  		FieldDescriptorProto: createMsg.GetField()[0],
  3647  	}
  3648  	bookField := &descriptor.Field{
  3649  		Message:              createMsg,
  3650  		FieldMessage:         bookMsg,
  3651  		FieldDescriptorProto: createMsg.GetField()[1],
  3652  	}
  3653  	bookIDField := &descriptor.Field{
  3654  		Message:              createMsg,
  3655  		FieldDescriptorProto: createMsg.GetField()[2],
  3656  	}
  3657  
  3658  	createMsg.Fields = []*descriptor.Field{parentField, bookField, bookIDField}
  3659  
  3660  	file := descriptor.File{
  3661  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  3662  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  3663  			Name:           proto.String("book.proto"),
  3664  			MessageType:    []*descriptorpb.DescriptorProto{bookDesc, createDesc},
  3665  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  3666  			Options: &descriptorpb.FileOptions{
  3667  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  3668  			},
  3669  		},
  3670  		GoPkg: descriptor.GoPackage{
  3671  			Path: "example.com/path/to/book.pb",
  3672  			Name: "book_pb",
  3673  		},
  3674  		Messages: []*descriptor.Message{bookMsg, createMsg},
  3675  		Services: []*descriptor.Service{
  3676  			{
  3677  				ServiceDescriptorProto: svc,
  3678  				Methods: []*descriptor.Method{
  3679  					{
  3680  						MethodDescriptorProto: meth,
  3681  						RequestType:           createMsg,
  3682  						ResponseType:          bookMsg,
  3683  						Bindings: []*descriptor.Binding{
  3684  							{
  3685  								HTTPMethod: "POST",
  3686  								PathTmpl: httprule.Template{
  3687  									Version:  1,
  3688  									OpCodes:  []int{0, 0},
  3689  									Template: "/v1/{parent=publishers/*}/books",
  3690  								},
  3691  								PathParams: []descriptor.Parameter{
  3692  									{
  3693  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  3694  											{
  3695  												Name:   "parent",
  3696  												Target: parentField,
  3697  											},
  3698  										}),
  3699  										Target: parentField,
  3700  									},
  3701  								},
  3702  								Body: &descriptor.Body{
  3703  									FieldPath: []descriptor.FieldPathComponent{},
  3704  								},
  3705  							},
  3706  						},
  3707  					},
  3708  				},
  3709  			},
  3710  		},
  3711  	}
  3712  
  3713  	reg := descriptor.NewRegistry()
  3714  	if err := AddErrorDefs(reg); err != nil {
  3715  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  3716  		return
  3717  	}
  3718  	fileCL := crossLinkFixture(&file)
  3719  	err := reg.Load(reqFromFile(fileCL))
  3720  	if err != nil {
  3721  		t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  3722  		return
  3723  	}
  3724  	result, err := applyTemplate(param{File: fileCL, reg: reg})
  3725  	if err != nil {
  3726  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  3727  		return
  3728  	}
  3729  	if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
  3730  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  3731  	}
  3732  	if want, is, name := "", result.BasePath, "BasePath"; !reflect.DeepEqual(is, want) {
  3733  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  3734  	}
  3735  	if want, is, name := ([]string)(nil), result.Schemes, "Schemes"; !reflect.DeepEqual(is, want) {
  3736  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  3737  	}
  3738  	if want, is, name := []string{"application/json"}, result.Consumes, "Consumes"; !reflect.DeepEqual(is, want) {
  3739  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  3740  	}
  3741  	if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
  3742  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  3743  	}
  3744  
  3745  	if want, is, name := 1, len(result.Paths), "len(result.Paths)"; !reflect.DeepEqual(is, want) {
  3746  		t.Errorf("%s = %d want to be %d", name, want, is)
  3747  	}
  3748  	if want, is, name := 4, len(result.Paths[0].PathItemObject.Post.Parameters), "len(result.Paths[0].PathItemObject.Post.Parameters)"; !reflect.DeepEqual(is, want) {
  3749  		t.Errorf("%s = %d want to be %d", name, want, is)
  3750  	}
  3751  	if want, is, name := "#/definitions/BookServiceCreateBookBody", result.Paths[0].PathItemObject.Post.Parameters[1].Schema.schemaCore.Ref, "result.Paths[0].PathItemObject.Post.Parameters[1].Schema.schemaCore.Ref"; !reflect.DeepEqual(is, want) {
  3752  		t.Errorf("%s = %s want to be %s", name, want, is)
  3753  	}
  3754  
  3755  	_, found := result.Definitions["BookServiceCreateBookBody"]
  3756  	if !found {
  3757  		t.Error("expecting definition to contain BookServiceCreateBookBody")
  3758  	}
  3759  
  3760  	// If there was a failure, print out the input and the json result for debugging.
  3761  	if t.Failed() {
  3762  		t.Errorf("had: %s", file)
  3763  		t.Errorf("got: %s", fmt.Sprint(result))
  3764  	}
  3765  }
  3766  
  3767  // TestApplyTemplateProtobufAny tests that the protobufAny definition is correctly rendered with the @type field and
  3768  // allowing additional properties.
  3769  func TestApplyTemplateProtobufAny(t *testing.T) {
  3770  	// checkProtobufAnyFormat verifies the only property should be @type and additional properties are allowed
  3771  	checkProtobufAnyFormat := func(t *testing.T, protobufAny openapiSchemaObject) {
  3772  		anyPropsJSON, err := protobufAny.Properties.MarshalJSON()
  3773  		if err != nil {
  3774  			t.Errorf("protobufAny.Properties.MarshalJSON(), got error = %v", err)
  3775  		}
  3776  		var anyPropsMap map[string]interface{}
  3777  		if err := json.Unmarshal(anyPropsJSON, &anyPropsMap); err != nil {
  3778  			t.Errorf("json.Unmarshal(), got error = %v", err)
  3779  		}
  3780  
  3781  		// @type should exist
  3782  		if _, ok := anyPropsMap["@type"]; !ok {
  3783  			t.Errorf("protobufAny.Properties missing key, \"@type\". got = %#v", anyPropsMap)
  3784  		}
  3785  
  3786  		// and @type should be the only property
  3787  		if len(anyPropsMap) > 1 {
  3788  			t.Errorf("len(protobufAny.Properties) = %v, want = %v", len(anyPropsMap), 1)
  3789  		}
  3790  
  3791  		// protobufAny should have additionalProperties allowed
  3792  		if protobufAny.AdditionalProperties == nil {
  3793  			t.Errorf("protobufAny.AdditionalProperties = nil, want not-nil")
  3794  		}
  3795  	}
  3796  
  3797  	type args struct {
  3798  		regConfig      func(registry *descriptor.Registry)
  3799  		msgContainsAny bool
  3800  	}
  3801  	tests := []struct {
  3802  		name               string
  3803  		args               args
  3804  		wantNumDefinitions int
  3805  	}{
  3806  		{
  3807  			// our proto schema doesn't directly use protobufAny, but it is implicitly used by rpcStatus being
  3808  			// automatically rendered
  3809  			name: "default_protobufAny_from_rpcStatus",
  3810  			args: args{
  3811  				msgContainsAny: false,
  3812  			},
  3813  			wantNumDefinitions: 4,
  3814  		},
  3815  		{
  3816  			// we have a protobufAny in a message, it should contain a ref inside the custom message
  3817  			name: "protobufAny_referenced_in_message",
  3818  			args: args{
  3819  				msgContainsAny: true,
  3820  			},
  3821  			wantNumDefinitions: 4,
  3822  		},
  3823  		{
  3824  			// we have a protobufAny in a message but with automatic rendering of rpcStatus disabled
  3825  			name: "protobufAny_referenced_in_message_with_default_errors_disabled",
  3826  			args: args{
  3827  				msgContainsAny: true,
  3828  				regConfig: func(reg *descriptor.Registry) {
  3829  					reg.SetDisableDefaultErrors(true)
  3830  				},
  3831  			},
  3832  			wantNumDefinitions: 3,
  3833  		},
  3834  		{
  3835  			// we have a protobufAny in a message but with automatic rendering of responses disabled
  3836  			name: "protobufAny_referenced_in_message_with_default_responses_disabled",
  3837  			args: args{
  3838  				msgContainsAny: true,
  3839  				regConfig: func(reg *descriptor.Registry) {
  3840  					reg.SetDisableDefaultResponses(true)
  3841  				},
  3842  			},
  3843  			wantNumDefinitions: 4,
  3844  		},
  3845  	}
  3846  
  3847  	for _, tt := range tests {
  3848  		t.Run(tt.name, func(t *testing.T) {
  3849  			reqdesc := &descriptorpb.DescriptorProto{
  3850  				Name: proto.String("ExampleMessage"),
  3851  				Field: []*descriptorpb.FieldDescriptorProto{
  3852  					{
  3853  						Name:   proto.String("name"),
  3854  						Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3855  						Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3856  						Number: proto.Int32(1),
  3857  					},
  3858  				},
  3859  			}
  3860  			respdesc := &descriptorpb.DescriptorProto{
  3861  				Name: proto.String("EmptyMessage"),
  3862  			}
  3863  			meth := &descriptorpb.MethodDescriptorProto{
  3864  				Name:            proto.String("Example"),
  3865  				InputType:       proto.String("ExampleMessage"),
  3866  				OutputType:      proto.String("EmptyMessage"),
  3867  				ClientStreaming: proto.Bool(false),
  3868  				ServerStreaming: proto.Bool(false),
  3869  			}
  3870  			svc := &descriptorpb.ServiceDescriptorProto{
  3871  				Name:   proto.String("ExampleService"),
  3872  				Method: []*descriptorpb.MethodDescriptorProto{meth},
  3873  			}
  3874  
  3875  			req := &descriptor.Message{
  3876  				DescriptorProto: reqdesc,
  3877  			}
  3878  			resp := &descriptor.Message{
  3879  				DescriptorProto: respdesc,
  3880  			}
  3881  			file := descriptor.File{
  3882  				FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  3883  					SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  3884  					Name:           proto.String("example.proto"),
  3885  					Package:        proto.String("example"),
  3886  					MessageType:    []*descriptorpb.DescriptorProto{reqdesc, respdesc},
  3887  					Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  3888  					Options: &descriptorpb.FileOptions{
  3889  						GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  3890  					},
  3891  				},
  3892  				GoPkg: descriptor.GoPackage{
  3893  					Path: "example.com/path/to/example/example.pb",
  3894  					Name: "example_pb",
  3895  				},
  3896  				Messages: []*descriptor.Message{req, resp},
  3897  				Services: []*descriptor.Service{
  3898  					{
  3899  						ServiceDescriptorProto: svc,
  3900  						Methods: []*descriptor.Method{
  3901  							{
  3902  								MethodDescriptorProto: meth,
  3903  								RequestType:           req,
  3904  								ResponseType:          resp,
  3905  							},
  3906  						},
  3907  					},
  3908  				},
  3909  			}
  3910  
  3911  			reg := descriptor.NewRegistry()
  3912  			reg.SetGenerateUnboundMethods(true)
  3913  
  3914  			if tt.args.regConfig != nil {
  3915  				tt.args.regConfig(reg)
  3916  			}
  3917  
  3918  			if err := AddErrorDefs(reg); err != nil {
  3919  				t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  3920  				return
  3921  			}
  3922  
  3923  			protoFiles := []*descriptorpb.FileDescriptorProto{
  3924  				file.FileDescriptorProto,
  3925  			}
  3926  
  3927  			if tt.args.msgContainsAny {
  3928  				// add an Any field to the request message
  3929  				reqdesc.Field = append(reqdesc.Field, &descriptorpb.FieldDescriptorProto{
  3930  					Name:     proto.String("any_value"),
  3931  					Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3932  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3933  					TypeName: proto.String(".google.protobuf.Any"),
  3934  					Number:   proto.Int32(2),
  3935  				})
  3936  
  3937  				// update the dependencies to import it
  3938  				file.Dependency = append(file.Dependency, "google/protobuf/any.proto")
  3939  
  3940  				anyDescriptorProto := protodesc.ToFileDescriptorProto((&anypb.Any{}).ProtoReflect().Descriptor().ParentFile())
  3941  				anyDescriptorProto.SourceCodeInfo = &descriptorpb.SourceCodeInfo{}
  3942  
  3943  				// prepend the anyDescriptorProto to the protoFiles slice so that the dependency can be resolved
  3944  				protoFiles = append(append(make([]*descriptorpb.FileDescriptorProto, 0, len(protoFiles)+1), anyDescriptorProto), protoFiles[0:]...)
  3945  			}
  3946  
  3947  			err := reg.Load(&pluginpb.CodeGeneratorRequest{
  3948  				ProtoFile:      protoFiles,
  3949  				FileToGenerate: []string{file.GetName()},
  3950  			})
  3951  			if err != nil {
  3952  				t.Fatalf("failed to load code generator request: %v", err)
  3953  			}
  3954  
  3955  			target, err := reg.LookupFile(file.GetName())
  3956  			if err != nil {
  3957  				t.Fatalf("failed to lookup file from reg: %v", err)
  3958  			}
  3959  			result, err := applyTemplate(param{File: crossLinkFixture(target), reg: reg})
  3960  			if err != nil {
  3961  				t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  3962  				return
  3963  			}
  3964  
  3965  			if want, got, name := tt.wantNumDefinitions, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
  3966  				t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  3967  			}
  3968  
  3969  			protobufAny, ok := result.Definitions["protobufAny"]
  3970  			if !ok {
  3971  				t.Error("expecting Definitions to contain protobufAny")
  3972  			}
  3973  
  3974  			checkProtobufAnyFormat(t, protobufAny)
  3975  
  3976  			// If there was a failure, print out the input and the json result for debugging.
  3977  			if t.Failed() {
  3978  				t.Errorf("had: %s", file)
  3979  				resultJSON, _ := json.Marshal(result)
  3980  				t.Errorf("got: %s", resultJSON)
  3981  			}
  3982  		})
  3983  	}
  3984  }
  3985  
  3986  func generateFieldsForJSONReservedName() []*descriptor.Field {
  3987  	fields := make([]*descriptor.Field, 0)
  3988  	fieldName := "json_name"
  3989  	fieldJSONName := "jsonNAME"
  3990  	fieldDescriptor := descriptorpb.FieldDescriptorProto{Name: &fieldName, JsonName: &fieldJSONName}
  3991  	field := &descriptor.Field{FieldDescriptorProto: &fieldDescriptor}
  3992  	return append(fields, field)
  3993  }
  3994  
  3995  func generateMsgsForJSONReservedName() []*descriptor.Message {
  3996  	result := make([]*descriptor.Message, 0)
  3997  	// The first message, its field is field_abc and its type is NewType
  3998  	// NewType field_abc
  3999  	fieldName := "field_abc"
  4000  	fieldJSONName := "fieldAbc"
  4001  	messageName1 := "message1"
  4002  	messageType := "pkg.a.NewType"
  4003  	pfd := descriptorpb.FieldDescriptorProto{Name: &fieldName, JsonName: &fieldJSONName, TypeName: &messageType}
  4004  	result = append(result,
  4005  		&descriptor.Message{
  4006  			DescriptorProto: &descriptorpb.DescriptorProto{
  4007  				Name: &messageName1, Field: []*descriptorpb.FieldDescriptorProto{&pfd},
  4008  			},
  4009  		})
  4010  	// The second message, its name is NewName, its type is string
  4011  	// message NewType {
  4012  	//    string field_newName [json_name = RESERVEDJSONNAME]
  4013  	// }
  4014  	messageName := "NewType"
  4015  	field := "field_newName"
  4016  	fieldJSONName2 := "RESERVEDJSONNAME"
  4017  	pfd2 := descriptorpb.FieldDescriptorProto{Name: &field, JsonName: &fieldJSONName2}
  4018  	result = append(result, &descriptor.Message{
  4019  		DescriptorProto: &descriptorpb.DescriptorProto{
  4020  			Name: &messageName, Field: []*descriptorpb.FieldDescriptorProto{&pfd2},
  4021  		},
  4022  	})
  4023  	return result
  4024  }
  4025  
  4026  func TestTemplateWithJsonCamelCase(t *testing.T) {
  4027  	var tests = []struct {
  4028  		input    string
  4029  		expected string
  4030  	}{
  4031  		{"/test/{test_id}", "/test/{testId}"},
  4032  		{"/test1/{test1_id}/test2/{test2_id}", "/test1/{test1Id}/test2/{test2Id}"},
  4033  		{"/test1/{test1_id}/{test2_id}", "/test1/{test1Id}/{test2Id}"},
  4034  		{"/test1/test2/{test1_id}/{test2_id}", "/test1/test2/{test1Id}/{test2Id}"},
  4035  		{"/test1/{test1_id1_id2}", "/test1/{test1Id1Id2}"},
  4036  		{"/test1/{test1_id1_id2}/test2/{test2_id3_id4}", "/test1/{test1Id1Id2}/test2/{test2Id3Id4}"},
  4037  		{"/test1/test2/{test1_id1_id2}/{test2_id3_id4}", "/test1/test2/{test1Id1Id2}/{test2Id3Id4}"},
  4038  		{"test/{a}", "test/{a}"},
  4039  		{"test/{ab}", "test/{ab}"},
  4040  		{"test/{a_a}", "test/{aA}"},
  4041  		{"test/{ab_c}", "test/{abC}"},
  4042  		{"test/{json_name}", "test/{jsonNAME}"},
  4043  		{"test/{field_abc.field_newName}", "test/{fieldAbc.RESERVEDJSONNAME}"},
  4044  	}
  4045  	reg := descriptor.NewRegistry()
  4046  	reg.SetUseJSONNamesForFields(true)
  4047  	for _, data := range tests {
  4048  		actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
  4049  		if data.expected != actual {
  4050  			t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  4051  		}
  4052  	}
  4053  }
  4054  
  4055  func TestTemplateWithoutJsonCamelCase(t *testing.T) {
  4056  	var tests = []struct {
  4057  		input    string
  4058  		expected string
  4059  	}{
  4060  		{"/test/{test_id}", "/test/{test_id}"},
  4061  		{"/test1/{test1_id}/test2/{test2_id}", "/test1/{test1_id}/test2/{test2_id}"},
  4062  		{"/test1/{test1_id}/{test2_id}", "/test1/{test1_id}/{test2_id}"},
  4063  		{"/test1/test2/{test1_id}/{test2_id}", "/test1/test2/{test1_id}/{test2_id}"},
  4064  		{"/test1/{test1_id1_id2}", "/test1/{test1_id1_id2}"},
  4065  		{"/test1/{test1_id1_id2}/test2/{test2_id3_id4}", "/test1/{test1_id1_id2}/test2/{test2_id3_id4}"},
  4066  		{"/test1/test2/{test1_id1_id2}/{test2_id3_id4}", "/test1/test2/{test1_id1_id2}/{test2_id3_id4}"},
  4067  		{"test/{a}", "test/{a}"},
  4068  		{"test/{ab}", "test/{ab}"},
  4069  		{"test/{a_a}", "test/{a_a}"},
  4070  		{"test/{json_name}", "test/{json_name}"},
  4071  		{"test/{field_abc.field_newName}", "test/{field_abc.field_newName}"},
  4072  	}
  4073  	reg := descriptor.NewRegistry()
  4074  	reg.SetUseJSONNamesForFields(false)
  4075  	for _, data := range tests {
  4076  		actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
  4077  		if data.expected != actual {
  4078  			t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  4079  		}
  4080  	}
  4081  }
  4082  
  4083  func TestTemplateToOpenAPIPath(t *testing.T) {
  4084  	var tests = []struct {
  4085  		input    string
  4086  		expected string
  4087  	}{
  4088  		{"/test", "/test"},
  4089  		{"/{test}", "/{test}"},
  4090  		{"/{test=prefix/*}", "/{test}"},
  4091  		{"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
  4092  		{"/{test1}/{test2}", "/{test1}/{test2}"},
  4093  		{"/{test1}/{test2}/", "/{test1}/{test2}/"},
  4094  		{"/{name=prefix/*}", "/{name}"},
  4095  		{"/{name=prefix1/*/prefix2/*}", "/{name}"},
  4096  		{"/{user.name=prefix/*}", "/{user.name}"},
  4097  		{"/{user.name=prefix1/*/prefix2/*}", "/{user.name}"},
  4098  		{"/{parent=prefix/*}/children", "/{parent}/children"},
  4099  		{"/{name=prefix/*}:customMethod", "/{name}:customMethod"},
  4100  		{"/{name=prefix1/*/prefix2/*}:customMethod", "/{name}:customMethod"},
  4101  		{"/{user.name=prefix/*}:customMethod", "/{user.name}:customMethod"},
  4102  		{"/{user.name=prefix1/*/prefix2/*}:customMethod", "/{user.name}:customMethod"},
  4103  		{"/{parent=prefix/*}/children:customMethod", "/{parent}/children:customMethod"},
  4104  	}
  4105  	reg := descriptor.NewRegistry()
  4106  	reg.SetUseJSONNamesForFields(false)
  4107  	for _, data := range tests {
  4108  		actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
  4109  		if data.expected != actual {
  4110  			t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  4111  		}
  4112  	}
  4113  	reg.SetUseJSONNamesForFields(true)
  4114  	for _, data := range tests {
  4115  		actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
  4116  		if data.expected != actual {
  4117  			t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  4118  		}
  4119  	}
  4120  }
  4121  
  4122  func BenchmarkTemplateToOpenAPIPath(b *testing.B) {
  4123  	const input = "/{user.name=prefix1/*/prefix2/*}:customMethod"
  4124  
  4125  	b.Run("with JSON names", func(b *testing.B) {
  4126  		reg := descriptor.NewRegistry()
  4127  		reg.SetUseJSONNamesForFields(false)
  4128  
  4129  		for i := 0; i < b.N; i++ {
  4130  			_ = templateToOpenAPIPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
  4131  		}
  4132  	})
  4133  
  4134  	b.Run("without JSON names", func(b *testing.B) {
  4135  		reg := descriptor.NewRegistry()
  4136  		reg.SetUseJSONNamesForFields(true)
  4137  
  4138  		for i := 0; i < b.N; i++ {
  4139  			_ = templateToOpenAPIPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
  4140  		}
  4141  	})
  4142  }
  4143  
  4144  func TestResolveFullyQualifiedNameToOpenAPIName(t *testing.T) {
  4145  	var tests = []struct {
  4146  		input          string
  4147  		output         string
  4148  		listOfFQMNs    []string
  4149  		namingStrategy string
  4150  	}{
  4151  		{
  4152  			".a.b.C",
  4153  			"C",
  4154  			[]string{
  4155  				".a.b.C",
  4156  			},
  4157  			"legacy",
  4158  		},
  4159  		{
  4160  			".a.b.C",
  4161  			"C",
  4162  			[]string{
  4163  				".a.b.C",
  4164  			},
  4165  			"simple",
  4166  		},
  4167  		{
  4168  			".a.b.C",
  4169  			"abC",
  4170  			[]string{
  4171  				".a.C",
  4172  				".a.b.C",
  4173  			},
  4174  			"legacy",
  4175  		},
  4176  		{
  4177  			".a.b.C",
  4178  			"b.C",
  4179  			[]string{
  4180  				".a.C",
  4181  				".a.b.C",
  4182  			},
  4183  			"simple",
  4184  		},
  4185  		{
  4186  			".a.b.C",
  4187  			"abC",
  4188  			[]string{
  4189  				".C",
  4190  				".a.C",
  4191  				".a.b.C",
  4192  			},
  4193  			"legacy",
  4194  		},
  4195  		{
  4196  			".a.b.C",
  4197  			"b.C",
  4198  			[]string{
  4199  				".C",
  4200  				".a.C",
  4201  				".a.b.C",
  4202  			},
  4203  			"simple",
  4204  		},
  4205  		{
  4206  			".a.b.C",
  4207  			"a.b.C",
  4208  			[]string{
  4209  				".C",
  4210  				".a.C",
  4211  				".a.b.C",
  4212  			},
  4213  			"fqn",
  4214  		},
  4215  	}
  4216  
  4217  	for _, data := range tests {
  4218  		names := resolveFullyQualifiedNameToOpenAPINames(data.listOfFQMNs, data.namingStrategy)
  4219  		output := names[data.input]
  4220  		if output != data.output {
  4221  			t.Errorf("Expected fullyQualifiedNameToOpenAPIName(%v, %s) to be %s but got %s",
  4222  				data.input, data.namingStrategy, data.output, output)
  4223  		}
  4224  	}
  4225  }
  4226  
  4227  func templateToOpenAPIPath(path string, reg *descriptor.Registry, fields []*descriptor.Field, msgs []*descriptor.Message, pathParamNames map[string]string) string {
  4228  	return partsToOpenAPIPath(templateToParts(path, reg, fields, msgs), pathParamNames)
  4229  }
  4230  
  4231  func templateToRegexpMap(path string, reg *descriptor.Registry, fields []*descriptor.Field, msgs []*descriptor.Message) map[string]string {
  4232  	return partsToRegexpMap(templateToParts(path, reg, fields, msgs))
  4233  }
  4234  
  4235  func TestFQMNToRegexpMap(t *testing.T) {
  4236  	var tests = []struct {
  4237  		input    string
  4238  		expected map[string]string
  4239  	}{
  4240  		{"/test", map[string]string{}},
  4241  		{"/{test}", map[string]string{}},
  4242  		{"/{test" + pathParamUniqueSuffixDeliminator + "1=prefix/*}", map[string]string{"test" + pathParamUniqueSuffixDeliminator + "1": "prefix/[^/]+"}},
  4243  		{"/{test=prefix/that/has/multiple/parts/to/it/**}", map[string]string{"test": "prefix/that/has/multiple/parts/to/it/.+"}},
  4244  		{"/{test1=organizations/*}/{test2=divisions/*}", map[string]string{
  4245  			"test1": "organizations/[^/]+",
  4246  			"test2": "divisions/[^/]+",
  4247  		}},
  4248  		{"/v1/{name=projects/*/topics/*}:delete", map[string]string{"name": "projects/[^/]+/topics/[^/]+"}},
  4249  	}
  4250  	reg := descriptor.NewRegistry()
  4251  	for _, data := range tests {
  4252  		actual := templateToRegexpMap(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  4253  		if !reflect.DeepEqual(data.expected, actual) {
  4254  			t.Errorf("Expected partsToRegexpMap(%v) = %v, actual: %v", data.input, data.expected, actual)
  4255  		}
  4256  	}
  4257  }
  4258  
  4259  func TestFQMNtoOpenAPIName(t *testing.T) {
  4260  	var tests = []struct {
  4261  		input    string
  4262  		expected string
  4263  	}{
  4264  		{"/test", "/test"},
  4265  		{"/{test}", "/{test}"},
  4266  		{"/{test=prefix/*}", "/{test}"},
  4267  		{"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
  4268  		{"/{test1}/{test2}", "/{test1}/{test2}"},
  4269  		{"/{test1}/{test2}/", "/{test1}/{test2}/"},
  4270  		{"/v1/{name=tests/*}/tests", "/v1/{name}/tests"},
  4271  	}
  4272  	reg := descriptor.NewRegistry()
  4273  	reg.SetUseJSONNamesForFields(false)
  4274  	for _, data := range tests {
  4275  		actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
  4276  		if data.expected != actual {
  4277  			t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  4278  		}
  4279  	}
  4280  	reg.SetUseJSONNamesForFields(true)
  4281  	for _, data := range tests {
  4282  		actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName(), make(map[string]string))
  4283  		if data.expected != actual {
  4284  			t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  4285  		}
  4286  	}
  4287  }
  4288  
  4289  func TestSchemaOfField(t *testing.T) {
  4290  	type test struct {
  4291  		field                 *descriptor.Field
  4292  		refs                  refMap
  4293  		expected              openapiSchemaObject
  4294  		openAPIOptions        *openapiconfig.OpenAPIOptions
  4295  		useJSONNamesForFields bool
  4296  	}
  4297  
  4298  	jsonSchema := &openapi_options.JSONSchema{
  4299  		Title:       "field title",
  4300  		Description: "field description",
  4301  	}
  4302  	jsonSchemaWithOptions := &openapi_options.JSONSchema{
  4303  		Title:            "field title",
  4304  		Description:      "field description",
  4305  		MultipleOf:       100,
  4306  		Maximum:          101,
  4307  		ExclusiveMaximum: true,
  4308  		Minimum:          1,
  4309  		ExclusiveMinimum: true,
  4310  		MaxLength:        10,
  4311  		MinLength:        3,
  4312  		Pattern:          "[a-z]+",
  4313  		MaxItems:         20,
  4314  		MinItems:         2,
  4315  		UniqueItems:      true,
  4316  		MaxProperties:    33,
  4317  		MinProperties:    22,
  4318  		Required:         []string{"req"},
  4319  		ReadOnly:         true,
  4320  	}
  4321  	jsonSchemaRequired := &openapi_options.JSONSchema{
  4322  		Required: []string{"required_via_json_schema"},
  4323  	}
  4324  	jsonSchemaWithFormat := &openapi_options.JSONSchema{
  4325  		Format: "uuid",
  4326  	}
  4327  
  4328  	var fieldOptions = new(descriptorpb.FieldOptions)
  4329  	proto.SetExtension(fieldOptions, openapi_options.E_Openapiv2Field, jsonSchema)
  4330  
  4331  	var requiredField = []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
  4332  	var requiredFieldOptions = new(descriptorpb.FieldOptions)
  4333  	proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, requiredField)
  4334  
  4335  	var outputOnlyField = []annotations.FieldBehavior{annotations.FieldBehavior_OUTPUT_ONLY}
  4336  	var outputOnlyOptions = new(descriptorpb.FieldOptions)
  4337  	proto.SetExtension(outputOnlyOptions, annotations.E_FieldBehavior, outputOnlyField)
  4338  
  4339  	tests := []test{
  4340  		{
  4341  			field: &descriptor.Field{
  4342  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4343  					Name: proto.String("primitive_field"),
  4344  					Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  4345  				},
  4346  			},
  4347  			refs: make(refMap),
  4348  			expected: openapiSchemaObject{
  4349  				schemaCore: schemaCore{
  4350  					Type: "string",
  4351  				},
  4352  			},
  4353  		},
  4354  		{
  4355  			field: &descriptor.Field{
  4356  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4357  					Name:  proto.String("repeated_primitive_field"),
  4358  					Type:  descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  4359  					Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  4360  				},
  4361  			},
  4362  			refs: make(refMap),
  4363  			expected: openapiSchemaObject{
  4364  				schemaCore: schemaCore{
  4365  					Type: "array",
  4366  					Items: &openapiItemsObject{
  4367  						schemaCore: schemaCore{
  4368  							Type: "string",
  4369  						},
  4370  					},
  4371  				},
  4372  			},
  4373  		},
  4374  		{
  4375  			field: &descriptor.Field{
  4376  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4377  					Name:     proto.String("empty_field"),
  4378  					TypeName: proto.String(".google.protobuf.Empty"),
  4379  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4380  				},
  4381  			},
  4382  			refs: make(refMap),
  4383  			expected: openapiSchemaObject{
  4384  				schemaCore: schemaCore{
  4385  					Type: "object",
  4386  				},
  4387  				Properties: &openapiSchemaObjectProperties{},
  4388  			},
  4389  		},
  4390  		{
  4391  			field: &descriptor.Field{
  4392  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4393  					Name:     proto.String("wrapped_field"),
  4394  					TypeName: proto.String(".google.protobuf.FieldMask"),
  4395  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4396  				},
  4397  			},
  4398  			refs: make(refMap),
  4399  			expected: openapiSchemaObject{
  4400  				schemaCore: schemaCore{
  4401  					Type: "string",
  4402  				},
  4403  			},
  4404  		},
  4405  		{
  4406  			field: &descriptor.Field{
  4407  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4408  					Name:     proto.String("wrapped_field"),
  4409  					TypeName: proto.String(".google.protobuf.Timestamp"),
  4410  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4411  				},
  4412  			},
  4413  			refs: make(refMap),
  4414  			expected: openapiSchemaObject{
  4415  				schemaCore: schemaCore{
  4416  					Type:   "string",
  4417  					Format: "date-time",
  4418  				},
  4419  			},
  4420  		},
  4421  		{
  4422  			field: &descriptor.Field{
  4423  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4424  					Name:     proto.String("wrapped_field"),
  4425  					TypeName: proto.String(".google.protobuf.Duration"),
  4426  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4427  				},
  4428  			},
  4429  			refs: make(refMap),
  4430  			expected: openapiSchemaObject{
  4431  				schemaCore: schemaCore{
  4432  					Type: "string",
  4433  				},
  4434  			},
  4435  		},
  4436  		{
  4437  			field: &descriptor.Field{
  4438  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4439  					Name:     proto.String("wrapped_field"),
  4440  					TypeName: proto.String(".google.protobuf.StringValue"),
  4441  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4442  				},
  4443  			},
  4444  			refs: make(refMap),
  4445  			expected: openapiSchemaObject{
  4446  				schemaCore: schemaCore{
  4447  					Type: "string",
  4448  				},
  4449  			},
  4450  		},
  4451  		{
  4452  			field: &descriptor.Field{
  4453  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4454  					Name:     proto.String("repeated_wrapped_field"),
  4455  					TypeName: proto.String(".google.protobuf.StringValue"),
  4456  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4457  					Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  4458  				},
  4459  			},
  4460  			refs: make(refMap),
  4461  			expected: openapiSchemaObject{
  4462  				schemaCore: schemaCore{
  4463  					Type: "array",
  4464  					Items: &openapiItemsObject{
  4465  						schemaCore: schemaCore{
  4466  							Type: "string",
  4467  						},
  4468  					},
  4469  				},
  4470  			},
  4471  		},
  4472  		{
  4473  			field: &descriptor.Field{
  4474  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4475  					Name:     proto.String("wrapped_field"),
  4476  					TypeName: proto.String(".google.protobuf.BytesValue"),
  4477  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4478  				},
  4479  			},
  4480  			refs: make(refMap),
  4481  			expected: openapiSchemaObject{
  4482  				schemaCore: schemaCore{
  4483  					Type:   "string",
  4484  					Format: "byte",
  4485  				},
  4486  			},
  4487  		},
  4488  		{
  4489  			field: &descriptor.Field{
  4490  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4491  					Name:     proto.String("wrapped_field"),
  4492  					TypeName: proto.String(".google.protobuf.Int32Value"),
  4493  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4494  				},
  4495  			},
  4496  			refs: make(refMap),
  4497  			expected: openapiSchemaObject{
  4498  				schemaCore: schemaCore{
  4499  					Type:   "integer",
  4500  					Format: "int32",
  4501  				},
  4502  			},
  4503  		},
  4504  		{
  4505  			field: &descriptor.Field{
  4506  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4507  					Name:     proto.String("wrapped_field"),
  4508  					TypeName: proto.String(".google.protobuf.UInt32Value"),
  4509  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4510  				},
  4511  			},
  4512  			refs: make(refMap),
  4513  			expected: openapiSchemaObject{
  4514  				schemaCore: schemaCore{
  4515  					Type:   "integer",
  4516  					Format: "int64",
  4517  				},
  4518  			},
  4519  		},
  4520  		{
  4521  			field: &descriptor.Field{
  4522  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4523  					Name:     proto.String("wrapped_field"),
  4524  					TypeName: proto.String(".google.protobuf.Int64Value"),
  4525  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4526  				},
  4527  			},
  4528  			refs: make(refMap),
  4529  			expected: openapiSchemaObject{
  4530  				schemaCore: schemaCore{
  4531  					Type:   "string",
  4532  					Format: "int64",
  4533  				},
  4534  			},
  4535  		},
  4536  		{
  4537  			field: &descriptor.Field{
  4538  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4539  					Name:     proto.String("wrapped_field"),
  4540  					TypeName: proto.String(".google.protobuf.UInt64Value"),
  4541  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4542  				},
  4543  			},
  4544  			refs: make(refMap),
  4545  			expected: openapiSchemaObject{
  4546  				schemaCore: schemaCore{
  4547  					Type:   "string",
  4548  					Format: "uint64",
  4549  				},
  4550  			},
  4551  		},
  4552  		{
  4553  			field: &descriptor.Field{
  4554  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4555  					Name:     proto.String("wrapped_field"),
  4556  					TypeName: proto.String(".google.protobuf.FloatValue"),
  4557  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4558  				},
  4559  			},
  4560  			refs: make(refMap),
  4561  			expected: openapiSchemaObject{
  4562  				schemaCore: schemaCore{
  4563  					Type:   "number",
  4564  					Format: "float",
  4565  				},
  4566  			},
  4567  		},
  4568  		{
  4569  			field: &descriptor.Field{
  4570  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4571  					Name:     proto.String("wrapped_field"),
  4572  					TypeName: proto.String(".google.protobuf.DoubleValue"),
  4573  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4574  				},
  4575  			},
  4576  			refs: make(refMap),
  4577  			expected: openapiSchemaObject{
  4578  				schemaCore: schemaCore{
  4579  					Type:   "number",
  4580  					Format: "double",
  4581  				},
  4582  			},
  4583  		},
  4584  		{
  4585  			field: &descriptor.Field{
  4586  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4587  					Name:     proto.String("wrapped_field"),
  4588  					TypeName: proto.String(".google.protobuf.BoolValue"),
  4589  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4590  				},
  4591  			},
  4592  			refs: make(refMap),
  4593  			expected: openapiSchemaObject{
  4594  				schemaCore: schemaCore{
  4595  					Type: "boolean",
  4596  				},
  4597  			},
  4598  		},
  4599  		{
  4600  			field: &descriptor.Field{
  4601  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4602  					Name:     proto.String("wrapped_field"),
  4603  					TypeName: proto.String(".google.protobuf.Struct"),
  4604  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4605  				},
  4606  			},
  4607  			refs: make(refMap),
  4608  			expected: openapiSchemaObject{
  4609  				schemaCore: schemaCore{
  4610  					Type: "object",
  4611  				},
  4612  			},
  4613  		},
  4614  		{
  4615  			field: &descriptor.Field{
  4616  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4617  					Name:     proto.String("wrapped_field"),
  4618  					TypeName: proto.String(".google.protobuf.Value"),
  4619  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4620  				},
  4621  			},
  4622  			refs: make(refMap),
  4623  			expected: openapiSchemaObject{
  4624  				schemaCore: schemaCore{},
  4625  			},
  4626  		},
  4627  		{
  4628  			field: &descriptor.Field{
  4629  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4630  					Name:     proto.String("wrapped_field"),
  4631  					TypeName: proto.String(".google.protobuf.ListValue"),
  4632  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4633  				},
  4634  			},
  4635  			refs: make(refMap),
  4636  			expected: openapiSchemaObject{
  4637  				schemaCore: schemaCore{
  4638  					Type: "array",
  4639  					Items: &openapiItemsObject{schemaCore: schemaCore{
  4640  						Type: "object",
  4641  					}},
  4642  				},
  4643  			},
  4644  		},
  4645  		{
  4646  			field: &descriptor.Field{
  4647  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4648  					Name:     proto.String("wrapped_field"),
  4649  					TypeName: proto.String(".google.protobuf.NullValue"),
  4650  					Type:     descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
  4651  				},
  4652  			},
  4653  			refs: make(refMap),
  4654  			expected: openapiSchemaObject{
  4655  				schemaCore: schemaCore{
  4656  					Type: "string",
  4657  				},
  4658  			},
  4659  		},
  4660  		{
  4661  			field: &descriptor.Field{
  4662  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4663  					Name:     proto.String("message_field"),
  4664  					TypeName: proto.String(".example.Message"),
  4665  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4666  				},
  4667  			},
  4668  			refs: refMap{".example.Message": struct{}{}},
  4669  			expected: openapiSchemaObject{
  4670  				schemaCore: schemaCore{
  4671  					Ref: "#/definitions/exampleMessage",
  4672  				},
  4673  			},
  4674  		},
  4675  		{
  4676  			field: &descriptor.Field{
  4677  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4678  					Name:     proto.String("map_field"),
  4679  					Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  4680  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4681  					TypeName: proto.String(".example.Message.MapFieldEntry"),
  4682  					Options:  fieldOptions,
  4683  				},
  4684  			},
  4685  			refs: make(refMap),
  4686  			expected: openapiSchemaObject{
  4687  				schemaCore: schemaCore{
  4688  					Type: "object",
  4689  				},
  4690  				AdditionalProperties: &openapiSchemaObject{
  4691  					schemaCore: schemaCore{Type: "string"},
  4692  				},
  4693  				Title:       "field title",
  4694  				Description: "field description",
  4695  			},
  4696  		},
  4697  		{
  4698  			field: &descriptor.Field{
  4699  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4700  					Name:    proto.String("array_field"),
  4701  					Label:   descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  4702  					Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  4703  					Options: fieldOptions,
  4704  				},
  4705  			},
  4706  			refs: make(refMap),
  4707  			expected: openapiSchemaObject{
  4708  				schemaCore: schemaCore{
  4709  					Type: "array",
  4710  					Items: &openapiItemsObject{schemaCore: schemaCore{
  4711  						Type: "string",
  4712  					}},
  4713  				},
  4714  				Title:       "field title",
  4715  				Description: "field description",
  4716  			},
  4717  		},
  4718  		{
  4719  			field: &descriptor.Field{
  4720  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4721  					Name:    proto.String("primitive_field"),
  4722  					Label:   descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  4723  					Type:    descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  4724  					Options: fieldOptions,
  4725  				},
  4726  			},
  4727  			refs: make(refMap),
  4728  			expected: openapiSchemaObject{
  4729  				schemaCore: schemaCore{
  4730  					Type:   "integer",
  4731  					Format: "int32",
  4732  				},
  4733  				Title:       "field title",
  4734  				Description: "field description",
  4735  			},
  4736  		},
  4737  		{
  4738  			field: &descriptor.Field{
  4739  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4740  					Name:     proto.String("message_field"),
  4741  					Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  4742  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4743  					TypeName: proto.String(".example.Empty"),
  4744  					Options:  fieldOptions,
  4745  				},
  4746  			},
  4747  			refs: refMap{".example.Empty": struct{}{}},
  4748  			expected: openapiSchemaObject{
  4749  				schemaCore: schemaCore{
  4750  					Ref: "#/definitions/exampleEmpty",
  4751  				},
  4752  				Title:       "field title",
  4753  				Description: "field description",
  4754  			},
  4755  		},
  4756  		{
  4757  			field: &descriptor.Field{
  4758  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4759  					Name:     proto.String("map_field"), // should be called map_field_option but it's not valid map field name
  4760  					Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  4761  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4762  					TypeName: proto.String(".example.Message.MapFieldEntry"),
  4763  				},
  4764  			},
  4765  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  4766  				Field: []*openapiconfig.OpenAPIFieldOption{
  4767  					{
  4768  						Field:  "example.Message.map_field",
  4769  						Option: jsonSchema,
  4770  					},
  4771  				},
  4772  			},
  4773  			refs: make(refMap),
  4774  			expected: openapiSchemaObject{
  4775  				schemaCore: schemaCore{
  4776  					Type: "object",
  4777  				},
  4778  				AdditionalProperties: &openapiSchemaObject{
  4779  					schemaCore: schemaCore{Type: "string"},
  4780  				},
  4781  				Title:       "field title",
  4782  				Description: "field description",
  4783  			},
  4784  		},
  4785  		{
  4786  			field: &descriptor.Field{
  4787  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4788  					Name:  proto.String("array_field_option"),
  4789  					Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  4790  					Type:  descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  4791  				},
  4792  			},
  4793  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  4794  				Field: []*openapiconfig.OpenAPIFieldOption{
  4795  					{
  4796  						Field:  "example.Message.array_field_option",
  4797  						Option: jsonSchema,
  4798  					},
  4799  				},
  4800  			},
  4801  			refs: make(refMap),
  4802  			expected: openapiSchemaObject{
  4803  				schemaCore: schemaCore{
  4804  					Type: "array",
  4805  					Items: &openapiItemsObject{schemaCore: schemaCore{
  4806  						Type: "string",
  4807  					}},
  4808  				},
  4809  				Title:       "field title",
  4810  				Description: "field description",
  4811  			},
  4812  		},
  4813  		{
  4814  			field: &descriptor.Field{
  4815  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4816  					Name:  proto.String("primitive_field_option"),
  4817  					Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  4818  					Type:  descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  4819  				},
  4820  			},
  4821  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  4822  				Field: []*openapiconfig.OpenAPIFieldOption{
  4823  					{
  4824  						Field:  "example.Message.primitive_field_option",
  4825  						Option: jsonSchema,
  4826  					},
  4827  				},
  4828  			},
  4829  			refs: make(refMap),
  4830  			expected: openapiSchemaObject{
  4831  				schemaCore: schemaCore{
  4832  					Type:   "integer",
  4833  					Format: "int32",
  4834  				},
  4835  				Title:       "field title",
  4836  				Description: "field description",
  4837  			},
  4838  		},
  4839  		{
  4840  			field: &descriptor.Field{
  4841  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4842  					Name:  proto.String("primitive_field_option"),
  4843  					Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  4844  					Type:  descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum().Enum(),
  4845  				},
  4846  			},
  4847  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  4848  				Field: []*openapiconfig.OpenAPIFieldOption{
  4849  					{
  4850  						Field: "example.Message.primitive_field_option",
  4851  						Option: &openapi_options.JSONSchema{
  4852  							Title:       "field title",
  4853  							Description: "field description",
  4854  							Format:      "uuid",
  4855  						},
  4856  					},
  4857  				},
  4858  			},
  4859  			refs: make(refMap),
  4860  			expected: openapiSchemaObject{
  4861  				schemaCore: schemaCore{
  4862  					Type:   "string",
  4863  					Format: "uuid",
  4864  				},
  4865  				Title:       "field title",
  4866  				Description: "field description",
  4867  			},
  4868  		},
  4869  		{
  4870  			field: &descriptor.Field{
  4871  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4872  					Name:     proto.String("message_field_option"),
  4873  					Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  4874  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4875  					TypeName: proto.String(".example.Empty"),
  4876  				},
  4877  			},
  4878  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  4879  				Field: []*openapiconfig.OpenAPIFieldOption{
  4880  					{
  4881  						Field:  "example.Message.message_field_option",
  4882  						Option: jsonSchema,
  4883  					},
  4884  				},
  4885  			},
  4886  			refs: refMap{".example.Empty": struct{}{}},
  4887  			expected: openapiSchemaObject{
  4888  				schemaCore: schemaCore{
  4889  					Ref: "#/definitions/exampleEmpty",
  4890  				},
  4891  				Title:       "field title",
  4892  				Description: "field description",
  4893  			},
  4894  		},
  4895  		{
  4896  			field: &descriptor.Field{
  4897  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4898  					Name:    proto.String("required_via_field_behavior_field"),
  4899  					Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  4900  					Options: requiredFieldOptions,
  4901  				},
  4902  			},
  4903  			refs: make(refMap),
  4904  			expected: openapiSchemaObject{
  4905  				schemaCore: schemaCore{
  4906  					Type: "string",
  4907  				},
  4908  				Required: []string{"required_via_field_behavior_field"},
  4909  			},
  4910  		},
  4911  		{
  4912  			field: &descriptor.Field{
  4913  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4914  					Name:    proto.String("readonly_via_field_behavior_field"),
  4915  					Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  4916  					Options: outputOnlyOptions,
  4917  				},
  4918  			},
  4919  			refs: make(refMap),
  4920  			expected: openapiSchemaObject{
  4921  				schemaCore: schemaCore{
  4922  					Type: "string",
  4923  				},
  4924  				ReadOnly: true,
  4925  			},
  4926  		},
  4927  		{
  4928  			field: &descriptor.Field{
  4929  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4930  					Name:     proto.String("required_message_field"),
  4931  					TypeName: proto.String(".example.Message"),
  4932  					Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  4933  					Options:  requiredFieldOptions,
  4934  				},
  4935  			},
  4936  			refs: refMap{".example.Message": struct{}{}},
  4937  			expected: openapiSchemaObject{
  4938  				schemaCore: schemaCore{
  4939  					Ref: "#/definitions/exampleMessage",
  4940  				},
  4941  				Required: []string{"required_message_field"},
  4942  			},
  4943  		},
  4944  		{
  4945  			field: &descriptor.Field{
  4946  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4947  					Name:  proto.String("array_field_option"),
  4948  					Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  4949  					Type:  descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  4950  				},
  4951  			},
  4952  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  4953  				Field: []*openapiconfig.OpenAPIFieldOption{
  4954  					{
  4955  						Field:  "example.Message.array_field_option",
  4956  						Option: jsonSchemaWithOptions,
  4957  					},
  4958  				},
  4959  			},
  4960  			refs: make(refMap),
  4961  			expected: openapiSchemaObject{
  4962  				schemaCore: schemaCore{
  4963  					Type: "array",
  4964  					Items: &openapiItemsObject{
  4965  						schemaCore: schemaCore{
  4966  							Type: "string",
  4967  						},
  4968  						MultipleOf:       100,
  4969  						Maximum:          101,
  4970  						ExclusiveMaximum: true,
  4971  						Minimum:          1,
  4972  						ExclusiveMinimum: true,
  4973  						MaxLength:        10,
  4974  						MinLength:        3,
  4975  						Pattern:          "[a-z]+",
  4976  						UniqueItems:      true,
  4977  						MaxProperties:    33,
  4978  						MinProperties:    22,
  4979  						Required:         []string{"req"},
  4980  						ReadOnly:         true,
  4981  					},
  4982  				},
  4983  				Title:       "field title",
  4984  				Description: "field description",
  4985  				MaxItems:    20,
  4986  				MinItems:    2,
  4987  			},
  4988  		},
  4989  		{
  4990  			field: &descriptor.Field{
  4991  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  4992  					Name:  proto.String("array_field_option"),
  4993  					Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  4994  					Type:  descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(),
  4995  				},
  4996  			},
  4997  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  4998  				Field: []*openapiconfig.OpenAPIFieldOption{
  4999  					{
  5000  						Field:  "example.Message.array_field_option",
  5001  						Option: jsonSchemaWithOptions,
  5002  					},
  5003  				},
  5004  			},
  5005  			refs: make(refMap),
  5006  			expected: openapiSchemaObject{
  5007  				schemaCore: schemaCore{
  5008  					Type: "array",
  5009  					Items: &openapiItemsObject{
  5010  						schemaCore: schemaCore{
  5011  							Type:   "string",
  5012  							Format: "int64",
  5013  						},
  5014  						MultipleOf:       100,
  5015  						Maximum:          101,
  5016  						ExclusiveMaximum: true,
  5017  						Minimum:          1,
  5018  						ExclusiveMinimum: true,
  5019  						MaxLength:        10,
  5020  						MinLength:        3,
  5021  						Pattern:          "[a-z]+",
  5022  						UniqueItems:      true,
  5023  						MaxProperties:    33,
  5024  						MinProperties:    22,
  5025  						Required:         []string{"req"},
  5026  						ReadOnly:         true,
  5027  					},
  5028  				},
  5029  				Title:       "field title",
  5030  				Description: "field description",
  5031  				MaxItems:    20,
  5032  				MinItems:    2,
  5033  			},
  5034  		},
  5035  		{
  5036  			field: &descriptor.Field{
  5037  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  5038  					Name:  proto.String("array_field_format"),
  5039  					Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  5040  					Type:  descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5041  				},
  5042  			},
  5043  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  5044  				Field: []*openapiconfig.OpenAPIFieldOption{
  5045  					{
  5046  						Field:  "example.Message.array_field_format",
  5047  						Option: jsonSchemaWithFormat,
  5048  					},
  5049  				},
  5050  			},
  5051  			refs: make(refMap),
  5052  			expected: openapiSchemaObject{
  5053  				schemaCore: schemaCore{
  5054  					Type: "array",
  5055  					Items: &openapiItemsObject{
  5056  						schemaCore: schemaCore{
  5057  							Type:   "string",
  5058  							Format: "uuid",
  5059  						},
  5060  					},
  5061  				},
  5062  			},
  5063  		},
  5064  		{
  5065  			field: &descriptor.Field{
  5066  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  5067  					Name:     proto.String("required_via_field_behavior_field_json_name"),
  5068  					Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5069  					JsonName: proto.String("required_field_custom_name"),
  5070  					Options:  requiredFieldOptions,
  5071  				},
  5072  			},
  5073  			refs: make(refMap),
  5074  			expected: openapiSchemaObject{
  5075  				schemaCore: schemaCore{
  5076  					Type: "string",
  5077  				},
  5078  				Required: []string{"required_field_custom_name"},
  5079  			},
  5080  			useJSONNamesForFields: true,
  5081  		},
  5082  		{
  5083  			field: &descriptor.Field{
  5084  				FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  5085  					Name:     proto.String("required_via_json_schema"),
  5086  					Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5087  					JsonName: proto.String("required_via_json_schema_json_name"),
  5088  				},
  5089  			},
  5090  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  5091  				Field: []*openapiconfig.OpenAPIFieldOption{
  5092  					{
  5093  						Field:  "example.Message.required_via_json_schema",
  5094  						Option: jsonSchemaRequired,
  5095  					},
  5096  				},
  5097  			},
  5098  			refs:                  make(refMap),
  5099  			useJSONNamesForFields: true,
  5100  			expected: openapiSchemaObject{
  5101  				schemaCore: schemaCore{
  5102  					Type: "string",
  5103  				},
  5104  				Required: []string{"required_via_json_schema_json_name"},
  5105  			},
  5106  		},
  5107  	}
  5108  	for _, test := range tests {
  5109  		reg := descriptor.NewRegistry()
  5110  		reg.SetUseJSONNamesForFields(test.useJSONNamesForFields)
  5111  
  5112  		req := &pluginpb.CodeGeneratorRequest{
  5113  			ProtoFile: []*descriptorpb.FileDescriptorProto{
  5114  				{
  5115  					Name:    proto.String("third_party/google.proto"),
  5116  					Package: proto.String("google.protobuf"),
  5117  					Options: &descriptorpb.FileOptions{
  5118  						GoPackage: proto.String("third_party/google"),
  5119  					},
  5120  					MessageType: []*descriptorpb.DescriptorProto{
  5121  						protodesc.ToDescriptorProto((&emptypb.Empty{}).ProtoReflect().Descriptor()),
  5122  						protodesc.ToDescriptorProto((&structpb.Struct{}).ProtoReflect().Descriptor()),
  5123  						protodesc.ToDescriptorProto((&structpb.Value{}).ProtoReflect().Descriptor()),
  5124  						protodesc.ToDescriptorProto((&structpb.ListValue{}).ProtoReflect().Descriptor()),
  5125  						protodesc.ToDescriptorProto((&field_mask.FieldMask{}).ProtoReflect().Descriptor()),
  5126  						protodesc.ToDescriptorProto((&timestamppb.Timestamp{}).ProtoReflect().Descriptor()),
  5127  						protodesc.ToDescriptorProto((&durationpb.Duration{}).ProtoReflect().Descriptor()),
  5128  						protodesc.ToDescriptorProto((&wrapperspb.StringValue{}).ProtoReflect().Descriptor()),
  5129  						protodesc.ToDescriptorProto((&wrapperspb.BytesValue{}).ProtoReflect().Descriptor()),
  5130  						protodesc.ToDescriptorProto((&wrapperspb.Int32Value{}).ProtoReflect().Descriptor()),
  5131  						protodesc.ToDescriptorProto((&wrapperspb.UInt32Value{}).ProtoReflect().Descriptor()),
  5132  						protodesc.ToDescriptorProto((&wrapperspb.Int64Value{}).ProtoReflect().Descriptor()),
  5133  						protodesc.ToDescriptorProto((&wrapperspb.UInt64Value{}).ProtoReflect().Descriptor()),
  5134  						protodesc.ToDescriptorProto((&wrapperspb.FloatValue{}).ProtoReflect().Descriptor()),
  5135  						protodesc.ToDescriptorProto((&wrapperspb.DoubleValue{}).ProtoReflect().Descriptor()),
  5136  						protodesc.ToDescriptorProto((&wrapperspb.BoolValue{}).ProtoReflect().Descriptor()),
  5137  					},
  5138  					EnumType: []*descriptorpb.EnumDescriptorProto{
  5139  						protodesc.ToEnumDescriptorProto(structpb.NullValue(0).Descriptor()),
  5140  					},
  5141  				},
  5142  				{
  5143  					SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  5144  					Name:           proto.String("example.proto"),
  5145  					Package:        proto.String("example"),
  5146  					Dependency:     []string{"third_party/google.proto"},
  5147  					Options: &descriptorpb.FileOptions{
  5148  						GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  5149  					},
  5150  					MessageType: []*descriptorpb.DescriptorProto{
  5151  						{
  5152  							Name: proto.String("Message"),
  5153  							Field: []*descriptorpb.FieldDescriptorProto{
  5154  								{
  5155  									Name:   proto.String("value"),
  5156  									Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5157  									Number: proto.Int32(1),
  5158  								},
  5159  								func() *descriptorpb.FieldDescriptorProto {
  5160  									fd := test.field.FieldDescriptorProto
  5161  									fd.Number = proto.Int32(2)
  5162  									return fd
  5163  								}(),
  5164  							},
  5165  							NestedType: []*descriptorpb.DescriptorProto{
  5166  								{
  5167  									Name:    proto.String("MapFieldEntry"),
  5168  									Options: &descriptorpb.MessageOptions{MapEntry: proto.Bool(true)},
  5169  									Field: []*descriptorpb.FieldDescriptorProto{
  5170  										{
  5171  											Name:   proto.String("key"),
  5172  											Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  5173  											Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5174  											Number: proto.Int32(1),
  5175  										},
  5176  										{
  5177  											Name:   proto.String("value"),
  5178  											Label:  descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  5179  											Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5180  											Number: proto.Int32(2),
  5181  										},
  5182  									},
  5183  								},
  5184  							},
  5185  						},
  5186  						{
  5187  							Name: proto.String("Empty"),
  5188  						},
  5189  					},
  5190  					EnumType: []*descriptorpb.EnumDescriptorProto{
  5191  						{
  5192  							Name: proto.String("MessageType"),
  5193  							Value: []*descriptorpb.EnumValueDescriptorProto{
  5194  								{
  5195  									Name:   proto.String("MESSAGE_TYPE_1"),
  5196  									Number: proto.Int32(0),
  5197  								},
  5198  							},
  5199  						},
  5200  					},
  5201  					Service: []*descriptorpb.ServiceDescriptorProto{},
  5202  				},
  5203  			},
  5204  		}
  5205  		err := reg.Load(req)
  5206  		if err != nil {
  5207  			t.Errorf("failed to reg.Load(req): %v", err)
  5208  		}
  5209  
  5210  		// set field's parent message pointer to message so field can resolve its FQFN
  5211  		test.field.Message = &descriptor.Message{
  5212  			DescriptorProto: req.ProtoFile[1].MessageType[0],
  5213  			File: &descriptor.File{
  5214  				FileDescriptorProto: req.ProtoFile[1],
  5215  			},
  5216  		}
  5217  
  5218  		if test.openAPIOptions != nil {
  5219  			if err := reg.RegisterOpenAPIOptions(test.openAPIOptions); err != nil {
  5220  				t.Fatalf("failed to register OpenAPI options: %s", err)
  5221  			}
  5222  		}
  5223  
  5224  		refs := make(refMap)
  5225  		actual := schemaOfField(test.field, reg, refs)
  5226  		expectedSchemaObject := test.expected
  5227  		if e, a := expectedSchemaObject, actual; !reflect.DeepEqual(a, e) {
  5228  			t.Errorf("Expected schemaOfField(%v) = \n%#+v, actual: \n%#+v", test.field, e, a)
  5229  		}
  5230  		if !reflect.DeepEqual(refs, test.refs) {
  5231  			t.Errorf("Expected schemaOfField(%v) to add refs %v, not %v", test.field, test.refs, refs)
  5232  		}
  5233  	}
  5234  }
  5235  
  5236  func TestRenderMessagesAsDefinition(t *testing.T) {
  5237  	jsonSchema := &openapi_options.JSONSchema{
  5238  		Title:       "field title",
  5239  		Description: "field description",
  5240  		Required:    []string{"aRequiredField"},
  5241  	}
  5242  
  5243  	var requiredField = new(descriptorpb.FieldOptions)
  5244  	proto.SetExtension(requiredField, openapi_options.E_Openapiv2Field, jsonSchema)
  5245  
  5246  	var fieldBehaviorRequired = []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
  5247  	var requiredFieldOptions = new(descriptorpb.FieldOptions)
  5248  	proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, fieldBehaviorRequired)
  5249  
  5250  	var fieldBehaviorOutputOnlyField = []annotations.FieldBehavior{annotations.FieldBehavior_OUTPUT_ONLY}
  5251  	var fieldBehaviorOutputOnlyOptions = new(descriptorpb.FieldOptions)
  5252  	proto.SetExtension(fieldBehaviorOutputOnlyOptions, annotations.E_FieldBehavior, fieldBehaviorOutputOnlyField)
  5253  
  5254  	var fieldVisibilityFieldInternal = &visibility.VisibilityRule{Restriction: "INTERNAL"}
  5255  	var fieldVisibilityInternalOption = new(descriptorpb.FieldOptions)
  5256  	proto.SetExtension(fieldVisibilityInternalOption, visibility.E_FieldVisibility, fieldVisibilityFieldInternal)
  5257  
  5258  	var fieldVisibilityFieldPreview = &visibility.VisibilityRule{Restriction: "INTERNAL,PREVIEW"}
  5259  	var fieldVisibilityPreviewOption = new(descriptorpb.FieldOptions)
  5260  	proto.SetExtension(fieldVisibilityPreviewOption, visibility.E_FieldVisibility, fieldVisibilityFieldPreview)
  5261  
  5262  	tests := []struct {
  5263  		descr                 string
  5264  		msgDescs              []*descriptorpb.DescriptorProto
  5265  		schema                map[string]*openapi_options.Schema // per-message schema to add
  5266  		defs                  openapiDefinitionsObject
  5267  		openAPIOptions        *openapiconfig.OpenAPIOptions
  5268  		pathParams            []descriptor.Parameter
  5269  		UseJSONNamesForFields bool
  5270  		UseAllOfForRefs       bool
  5271  	}{
  5272  		{
  5273  			descr: "no OpenAPI options",
  5274  			msgDescs: []*descriptorpb.DescriptorProto{
  5275  				{Name: proto.String("Message")},
  5276  			},
  5277  			schema: map[string]*openapi_options.Schema{},
  5278  			defs: map[string]openapiSchemaObject{
  5279  				"Message": {schemaCore: schemaCore{Type: "object"}},
  5280  			},
  5281  		},
  5282  		{
  5283  			descr: "example option",
  5284  			msgDescs: []*descriptorpb.DescriptorProto{
  5285  				{Name: proto.String("Message")},
  5286  			},
  5287  			schema: map[string]*openapi_options.Schema{
  5288  				"Message": {
  5289  					Example: `{"foo":"bar"}`,
  5290  				},
  5291  			},
  5292  			defs: map[string]openapiSchemaObject{
  5293  				"Message": {schemaCore: schemaCore{
  5294  					Type:    "object",
  5295  					Example: RawExample(`{"foo":"bar"}`),
  5296  				}},
  5297  			},
  5298  		},
  5299  		{
  5300  			descr: "example option with something non-json",
  5301  			msgDescs: []*descriptorpb.DescriptorProto{
  5302  				{Name: proto.String("Message")},
  5303  			},
  5304  			schema: map[string]*openapi_options.Schema{
  5305  				"Message": {
  5306  					Example: `XXXX anything goes XXXX`,
  5307  				},
  5308  			},
  5309  			defs: map[string]openapiSchemaObject{
  5310  				"Message": {schemaCore: schemaCore{
  5311  					Type:    "object",
  5312  					Example: RawExample(`XXXX anything goes XXXX`),
  5313  				}},
  5314  			},
  5315  		},
  5316  		{
  5317  			descr: "external docs option",
  5318  			msgDescs: []*descriptorpb.DescriptorProto{
  5319  				{Name: proto.String("Message")},
  5320  			},
  5321  			schema: map[string]*openapi_options.Schema{
  5322  				"Message": {
  5323  					ExternalDocs: &openapi_options.ExternalDocumentation{
  5324  						Description: "glorious docs",
  5325  						Url:         "https://nada",
  5326  					},
  5327  				},
  5328  			},
  5329  			defs: map[string]openapiSchemaObject{
  5330  				"Message": {
  5331  					schemaCore: schemaCore{
  5332  						Type: "object",
  5333  					},
  5334  					ExternalDocs: &openapiExternalDocumentationObject{
  5335  						Description: "glorious docs",
  5336  						URL:         "https://nada",
  5337  					},
  5338  				},
  5339  			},
  5340  		},
  5341  		{
  5342  			descr: "JSONSchema options",
  5343  			msgDescs: []*descriptorpb.DescriptorProto{
  5344  				{Name: proto.String("Message")},
  5345  			},
  5346  			schema: map[string]*openapi_options.Schema{
  5347  				"Message": {
  5348  					JsonSchema: &openapi_options.JSONSchema{
  5349  						Title:            "title",
  5350  						Description:      "desc",
  5351  						MultipleOf:       100,
  5352  						Maximum:          101,
  5353  						ExclusiveMaximum: true,
  5354  						Minimum:          1,
  5355  						ExclusiveMinimum: true,
  5356  						MaxLength:        10,
  5357  						MinLength:        3,
  5358  						Pattern:          "[a-z]+",
  5359  						MaxItems:         20,
  5360  						MinItems:         2,
  5361  						UniqueItems:      true,
  5362  						MaxProperties:    33,
  5363  						MinProperties:    22,
  5364  						Required:         []string{"req"},
  5365  						ReadOnly:         true,
  5366  					},
  5367  				},
  5368  			},
  5369  			defs: map[string]openapiSchemaObject{
  5370  				"Message": {
  5371  					schemaCore: schemaCore{
  5372  						Type: "object",
  5373  					},
  5374  					Title:            "title",
  5375  					Description:      "desc",
  5376  					MultipleOf:       100,
  5377  					Maximum:          101,
  5378  					ExclusiveMaximum: true,
  5379  					Minimum:          1,
  5380  					ExclusiveMinimum: true,
  5381  					MaxLength:        10,
  5382  					MinLength:        3,
  5383  					Pattern:          "[a-z]+",
  5384  					MaxItems:         20,
  5385  					MinItems:         2,
  5386  					UniqueItems:      true,
  5387  					MaxProperties:    33,
  5388  					MinProperties:    22,
  5389  					Required:         []string{"req"},
  5390  					ReadOnly:         true,
  5391  				},
  5392  			},
  5393  		},
  5394  		{
  5395  			descr: "JSONSchema options from registry",
  5396  			msgDescs: []*descriptorpb.DescriptorProto{
  5397  				{Name: proto.String("Message")},
  5398  			},
  5399  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  5400  				Message: []*openapiconfig.OpenAPIMessageOption{
  5401  					{
  5402  						Message: "example.Message",
  5403  						Option: &openapi_options.Schema{
  5404  							JsonSchema: &openapi_options.JSONSchema{
  5405  								Title:            "title",
  5406  								Description:      "desc",
  5407  								MultipleOf:       100,
  5408  								Maximum:          101,
  5409  								ExclusiveMaximum: true,
  5410  								Minimum:          1,
  5411  								ExclusiveMinimum: true,
  5412  								MaxLength:        10,
  5413  								MinLength:        3,
  5414  								Pattern:          "[a-z]+",
  5415  								MaxItems:         20,
  5416  								MinItems:         2,
  5417  								UniqueItems:      true,
  5418  								MaxProperties:    33,
  5419  								MinProperties:    22,
  5420  								Required:         []string{"req"},
  5421  								ReadOnly:         true,
  5422  							},
  5423  						},
  5424  					},
  5425  				},
  5426  			},
  5427  			defs: map[string]openapiSchemaObject{
  5428  				"Message": {
  5429  					schemaCore: schemaCore{
  5430  						Type: "object",
  5431  					},
  5432  					Title:            "title",
  5433  					Description:      "desc",
  5434  					MultipleOf:       100,
  5435  					Maximum:          101,
  5436  					ExclusiveMaximum: true,
  5437  					Minimum:          1,
  5438  					ExclusiveMinimum: true,
  5439  					MaxLength:        10,
  5440  					MinLength:        3,
  5441  					Pattern:          "[a-z]+",
  5442  					MaxItems:         20,
  5443  					MinItems:         2,
  5444  					UniqueItems:      true,
  5445  					MaxProperties:    33,
  5446  					MinProperties:    22,
  5447  					Required:         []string{"req"},
  5448  					ReadOnly:         true,
  5449  				},
  5450  			},
  5451  		},
  5452  		{
  5453  			descr: "JSONSchema with required properties",
  5454  			msgDescs: []*descriptorpb.DescriptorProto{
  5455  				{
  5456  					Name: proto.String("Message"),
  5457  					Field: []*descriptorpb.FieldDescriptorProto{
  5458  						{
  5459  							Name:   proto.String("FieldOne"),
  5460  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5461  							Number: proto.Int32(1),
  5462  						},
  5463  						{
  5464  							Name:    proto.String("FieldTwo"),
  5465  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5466  							Number:  proto.Int32(2),
  5467  							Options: requiredFieldOptions,
  5468  						},
  5469  						{
  5470  							Name:    proto.String("FieldThree"),
  5471  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5472  							Number:  proto.Int32(3),
  5473  							Options: requiredFieldOptions,
  5474  						},
  5475  					},
  5476  				},
  5477  			},
  5478  			schema: map[string]*openapi_options.Schema{
  5479  				"Message": {
  5480  					JsonSchema: &openapi_options.JSONSchema{
  5481  						Title:       "title",
  5482  						Description: "desc",
  5483  						Required:    []string{"FieldOne", "FieldTwo"},
  5484  					},
  5485  				},
  5486  			},
  5487  			defs: map[string]openapiSchemaObject{
  5488  				"Message": {
  5489  					schemaCore: schemaCore{
  5490  						Type: "object",
  5491  					},
  5492  					Title:       "title",
  5493  					Description: "desc",
  5494  					Required:    []string{"FieldOne", "FieldTwo", "FieldThree"},
  5495  					Properties: &openapiSchemaObjectProperties{
  5496  						{
  5497  							Key: "FieldOne",
  5498  							Value: openapiSchemaObject{
  5499  								schemaCore: schemaCore{
  5500  									Type: "string",
  5501  								},
  5502  							},
  5503  						},
  5504  						{
  5505  							Key: "FieldTwo",
  5506  							Value: openapiSchemaObject{
  5507  								schemaCore: schemaCore{
  5508  									Type: "string",
  5509  								},
  5510  							},
  5511  						},
  5512  						{
  5513  							Key: "FieldThree",
  5514  							Value: openapiSchemaObject{
  5515  								schemaCore: schemaCore{
  5516  									Type: "string",
  5517  								},
  5518  							},
  5519  						},
  5520  					},
  5521  				},
  5522  			},
  5523  		},
  5524  		{
  5525  			descr: "JSONSchema with required properties",
  5526  			msgDescs: []*descriptorpb.DescriptorProto{
  5527  				{
  5528  					Name: proto.String("Message"),
  5529  					Field: []*descriptorpb.FieldDescriptorProto{
  5530  						{
  5531  							Name:    proto.String("FieldOne"),
  5532  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5533  							Number:  proto.Int32(3),
  5534  							Options: requiredFieldOptions,
  5535  						},
  5536  					},
  5537  				},
  5538  			},
  5539  			schema: map[string]*openapi_options.Schema{
  5540  				"Message": {
  5541  					JsonSchema: &openapi_options.JSONSchema{
  5542  						Title:       "title",
  5543  						Description: "desc",
  5544  					},
  5545  				},
  5546  			},
  5547  			defs: map[string]openapiSchemaObject{
  5548  				"Message": {
  5549  					schemaCore: schemaCore{
  5550  						Type: "object",
  5551  					},
  5552  					Title:       "title",
  5553  					Description: "desc",
  5554  					Required:    []string{"FieldOne"},
  5555  					Properties: &openapiSchemaObjectProperties{
  5556  						{
  5557  							Key: "FieldOne",
  5558  							Value: openapiSchemaObject{
  5559  								schemaCore: schemaCore{
  5560  									Type: "string",
  5561  								},
  5562  							},
  5563  						},
  5564  					},
  5565  				},
  5566  			},
  5567  		},
  5568  		{
  5569  			descr: "JSONSchema with required properties by using annotations",
  5570  			msgDescs: []*descriptorpb.DescriptorProto{
  5571  				{
  5572  					Name: proto.String("Message"),
  5573  					Field: []*descriptorpb.FieldDescriptorProto{
  5574  						{
  5575  							Name:    proto.String("FieldOne"),
  5576  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5577  							Number:  proto.Int32(2),
  5578  							Options: requiredFieldOptions,
  5579  						},
  5580  					},
  5581  				},
  5582  			},
  5583  			schema: map[string]*openapi_options.Schema{
  5584  				"Message": {
  5585  					JsonSchema: &openapi_options.JSONSchema{
  5586  						Title:       "title",
  5587  						Description: "desc",
  5588  					},
  5589  				},
  5590  			},
  5591  			defs: map[string]openapiSchemaObject{
  5592  				"Message": {
  5593  					schemaCore: schemaCore{
  5594  						Type: "object",
  5595  					},
  5596  					Title:       "title",
  5597  					Description: "desc",
  5598  					Required:    []string{"FieldOne"},
  5599  					Properties: &openapiSchemaObjectProperties{
  5600  						{
  5601  							Key: "FieldOne",
  5602  							Value: openapiSchemaObject{
  5603  								schemaCore: schemaCore{
  5604  									Type: "string",
  5605  								},
  5606  							},
  5607  						},
  5608  					},
  5609  				},
  5610  			},
  5611  		},
  5612  		{
  5613  			descr: "JSONSchema with hidden properties",
  5614  			msgDescs: []*descriptorpb.DescriptorProto{
  5615  				{
  5616  					Name: proto.String("Message"),
  5617  					Field: []*descriptorpb.FieldDescriptorProto{
  5618  						{
  5619  							Name:    proto.String("aInternalField"),
  5620  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5621  							Number:  proto.Int32(1),
  5622  							Options: fieldVisibilityInternalOption,
  5623  						},
  5624  						{
  5625  							Name:    proto.String("aPreviewField"),
  5626  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5627  							Number:  proto.Int32(2),
  5628  							Options: fieldVisibilityPreviewOption,
  5629  						},
  5630  						{
  5631  							Name:   proto.String("aVisibleField"),
  5632  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5633  							Number: proto.Int32(3),
  5634  						},
  5635  					},
  5636  				},
  5637  			},
  5638  			schema: map[string]*openapi_options.Schema{
  5639  				"Message": {
  5640  					JsonSchema: &openapi_options.JSONSchema{
  5641  						Title:       "title",
  5642  						Description: "desc",
  5643  						Required:    []string{"req"},
  5644  					},
  5645  				},
  5646  			},
  5647  			defs: map[string]openapiSchemaObject{
  5648  				"Message": {
  5649  					schemaCore: schemaCore{
  5650  						Type: "object",
  5651  					},
  5652  					Title:       "title",
  5653  					Description: "desc",
  5654  					Required:    []string{"req"},
  5655  					Properties: &openapiSchemaObjectProperties{
  5656  						{
  5657  							Key: "aPreviewField",
  5658  							Value: openapiSchemaObject{
  5659  								schemaCore: schemaCore{
  5660  									Type: "string",
  5661  								},
  5662  							},
  5663  						},
  5664  						{
  5665  							Key: "aVisibleField",
  5666  							Value: openapiSchemaObject{
  5667  								schemaCore: schemaCore{
  5668  									Type: "string",
  5669  								},
  5670  							},
  5671  						},
  5672  					},
  5673  				},
  5674  			},
  5675  		},
  5676  		{
  5677  			descr: "JSONSchema with path parameters",
  5678  			msgDescs: []*descriptorpb.DescriptorProto{
  5679  				{
  5680  					Name: proto.String("Message"),
  5681  					Field: []*descriptorpb.FieldDescriptorProto{
  5682  						{
  5683  							Name:    proto.String("aRequiredField"),
  5684  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5685  							Number:  proto.Int32(1),
  5686  							Options: requiredField,
  5687  						},
  5688  						{
  5689  							Name:   proto.String("aPathParameter"),
  5690  							Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5691  							Number: proto.Int32(2),
  5692  						},
  5693  					},
  5694  				},
  5695  			},
  5696  			schema: map[string]*openapi_options.Schema{
  5697  				"Message": {
  5698  					JsonSchema: &openapi_options.JSONSchema{
  5699  						Title:       "title",
  5700  						Description: "desc",
  5701  						Required:    []string{"req"},
  5702  					},
  5703  				},
  5704  			},
  5705  			defs: map[string]openapiSchemaObject{
  5706  				"Message": {
  5707  					schemaCore: schemaCore{
  5708  						Type: "object",
  5709  					},
  5710  					Title:       "title",
  5711  					Description: "desc",
  5712  					Required:    []string{"req", "aRequiredField"},
  5713  					Properties: &openapiSchemaObjectProperties{
  5714  						{
  5715  							Key: "aRequiredField",
  5716  							Value: openapiSchemaObject{
  5717  								schemaCore: schemaCore{
  5718  									Type: "string",
  5719  								},
  5720  								Description: "field description",
  5721  								Title:       "field title",
  5722  							},
  5723  						},
  5724  					},
  5725  				},
  5726  			},
  5727  			pathParams: []descriptor.Parameter{
  5728  				{
  5729  					FieldPath: descriptor.FieldPath{
  5730  						descriptor.FieldPathComponent{
  5731  							Name: ("aPathParameter"),
  5732  						},
  5733  					},
  5734  				},
  5735  			},
  5736  		},
  5737  		{
  5738  			descr: "JSONSchema with required properties via field_behavior",
  5739  			msgDescs: []*descriptorpb.DescriptorProto{
  5740  				{
  5741  					Name: proto.String("Message"),
  5742  					Field: []*descriptorpb.FieldDescriptorProto{
  5743  						{
  5744  							Name:    proto.String("aRequiredField"),
  5745  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5746  							Number:  proto.Int32(1),
  5747  							Options: requiredFieldOptions,
  5748  						},
  5749  						{
  5750  							Name:    proto.String("aOutputOnlyField"),
  5751  							Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5752  							Number:  proto.Int32(2),
  5753  							Options: fieldBehaviorOutputOnlyOptions,
  5754  						},
  5755  					},
  5756  				},
  5757  			},
  5758  			schema: map[string]*openapi_options.Schema{
  5759  				"Message": {
  5760  					JsonSchema: &openapi_options.JSONSchema{
  5761  						Title:       "title",
  5762  						Description: "desc",
  5763  						Required:    []string{"req"},
  5764  					},
  5765  				},
  5766  			},
  5767  			defs: map[string]openapiSchemaObject{
  5768  				"Message": {
  5769  					schemaCore: schemaCore{
  5770  						Type: "object",
  5771  					},
  5772  					Title:       "title",
  5773  					Description: "desc",
  5774  					Required:    []string{"req", "aRequiredField"},
  5775  					Properties: &openapiSchemaObjectProperties{
  5776  						{
  5777  							Key: "aRequiredField",
  5778  							Value: openapiSchemaObject{
  5779  								schemaCore: schemaCore{
  5780  									Type: "string",
  5781  								},
  5782  							},
  5783  						},
  5784  						{
  5785  							Key: "aOutputOnlyField",
  5786  							Value: openapiSchemaObject{
  5787  								schemaCore: schemaCore{
  5788  									Type: "string",
  5789  								},
  5790  								ReadOnly: true,
  5791  							},
  5792  						},
  5793  					},
  5794  				},
  5795  			},
  5796  		},
  5797  		{
  5798  			descr: "JSONSchema with required properties and fields with json_name",
  5799  			msgDescs: []*descriptorpb.DescriptorProto{
  5800  				{
  5801  					Name: proto.String("Message"),
  5802  					Field: []*descriptorpb.FieldDescriptorProto{
  5803  						{
  5804  							Name:     proto.String("FieldOne"),
  5805  							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5806  							Number:   proto.Int32(1),
  5807  							JsonName: proto.String("custom_json_1"),
  5808  						},
  5809  						{
  5810  							Name:     proto.String("FieldTwo"),
  5811  							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5812  							Number:   proto.Int32(2),
  5813  							JsonName: proto.String("custom_json_2"),
  5814  							Options:  requiredFieldOptions,
  5815  						},
  5816  						{
  5817  							Name:     proto.String("FieldThree"),
  5818  							Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  5819  							Number:   proto.Int32(3),
  5820  							JsonName: proto.String("custom_json_3"),
  5821  							Options:  requiredFieldOptions,
  5822  						},
  5823  					},
  5824  				},
  5825  			},
  5826  			schema: map[string]*openapi_options.Schema{
  5827  				"Message": {
  5828  					JsonSchema: &openapi_options.JSONSchema{
  5829  						Title:       "title",
  5830  						Description: "desc",
  5831  						Required:    []string{"FieldOne", "FieldTwo"},
  5832  					},
  5833  				},
  5834  			},
  5835  			defs: map[string]openapiSchemaObject{
  5836  				"Message": {
  5837  					schemaCore: schemaCore{
  5838  						Type: "object",
  5839  					},
  5840  					Title:       "title",
  5841  					Description: "desc",
  5842  					Required:    []string{"custom_json_1", "custom_json_2", "custom_json_3"},
  5843  					Properties: &openapiSchemaObjectProperties{
  5844  						{
  5845  							Key: "custom_json_1",
  5846  							Value: openapiSchemaObject{
  5847  								schemaCore: schemaCore{
  5848  									Type: "string",
  5849  								},
  5850  							},
  5851  						},
  5852  						{
  5853  							Key: "custom_json_2",
  5854  							Value: openapiSchemaObject{
  5855  								schemaCore: schemaCore{
  5856  									Type: "string",
  5857  								},
  5858  							},
  5859  						},
  5860  						{
  5861  							Key: "custom_json_3",
  5862  							Value: openapiSchemaObject{
  5863  								schemaCore: schemaCore{
  5864  									Type: "string",
  5865  								},
  5866  							},
  5867  						},
  5868  					},
  5869  				},
  5870  			},
  5871  			UseJSONNamesForFields: true,
  5872  		},
  5873  		{
  5874  			descr: "JSONSchema with a read_only nested field",
  5875  			msgDescs: []*descriptorpb.DescriptorProto{
  5876  				{
  5877  					Name: proto.String("Message"),
  5878  					Field: []*descriptorpb.FieldDescriptorProto{
  5879  						{
  5880  							Name:     proto.String("nested"),
  5881  							Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  5882  							TypeName: proto.String(".example.Message.Nested"),
  5883  							Number:   proto.Int32(1),
  5884  							Options:  fieldBehaviorOutputOnlyOptions,
  5885  						},
  5886  					},
  5887  					NestedType: []*descriptorpb.DescriptorProto{{
  5888  						Name: proto.String("Nested"),
  5889  					}},
  5890  				},
  5891  			},
  5892  			UseAllOfForRefs: true,
  5893  			schema: map[string]*openapi_options.Schema{
  5894  				"Message": {
  5895  					JsonSchema: &openapi_options.JSONSchema{
  5896  						Title:       "title",
  5897  						Description: "desc",
  5898  						Required:    []string{},
  5899  					},
  5900  				},
  5901  			},
  5902  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  5903  				Field: []*openapiconfig.OpenAPIFieldOption{
  5904  					{
  5905  						Field: "example.Message.nested",
  5906  						Option: &openapi_options.JSONSchema{
  5907  							Title:       "nested field title",
  5908  							Description: "nested field desc",
  5909  							Example:     `"ok":"TRUE"`,
  5910  						},
  5911  					},
  5912  				},
  5913  			},
  5914  			defs: map[string]openapiSchemaObject{
  5915  				"exampleMessage": {
  5916  					schemaCore: schemaCore{
  5917  						Type: "object",
  5918  					},
  5919  					Title:       "title",
  5920  					Description: "desc",
  5921  					Required:    nil,
  5922  					Properties: &openapiSchemaObjectProperties{
  5923  						{
  5924  							Key: "nested",
  5925  							Value: openapiSchemaObject{
  5926  								AllOf:    []allOfEntry{{Ref: "#/definitions/MessageNested"}},
  5927  								ReadOnly: true,
  5928  								schemaCore: schemaCore{
  5929  									Example: RawExample(`"ok":"TRUE"`),
  5930  								},
  5931  								Title:       "nested field title",
  5932  								Description: "nested field desc",
  5933  							},
  5934  						},
  5935  					},
  5936  				},
  5937  			},
  5938  		},
  5939  	}
  5940  
  5941  	for _, test := range tests {
  5942  		t.Run(test.descr, func(t *testing.T) {
  5943  
  5944  			msgs := []*descriptor.Message{}
  5945  			for _, msgdesc := range test.msgDescs {
  5946  				msgdesc.Options = &descriptorpb.MessageOptions{}
  5947  				msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  5948  			}
  5949  
  5950  			reg := descriptor.NewRegistry()
  5951  			file := descriptor.File{
  5952  				FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  5953  					SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  5954  					Name:           proto.String("example.proto"),
  5955  					Package:        proto.String("example"),
  5956  					Dependency:     []string{},
  5957  					MessageType:    test.msgDescs,
  5958  					EnumType:       []*descriptorpb.EnumDescriptorProto{},
  5959  					Service:        []*descriptorpb.ServiceDescriptorProto{},
  5960  					Options: &descriptorpb.FileOptions{
  5961  						GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  5962  					},
  5963  				},
  5964  				Messages: msgs,
  5965  			}
  5966  			err := reg.Load(&pluginpb.CodeGeneratorRequest{
  5967  				ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  5968  			})
  5969  			reg.SetVisibilityRestrictionSelectors([]string{"PREVIEW"})
  5970  
  5971  			if test.UseJSONNamesForFields {
  5972  				reg.SetUseJSONNamesForFields(true)
  5973  			}
  5974  
  5975  			if test.UseAllOfForRefs {
  5976  				reg.SetUseAllOfForRefs(true)
  5977  			}
  5978  
  5979  			if err != nil {
  5980  				t.Fatalf("failed to load code generator request: %v", err)
  5981  			}
  5982  
  5983  			msgMap := map[string]*descriptor.Message{}
  5984  			for _, d := range test.msgDescs {
  5985  				name := d.GetName()
  5986  				msg, err := reg.LookupMsg("example", name)
  5987  				if err != nil {
  5988  					t.Fatalf("lookup message %v: %v", name, err)
  5989  				}
  5990  				msgMap[msg.FQMN()] = msg
  5991  
  5992  				if schema, ok := test.schema[name]; ok {
  5993  					proto.SetExtension(d.Options, openapi_options.E_Openapiv2Schema, schema)
  5994  				}
  5995  			}
  5996  
  5997  			if test.openAPIOptions != nil {
  5998  				if err := reg.RegisterOpenAPIOptions(test.openAPIOptions); err != nil {
  5999  					t.Fatalf("failed to register OpenAPI options: %s", err)
  6000  				}
  6001  			}
  6002  
  6003  			refs := make(refMap)
  6004  			actual := make(openapiDefinitionsObject)
  6005  			if err := renderMessagesAsDefinition(msgMap, actual, reg, refs, test.pathParams); err != nil {
  6006  				t.Errorf("renderMessagesAsDefinition failed with: %s", err)
  6007  			}
  6008  
  6009  			if !reflect.DeepEqual(actual, test.defs) {
  6010  				t.Errorf("Expected renderMessagesAsDefinition() to add defs %+v, not %+v", test.defs, actual)
  6011  			}
  6012  		})
  6013  	}
  6014  }
  6015  
  6016  func TestUpdateOpenAPIDataFromComments(t *testing.T) {
  6017  
  6018  	tests := []struct {
  6019  		descr                 string
  6020  		openapiSwaggerObject  interface{}
  6021  		comments              string
  6022  		expectedError         error
  6023  		expectedOpenAPIObject interface{}
  6024  		useGoTemplate         bool
  6025  		goTemplateArgs        []string
  6026  	}{
  6027  		{
  6028  			descr:                 "empty comments",
  6029  			openapiSwaggerObject:  nil,
  6030  			expectedOpenAPIObject: nil,
  6031  			comments:              "",
  6032  			expectedError:         nil,
  6033  		},
  6034  		{
  6035  			descr:                "set field to read only",
  6036  			openapiSwaggerObject: &openapiSchemaObject{},
  6037  			expectedOpenAPIObject: &openapiSchemaObject{
  6038  				ReadOnly:    true,
  6039  				Description: "... Output only. ...",
  6040  			},
  6041  			comments:      "... Output only. ...",
  6042  			expectedError: nil,
  6043  		},
  6044  		{
  6045  			descr:                "set title",
  6046  			openapiSwaggerObject: &openapiSchemaObject{},
  6047  			expectedOpenAPIObject: &openapiSchemaObject{
  6048  				Title: "Comment with no trailing dot",
  6049  			},
  6050  			comments:      "Comment with no trailing dot",
  6051  			expectedError: nil,
  6052  		},
  6053  		{
  6054  			descr:                "set description",
  6055  			openapiSwaggerObject: &openapiSchemaObject{},
  6056  			expectedOpenAPIObject: &openapiSchemaObject{
  6057  				Description: "Comment with trailing dot.",
  6058  			},
  6059  			comments:      "Comment with trailing dot.",
  6060  			expectedError: nil,
  6061  		},
  6062  		{
  6063  			descr: "use info object",
  6064  			openapiSwaggerObject: &openapiSwaggerObject{
  6065  				Info: openapiInfoObject{},
  6066  			},
  6067  			expectedOpenAPIObject: &openapiSwaggerObject{
  6068  				Info: openapiInfoObject{
  6069  					Description: "Comment with trailing dot.",
  6070  				},
  6071  			},
  6072  			comments:      "Comment with trailing dot.",
  6073  			expectedError: nil,
  6074  		},
  6075  		{
  6076  			descr:                "multi line comment with title",
  6077  			openapiSwaggerObject: &openapiSchemaObject{},
  6078  			expectedOpenAPIObject: &openapiSchemaObject{
  6079  				Title:       "First line",
  6080  				Description: "Second line",
  6081  			},
  6082  			comments:      "First line\n\nSecond line",
  6083  			expectedError: nil,
  6084  		},
  6085  		{
  6086  			descr:                "multi line comment no title",
  6087  			openapiSwaggerObject: &openapiSchemaObject{},
  6088  			expectedOpenAPIObject: &openapiSchemaObject{
  6089  				Description: "First line.\n\nSecond line",
  6090  			},
  6091  			comments:      "First line.\n\nSecond line",
  6092  			expectedError: nil,
  6093  		},
  6094  		{
  6095  			descr:                "multi line comment with summary with dot",
  6096  			openapiSwaggerObject: &openapiOperationObject{},
  6097  			expectedOpenAPIObject: &openapiOperationObject{
  6098  				Summary:     "First line.",
  6099  				Description: "Second line",
  6100  			},
  6101  			comments:      "First line.\n\nSecond line",
  6102  			expectedError: nil,
  6103  		},
  6104  		{
  6105  			descr:                "multi line comment with summary no dot",
  6106  			openapiSwaggerObject: &openapiOperationObject{},
  6107  			expectedOpenAPIObject: &openapiOperationObject{
  6108  				Summary:     "First line",
  6109  				Description: "Second line",
  6110  			},
  6111  			comments:      "First line\n\nSecond line",
  6112  			expectedError: nil,
  6113  		},
  6114  		{
  6115  			descr:                 "multi line comment with summary no dot",
  6116  			openapiSwaggerObject:  &schemaCore{},
  6117  			expectedOpenAPIObject: &schemaCore{},
  6118  			comments:              "Any comment",
  6119  			expectedError:         errors.New("no description nor summary property"),
  6120  		},
  6121  		{
  6122  			descr:                "without use_go_template",
  6123  			openapiSwaggerObject: &openapiSchemaObject{},
  6124  			expectedOpenAPIObject: &openapiSchemaObject{
  6125  				Title:       "First line",
  6126  				Description: "{{import \"documentation.md\"}}",
  6127  			},
  6128  			comments:      "First line\n\n{{import \"documentation.md\"}}",
  6129  			expectedError: nil,
  6130  		},
  6131  		{
  6132  			descr:                "error with use_go_template",
  6133  			openapiSwaggerObject: &openapiSchemaObject{},
  6134  			expectedOpenAPIObject: &openapiSchemaObject{
  6135  				Title:       "First line",
  6136  				Description: "open noneexistingfile.txt: no such file or directory",
  6137  			},
  6138  			comments:      "First line\n\n{{import \"noneexistingfile.txt\"}}",
  6139  			expectedError: nil,
  6140  			useGoTemplate: true,
  6141  		},
  6142  		{
  6143  			descr:                "template with use_go_template",
  6144  			openapiSwaggerObject: &openapiSchemaObject{},
  6145  			expectedOpenAPIObject: &openapiSchemaObject{
  6146  				Title:       "Template",
  6147  				Description: `Description "which means nothing"`,
  6148  			},
  6149  			comments:      "Template\n\nDescription {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  6150  			expectedError: nil,
  6151  			useGoTemplate: true,
  6152  		},
  6153  		{
  6154  			descr:                "template with use_go_template and go_template_args",
  6155  			openapiSwaggerObject: &openapiSchemaObject{},
  6156  			expectedOpenAPIObject: &openapiSchemaObject{
  6157  				Title:       "Template",
  6158  				Description: `Description "which means nothing" for environment test with value my_value`,
  6159  			},
  6160  			comments: "Template\n\nDescription {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} for " +
  6161  				"environment {{arg \"environment\"}} with value {{arg \"my_key\"}}",
  6162  			expectedError:  nil,
  6163  			useGoTemplate:  true,
  6164  			goTemplateArgs: []string{"my_key=my_value", "environment=test"},
  6165  		},
  6166  		{
  6167  			descr:                "template with use_go_template and undefined go_template_args",
  6168  			openapiSwaggerObject: &openapiSchemaObject{},
  6169  			expectedOpenAPIObject: &openapiSchemaObject{
  6170  				Title: "Template",
  6171  				Description: `Description "which means nothing" for environment test with value ` +
  6172  					`goTemplateArg something_undefined not found`,
  6173  			},
  6174  			comments: "Template\n\nDescription {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} for " +
  6175  				"environment {{arg \"environment\"}} with value {{arg \"something_undefined\"}}",
  6176  			expectedError:  nil,
  6177  			useGoTemplate:  true,
  6178  			goTemplateArgs: []string{"environment=test"},
  6179  		},
  6180  	}
  6181  
  6182  	for _, test := range tests {
  6183  		t.Run(test.descr, func(t *testing.T) {
  6184  			reg := descriptor.NewRegistry()
  6185  			if test.useGoTemplate {
  6186  				reg.SetUseGoTemplate(true)
  6187  			}
  6188  			if len(test.goTemplateArgs) > 0 {
  6189  				reg.SetGoTemplateArgs(test.goTemplateArgs)
  6190  			}
  6191  			err := updateOpenAPIDataFromComments(reg, test.openapiSwaggerObject, nil, test.comments, false)
  6192  			if test.expectedError == nil {
  6193  				if err != nil {
  6194  					t.Errorf("unexpected error '%v'", err)
  6195  				}
  6196  				if !reflect.DeepEqual(test.openapiSwaggerObject, test.expectedOpenAPIObject) {
  6197  					t.Errorf("openapiSwaggerObject was not updated correctly, expected '%+v', got '%+v'", test.expectedOpenAPIObject, test.openapiSwaggerObject)
  6198  				}
  6199  			} else {
  6200  				if err == nil {
  6201  					t.Error("expected update error not returned")
  6202  				}
  6203  				if !reflect.DeepEqual(test.openapiSwaggerObject, test.expectedOpenAPIObject) {
  6204  					t.Errorf("openapiSwaggerObject was not updated correctly, expected '%+v', got '%+v'", test.expectedOpenAPIObject, test.openapiSwaggerObject)
  6205  				}
  6206  				if err.Error() != test.expectedError.Error() {
  6207  					t.Errorf("expected error malformed, expected %q, got %q", test.expectedError.Error(), err.Error())
  6208  				}
  6209  			}
  6210  		})
  6211  	}
  6212  }
  6213  
  6214  func TestMessageOptionsWithGoTemplate(t *testing.T) {
  6215  	tests := []struct {
  6216  		descr          string
  6217  		msgDescs       []*descriptorpb.DescriptorProto
  6218  		schema         map[string]*openapi_options.Schema // per-message schema to add
  6219  		defs           openapiDefinitionsObject
  6220  		openAPIOptions *openapiconfig.OpenAPIOptions
  6221  		useGoTemplate  bool
  6222  		goTemplateArgs []string
  6223  	}{
  6224  		{
  6225  			descr: "external docs option",
  6226  			msgDescs: []*descriptorpb.DescriptorProto{
  6227  				{Name: proto.String("Message")},
  6228  			},
  6229  			schema: map[string]*openapi_options.Schema{
  6230  				"Message": {
  6231  					JsonSchema: &openapi_options.JSONSchema{
  6232  						Title:       "{{.Name}}",
  6233  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  6234  					},
  6235  					ExternalDocs: &openapi_options.ExternalDocumentation{
  6236  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  6237  					},
  6238  				},
  6239  			},
  6240  			defs: map[string]openapiSchemaObject{
  6241  				"Message": {
  6242  					schemaCore: schemaCore{
  6243  						Type: "object",
  6244  					},
  6245  					Title:       "Message",
  6246  					Description: `Description "which means nothing"`,
  6247  					ExternalDocs: &openapiExternalDocumentationObject{
  6248  						Description: `Description "which means nothing"`,
  6249  					},
  6250  				},
  6251  			},
  6252  			useGoTemplate: true,
  6253  		},
  6254  		{
  6255  			descr: "external docs option",
  6256  			msgDescs: []*descriptorpb.DescriptorProto{
  6257  				{Name: proto.String("Message")},
  6258  			},
  6259  			schema: map[string]*openapi_options.Schema{
  6260  				"Message": {
  6261  					JsonSchema: &openapi_options.JSONSchema{
  6262  						Title:       "{{.Name}}",
  6263  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  6264  					},
  6265  					ExternalDocs: &openapi_options.ExternalDocumentation{
  6266  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  6267  					},
  6268  				},
  6269  			},
  6270  			defs: map[string]openapiSchemaObject{
  6271  				"Message": {
  6272  					schemaCore: schemaCore{
  6273  						Type: "object",
  6274  					},
  6275  					Title:       "{{.Name}}",
  6276  					Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  6277  					ExternalDocs: &openapiExternalDocumentationObject{
  6278  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  6279  					},
  6280  				},
  6281  			},
  6282  			useGoTemplate: false,
  6283  		},
  6284  		{
  6285  			descr: "external docs option with go template args",
  6286  			msgDescs: []*descriptorpb.DescriptorProto{
  6287  				{Name: proto.String("Message")},
  6288  			},
  6289  			schema: map[string]*openapi_options.Schema{
  6290  				"Message": {
  6291  					JsonSchema: &openapi_options.JSONSchema{
  6292  						Title: "{{.Name}}",
  6293  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} " +
  6294  							"{{arg \"my_key\"}}",
  6295  					},
  6296  					ExternalDocs: &openapi_options.ExternalDocumentation{
  6297  						Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} " +
  6298  							"{{arg \"my_key\"}}",
  6299  					},
  6300  				},
  6301  			},
  6302  			defs: map[string]openapiSchemaObject{
  6303  				"Message": {
  6304  					schemaCore: schemaCore{
  6305  						Type: "object",
  6306  					},
  6307  					Title:       "Message",
  6308  					Description: `Description "which means nothing" too`,
  6309  					ExternalDocs: &openapiExternalDocumentationObject{
  6310  						Description: `Description "which means nothing" too`,
  6311  					},
  6312  				},
  6313  			},
  6314  			useGoTemplate:  true,
  6315  			goTemplateArgs: []string{"my_key=too"},
  6316  		},
  6317  		{
  6318  			descr: "registered OpenAPIOption",
  6319  			msgDescs: []*descriptorpb.DescriptorProto{
  6320  				{Name: proto.String("Message")},
  6321  			},
  6322  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  6323  				Message: []*openapiconfig.OpenAPIMessageOption{
  6324  					{
  6325  						Message: "example.Message",
  6326  						Option: &openapi_options.Schema{
  6327  							JsonSchema: &openapi_options.JSONSchema{
  6328  								Title:       "{{.Name}}",
  6329  								Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  6330  							},
  6331  							ExternalDocs: &openapi_options.ExternalDocumentation{
  6332  								Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  6333  							},
  6334  						},
  6335  					},
  6336  				},
  6337  			},
  6338  			defs: map[string]openapiSchemaObject{
  6339  				"Message": {
  6340  					schemaCore: schemaCore{
  6341  						Type: "object",
  6342  					},
  6343  					Title:       "Message",
  6344  					Description: `Description "which means nothing"`,
  6345  					ExternalDocs: &openapiExternalDocumentationObject{
  6346  						Description: `Description "which means nothing"`,
  6347  					},
  6348  				},
  6349  			},
  6350  			useGoTemplate: true,
  6351  		},
  6352  		{
  6353  			descr: "registered OpenAPIOption with go template args",
  6354  			msgDescs: []*descriptorpb.DescriptorProto{
  6355  				{Name: proto.String("Message")},
  6356  			},
  6357  			openAPIOptions: &openapiconfig.OpenAPIOptions{
  6358  				Message: []*openapiconfig.OpenAPIMessageOption{
  6359  					{
  6360  						Message: "example.Message",
  6361  						Option: &openapi_options.Schema{
  6362  							JsonSchema: &openapi_options.JSONSchema{
  6363  								Title: "{{.Name}}",
  6364  								Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} " +
  6365  									"{{arg \"my_key\"}}",
  6366  							},
  6367  							ExternalDocs: &openapi_options.ExternalDocumentation{
  6368  								Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}} " +
  6369  									"{{arg \"my_key\"}}",
  6370  							},
  6371  						},
  6372  					},
  6373  				},
  6374  			},
  6375  			defs: map[string]openapiSchemaObject{
  6376  				"Message": {
  6377  					schemaCore: schemaCore{
  6378  						Type: "object",
  6379  					},
  6380  					Title:       "Message",
  6381  					Description: `Description "which means nothing" too`,
  6382  					ExternalDocs: &openapiExternalDocumentationObject{
  6383  						Description: `Description "which means nothing" too`,
  6384  					},
  6385  				},
  6386  			},
  6387  			useGoTemplate:  true,
  6388  			goTemplateArgs: []string{"my_key=too"},
  6389  		},
  6390  	}
  6391  
  6392  	for _, test := range tests {
  6393  		t.Run(test.descr, func(t *testing.T) {
  6394  
  6395  			msgs := []*descriptor.Message{}
  6396  			for _, msgdesc := range test.msgDescs {
  6397  				msgdesc.Options = &descriptorpb.MessageOptions{}
  6398  				msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  6399  			}
  6400  
  6401  			reg := descriptor.NewRegistry()
  6402  			reg.SetUseGoTemplate(test.useGoTemplate)
  6403  			reg.SetGoTemplateArgs(test.goTemplateArgs)
  6404  			file := descriptor.File{
  6405  				FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  6406  					SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  6407  					Name:           proto.String("example.proto"),
  6408  					Package:        proto.String("example"),
  6409  					Dependency:     []string{},
  6410  					MessageType:    test.msgDescs,
  6411  					EnumType:       []*descriptorpb.EnumDescriptorProto{},
  6412  					Service:        []*descriptorpb.ServiceDescriptorProto{},
  6413  					Options: &descriptorpb.FileOptions{
  6414  						GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  6415  					},
  6416  				},
  6417  				Messages: msgs,
  6418  			}
  6419  			err := reg.Load(&pluginpb.CodeGeneratorRequest{
  6420  				ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  6421  			})
  6422  			if err != nil {
  6423  				t.Fatalf("failed to load code generator request: %v", err)
  6424  			}
  6425  
  6426  			msgMap := map[string]*descriptor.Message{}
  6427  			for _, d := range test.msgDescs {
  6428  				name := d.GetName()
  6429  				msg, err := reg.LookupMsg("example", name)
  6430  				if err != nil {
  6431  					t.Fatalf("lookup message %v: %v", name, err)
  6432  				}
  6433  				msgMap[msg.FQMN()] = msg
  6434  
  6435  				if schema, ok := test.schema[name]; ok {
  6436  					proto.SetExtension(d.Options, openapi_options.E_Openapiv2Schema, schema)
  6437  				}
  6438  			}
  6439  
  6440  			if test.openAPIOptions != nil {
  6441  				if err := reg.RegisterOpenAPIOptions(test.openAPIOptions); err != nil {
  6442  					t.Fatalf("failed to register OpenAPI options: %s", err)
  6443  				}
  6444  			}
  6445  
  6446  			refs := make(refMap)
  6447  			actual := make(openapiDefinitionsObject)
  6448  			if err := renderMessagesAsDefinition(msgMap, actual, reg, refs, nil); err != nil {
  6449  				t.Errorf("renderMessagesAsDefinition failed with: %s", err)
  6450  			}
  6451  
  6452  			if !reflect.DeepEqual(actual, test.defs) {
  6453  				t.Errorf("Expected renderMessagesAsDefinition() to add defs %+v, not %+v", test.defs, actual)
  6454  			}
  6455  		})
  6456  	}
  6457  }
  6458  
  6459  func TestTagsWithGoTemplate(t *testing.T) {
  6460  	reg := descriptor.NewRegistry()
  6461  	reg.SetUseGoTemplate(true)
  6462  	reg.SetGoTemplateArgs([]string{"my_key=my_value"})
  6463  
  6464  	svc := &descriptorpb.ServiceDescriptorProto{
  6465  		Name:    proto.String("ExampleService"),
  6466  		Options: &descriptorpb.ServiceOptions{},
  6467  	}
  6468  
  6469  	file := descriptor.File{
  6470  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  6471  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  6472  			Name:           proto.String("example.proto"),
  6473  			Package:        proto.String("example"),
  6474  			Dependency:     []string{},
  6475  			MessageType:    []*descriptorpb.DescriptorProto{},
  6476  			EnumType:       []*descriptorpb.EnumDescriptorProto{},
  6477  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  6478  			Options: &descriptorpb.FileOptions{
  6479  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  6480  			},
  6481  		},
  6482  		Messages: []*descriptor.Message{},
  6483  		Services: []*descriptor.Service{
  6484  			{
  6485  				ServiceDescriptorProto: svc,
  6486  			},
  6487  		},
  6488  	}
  6489  
  6490  	// Set tag through service extension
  6491  	proto.SetExtension(file.GetService()[0].Options, openapi_options.E_Openapiv2Tag, &openapi_options.Tag{
  6492  		Name:        "service tag",
  6493  		Description: "{{ .Name }}!"})
  6494  
  6495  	// Set tags through file extension
  6496  	swagger := openapi_options.Swagger{
  6497  		Tags: []*openapi_options.Tag{
  6498  			{
  6499  				Name:        "not a service tag",
  6500  				Description: "{{ import \"file\" }}",
  6501  			},
  6502  			{
  6503  				Name:        "ExampleService",
  6504  				Description: "ExampleService!",
  6505  			},
  6506  			{
  6507  				Name:        "not a service tag 2",
  6508  				Description: "{{ import \"file\" }}",
  6509  			},
  6510  			{
  6511  				Name:        "Service with my_key",
  6512  				Description: "the {{arg \"my_key\"}}",
  6513  			},
  6514  		},
  6515  	}
  6516  	proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), openapi_options.E_Openapiv2Swagger, &swagger)
  6517  
  6518  	actual, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  6519  	if err != nil {
  6520  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  6521  	}
  6522  	expectedTags := []openapiTagObject{
  6523  		{
  6524  			Name:        "not a service tag",
  6525  			Description: "open file: no such file or directory",
  6526  		},
  6527  		{
  6528  			Name:        "ExampleService",
  6529  			Description: "ExampleService!",
  6530  		},
  6531  		{
  6532  			Name:        "not a service tag 2",
  6533  			Description: "open file: no such file or directory",
  6534  		},
  6535  		{
  6536  			Name:        "Service with my_key",
  6537  			Description: "the my_value",
  6538  		},
  6539  		{
  6540  			Name:        "service tag",
  6541  			Description: "ExampleService!",
  6542  		},
  6543  	}
  6544  	if !reflect.DeepEqual(actual.Tags, expectedTags) {
  6545  		t.Errorf("Expected tags %+v, not %+v", expectedTags, actual.Tags)
  6546  	}
  6547  }
  6548  
  6549  func TestTemplateWithoutErrorDefinition(t *testing.T) {
  6550  	msgdesc := &descriptorpb.DescriptorProto{
  6551  		Name:  proto.String("ExampleMessage"),
  6552  		Field: []*descriptorpb.FieldDescriptorProto{},
  6553  	}
  6554  	meth := &descriptorpb.MethodDescriptorProto{
  6555  		Name:       proto.String("Echo"),
  6556  		InputType:  proto.String("ExampleMessage"),
  6557  		OutputType: proto.String("ExampleMessage"),
  6558  	}
  6559  	svc := &descriptorpb.ServiceDescriptorProto{
  6560  		Name:   proto.String("ExampleService"),
  6561  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  6562  	}
  6563  
  6564  	msg := &descriptor.Message{
  6565  		DescriptorProto: msgdesc,
  6566  	}
  6567  
  6568  	file := descriptor.File{
  6569  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  6570  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  6571  			Name:           proto.String("example.proto"),
  6572  			Package:        proto.String("example"),
  6573  			MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
  6574  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  6575  			Options: &descriptorpb.FileOptions{
  6576  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  6577  			},
  6578  		},
  6579  		GoPkg: descriptor.GoPackage{
  6580  			Path: "example.com/path/to/example/example.pb",
  6581  			Name: "example_pb",
  6582  		},
  6583  		Messages: []*descriptor.Message{msg},
  6584  		Services: []*descriptor.Service{
  6585  			{
  6586  				ServiceDescriptorProto: svc,
  6587  				Methods: []*descriptor.Method{
  6588  					{
  6589  						MethodDescriptorProto: meth,
  6590  						RequestType:           msg,
  6591  						ResponseType:          msg,
  6592  						Bindings: []*descriptor.Binding{
  6593  							{
  6594  								HTTPMethod: "POST",
  6595  								PathTmpl: httprule.Template{
  6596  									Version:  1,
  6597  									OpCodes:  []int{0, 0},
  6598  									Template: "/v1/echo",
  6599  								},
  6600  								Body: &descriptor.Body{
  6601  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  6602  								},
  6603  							},
  6604  						},
  6605  					},
  6606  				},
  6607  			},
  6608  		},
  6609  	}
  6610  	reg := descriptor.NewRegistry()
  6611  	err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  6612  	if err != nil {
  6613  		t.Errorf("failed to reg.Load(): %v", err)
  6614  		return
  6615  	}
  6616  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  6617  	if err != nil {
  6618  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  6619  		return
  6620  	}
  6621  
  6622  	defRsp, ok := result.getPathItemObject("/v1/echo").Post.Responses["default"]
  6623  	if !ok {
  6624  		return
  6625  	}
  6626  
  6627  	ref := defRsp.Schema.schemaCore.Ref
  6628  	refName := strings.TrimPrefix(ref, "#/definitions/")
  6629  	if refName == "" {
  6630  		t.Fatal("created default Error response with empty reflink")
  6631  	}
  6632  
  6633  	if _, ok := result.Definitions[refName]; !ok {
  6634  		t.Errorf("default Error response with reflink '%v', but its definition was not found", refName)
  6635  	}
  6636  }
  6637  
  6638  func TestSingleServiceTemplateWithDuplicateHttp1Operations(t *testing.T) {
  6639  	fieldType := descriptorpb.FieldDescriptorProto_TYPE_STRING
  6640  	field1 := &descriptorpb.FieldDescriptorProto{
  6641  		Name:   proto.String("name"),
  6642  		Number: proto.Int32(1),
  6643  		Type:   &fieldType,
  6644  	}
  6645  
  6646  	getFooMsgDesc := &descriptorpb.DescriptorProto{
  6647  		Name: proto.String("GetFooRequest"),
  6648  		Field: []*descriptorpb.FieldDescriptorProto{
  6649  			field1,
  6650  		},
  6651  	}
  6652  	getFooMsg := &descriptor.Message{
  6653  		DescriptorProto: getFooMsgDesc,
  6654  	}
  6655  	deleteFooMsgDesc := &descriptorpb.DescriptorProto{
  6656  		Name: proto.String("DeleteFooRequest"),
  6657  		Field: []*descriptorpb.FieldDescriptorProto{
  6658  			field1,
  6659  		},
  6660  	}
  6661  	deleteFooMsg := &descriptor.Message{
  6662  		DescriptorProto: deleteFooMsgDesc,
  6663  	}
  6664  	getFoo := &descriptorpb.MethodDescriptorProto{
  6665  		Name:       proto.String("GetFoo"),
  6666  		InputType:  proto.String("GetFooRequest"),
  6667  		OutputType: proto.String("EmptyMessage"),
  6668  	}
  6669  	deleteFoo := &descriptorpb.MethodDescriptorProto{
  6670  		Name:       proto.String("DeleteFoo"),
  6671  		InputType:  proto.String("DeleteFooRequest"),
  6672  		OutputType: proto.String("EmptyMessage"),
  6673  	}
  6674  
  6675  	getBarMsgDesc := &descriptorpb.DescriptorProto{
  6676  		Name: proto.String("GetBarRequest"),
  6677  		Field: []*descriptorpb.FieldDescriptorProto{
  6678  			field1,
  6679  		},
  6680  	}
  6681  	getBarMsg := &descriptor.Message{
  6682  		DescriptorProto: getBarMsgDesc,
  6683  	}
  6684  	deleteBarMsgDesc := &descriptorpb.DescriptorProto{
  6685  		Name: proto.String("DeleteBarRequest"),
  6686  		Field: []*descriptorpb.FieldDescriptorProto{
  6687  			field1,
  6688  		},
  6689  	}
  6690  	deleteBarMsg := &descriptor.Message{
  6691  		DescriptorProto: deleteBarMsgDesc,
  6692  	}
  6693  	getBar := &descriptorpb.MethodDescriptorProto{
  6694  		Name:       proto.String("GetBar"),
  6695  		InputType:  proto.String("GetBarRequest"),
  6696  		OutputType: proto.String("EmptyMessage"),
  6697  	}
  6698  	deleteBar := &descriptorpb.MethodDescriptorProto{
  6699  		Name:       proto.String("DeleteBar"),
  6700  		InputType:  proto.String("DeleteBarRequest"),
  6701  		OutputType: proto.String("EmptyMessage"),
  6702  	}
  6703  
  6704  	svc1 := &descriptorpb.ServiceDescriptorProto{
  6705  		Name:   proto.String("Service1"),
  6706  		Method: []*descriptorpb.MethodDescriptorProto{getFoo, deleteFoo, getBar, deleteBar},
  6707  	}
  6708  
  6709  	emptyMsgDesc := &descriptorpb.DescriptorProto{
  6710  		Name: proto.String("EmptyMessage"),
  6711  	}
  6712  	emptyMsg := &descriptor.Message{
  6713  		DescriptorProto: emptyMsgDesc,
  6714  	}
  6715  
  6716  	file := descriptor.File{
  6717  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  6718  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  6719  			Name:           proto.String("service1.proto"),
  6720  			Package:        proto.String("example"),
  6721  			MessageType:    []*descriptorpb.DescriptorProto{getBarMsgDesc, deleteBarMsgDesc, getFooMsgDesc, deleteFooMsgDesc, emptyMsgDesc},
  6722  			Service:        []*descriptorpb.ServiceDescriptorProto{svc1},
  6723  			Options: &descriptorpb.FileOptions{
  6724  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  6725  			},
  6726  		},
  6727  		GoPkg: descriptor.GoPackage{
  6728  			Path: "example.com/path/to/example/example.pb",
  6729  			Name: "example_pb",
  6730  		},
  6731  		Messages: []*descriptor.Message{getFooMsg, deleteFooMsg, getBarMsg, deleteBarMsg, emptyMsg},
  6732  		Services: []*descriptor.Service{
  6733  			{
  6734  				ServiceDescriptorProto: svc1,
  6735  				Methods: []*descriptor.Method{
  6736  					{
  6737  						MethodDescriptorProto: getFoo,
  6738  						RequestType:           getFooMsg,
  6739  						ResponseType:          getFooMsg,
  6740  						Bindings: []*descriptor.Binding{
  6741  							{
  6742  								HTTPMethod: "GET",
  6743  								PathTmpl: httprule.Template{
  6744  									Version:  1,
  6745  									OpCodes:  []int{0, 0},
  6746  									Template: "/v1/{name=foos/*}",
  6747  								},
  6748  								PathParams: []descriptor.Parameter{
  6749  									{
  6750  										Target: &descriptor.Field{
  6751  											FieldDescriptorProto: field1,
  6752  											Message:              getFooMsg,
  6753  										},
  6754  										FieldPath: descriptor.FieldPath{
  6755  											{
  6756  												Name: "name",
  6757  											},
  6758  										},
  6759  									},
  6760  								},
  6761  								Body: &descriptor.Body{
  6762  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  6763  								},
  6764  							},
  6765  						},
  6766  					},
  6767  					{
  6768  						MethodDescriptorProto: deleteFoo,
  6769  						RequestType:           deleteFooMsg,
  6770  						ResponseType:          emptyMsg,
  6771  						Bindings: []*descriptor.Binding{
  6772  							{
  6773  								HTTPMethod: "DELETE",
  6774  								PathTmpl: httprule.Template{
  6775  									Version:  1,
  6776  									OpCodes:  []int{0, 0},
  6777  									Template: "/v1/{name=foos/*}",
  6778  								},
  6779  								PathParams: []descriptor.Parameter{
  6780  									{
  6781  										Target: &descriptor.Field{
  6782  											FieldDescriptorProto: field1,
  6783  											Message:              deleteFooMsg,
  6784  										},
  6785  										FieldPath: descriptor.FieldPath{
  6786  											{
  6787  												Name: "name",
  6788  											},
  6789  										},
  6790  									},
  6791  								},
  6792  								Body: &descriptor.Body{
  6793  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  6794  								},
  6795  							},
  6796  						},
  6797  					},
  6798  					{
  6799  						MethodDescriptorProto: getBar,
  6800  						RequestType:           getBarMsg,
  6801  						ResponseType:          getBarMsg,
  6802  						Bindings: []*descriptor.Binding{
  6803  							{
  6804  								HTTPMethod: "GET",
  6805  								PathTmpl: httprule.Template{
  6806  									Version:  1,
  6807  									OpCodes:  []int{0, 0},
  6808  									Template: "/v1/{name=bars/*}",
  6809  								},
  6810  								PathParams: []descriptor.Parameter{
  6811  									{
  6812  										Target: &descriptor.Field{
  6813  											FieldDescriptorProto: field1,
  6814  											Message:              getBarMsg,
  6815  										},
  6816  										FieldPath: descriptor.FieldPath{
  6817  											{
  6818  												Name: "name",
  6819  											},
  6820  										},
  6821  									},
  6822  								},
  6823  								Body: &descriptor.Body{
  6824  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  6825  								},
  6826  							},
  6827  						},
  6828  					},
  6829  					{
  6830  						MethodDescriptorProto: deleteBar,
  6831  						RequestType:           deleteBarMsg,
  6832  						ResponseType:          emptyMsg,
  6833  						Bindings: []*descriptor.Binding{
  6834  							{
  6835  								HTTPMethod: "DELETE",
  6836  								PathTmpl: httprule.Template{
  6837  									Version:  1,
  6838  									OpCodes:  []int{0, 0},
  6839  									Template: "/v1/{name=bars/*}",
  6840  								},
  6841  								PathParams: []descriptor.Parameter{
  6842  									{
  6843  										Target: &descriptor.Field{
  6844  											FieldDescriptorProto: field1,
  6845  											Message:              deleteBarMsg,
  6846  										},
  6847  										FieldPath: descriptor.FieldPath{
  6848  											{
  6849  												Name: "name",
  6850  											},
  6851  										},
  6852  									},
  6853  								},
  6854  								Body: &descriptor.Body{
  6855  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  6856  								},
  6857  							},
  6858  						},
  6859  					},
  6860  				},
  6861  			},
  6862  		},
  6863  	}
  6864  	reg := descriptor.NewRegistry()
  6865  	err := reg.Load(reqFromFile(&file))
  6866  	if err != nil {
  6867  		t.Fatalf("failed to reg.Load(): %v", err)
  6868  	}
  6869  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  6870  	if err != nil {
  6871  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  6872  	}
  6873  
  6874  	if got, want := len(result.Paths), 2; got != want {
  6875  		t.Fatalf("Results path length differed, got %d want %d", got, want)
  6876  	}
  6877  
  6878  	firstOpGet := result.getPathItemObject("/v1/{name}").Get
  6879  	if got, want := firstOpGet.OperationID, "Service1_GetFoo"; got != want {
  6880  		t.Fatalf("First operation GET id differed, got %s want %s", got, want)
  6881  	}
  6882  	if got, want := len(firstOpGet.Parameters), 2; got != want {
  6883  		t.Fatalf("First operation GET params length differed, got %d want %d", got, want)
  6884  	}
  6885  	if got, want := firstOpGet.Parameters[0].Name, "name"; got != want {
  6886  		t.Fatalf("First operation GET first param name differed, got %s want %s", got, want)
  6887  	}
  6888  	if got, want := firstOpGet.Parameters[0].Pattern, "foos/[^/]+"; got != want {
  6889  		t.Fatalf("First operation GET first param pattern differed, got %s want %s", got, want)
  6890  	}
  6891  	if got, want := firstOpGet.Parameters[1].In, "body"; got != want {
  6892  		t.Fatalf("First operation GET second param 'in' differed, got %s want %s", got, want)
  6893  	}
  6894  
  6895  	firstOpDelete := result.getPathItemObject("/v1/{name}").Delete
  6896  	if got, want := firstOpDelete.OperationID, "Service1_DeleteFoo"; got != want {
  6897  		t.Fatalf("First operation id DELETE differed, got %s want %s", got, want)
  6898  	}
  6899  	if got, want := len(firstOpDelete.Parameters), 2; got != want {
  6900  		t.Fatalf("First operation DELETE params length differed, got %d want %d", got, want)
  6901  	}
  6902  	if got, want := firstOpDelete.Parameters[0].Name, "name"; got != want {
  6903  		t.Fatalf("First operation DELETE first param name differed, got %s want %s", got, want)
  6904  	}
  6905  	if got, want := firstOpDelete.Parameters[0].Pattern, "foos/[^/]+"; got != want {
  6906  		t.Fatalf("First operation DELETE first param pattern differed, got %s want %s", got, want)
  6907  	}
  6908  	if got, want := firstOpDelete.Parameters[1].In, "body"; got != want {
  6909  		t.Fatalf("First operation DELETE second param 'in' differed, got %s want %s", got, want)
  6910  	}
  6911  
  6912  	secondOpGet := result.getPathItemObject("/v1/{name" + pathParamUniqueSuffixDeliminator + "1}").Get
  6913  	if got, want := secondOpGet.OperationID, "Service1_GetBar"; got != want {
  6914  		t.Fatalf("Second operation id GET differed, got %s want %s", got, want)
  6915  	}
  6916  	if got, want := len(secondOpGet.Parameters), 2; got != want {
  6917  		t.Fatalf("Second operation GET params length differed, got %d want %d", got, want)
  6918  	}
  6919  	if got, want := secondOpGet.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"1"; got != want {
  6920  		t.Fatalf("Second operation GET first param name differed, got %s want %s", got, want)
  6921  	}
  6922  	if got, want := secondOpGet.Parameters[0].Pattern, "bars/[^/]+"; got != want {
  6923  		t.Fatalf("Second operation GET first param pattern differed, got %s want %s", got, want)
  6924  	}
  6925  	if got, want := secondOpGet.Parameters[1].In, "body"; got != want {
  6926  		t.Fatalf("Second operation GET second param 'in' differed, got %s want %s", got, want)
  6927  	}
  6928  
  6929  	secondOpDelete := result.getPathItemObject("/v1/{name" + pathParamUniqueSuffixDeliminator + "1}").Delete
  6930  	if got, want := secondOpDelete.OperationID, "Service1_DeleteBar"; got != want {
  6931  		t.Fatalf("Second operation id differed, got %s want %s", got, want)
  6932  	}
  6933  	if got, want := len(secondOpDelete.Parameters), 2; got != want {
  6934  		t.Fatalf("Second operation params length differed, got %d want %d", got, want)
  6935  	}
  6936  	if got, want := secondOpDelete.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"1"; got != want {
  6937  		t.Fatalf("Second operation first param name differed, got %s want %s", got, want)
  6938  	}
  6939  	if got, want := secondOpDelete.Parameters[0].Pattern, "bars/[^/]+"; got != want {
  6940  		t.Fatalf("Second operation first param pattern differed, got %s want %s", got, want)
  6941  	}
  6942  	if got, want := secondOpDelete.Parameters[1].In, "body"; got != want {
  6943  		t.Fatalf("Second operation third param 'in' differed, got %s want %s", got, want)
  6944  	}
  6945  }
  6946  
  6947  func getOperation(pathItem openapiPathItemObject, httpMethod string) *openapiOperationObject {
  6948  	switch httpMethod {
  6949  	case "GET":
  6950  		return pathItem.Get
  6951  	case "POST":
  6952  		return pathItem.Post
  6953  	case "PUT":
  6954  		return pathItem.Put
  6955  	case "DELETE":
  6956  		return pathItem.Delete
  6957  	case "PATCH":
  6958  		return pathItem.Patch
  6959  	case "HEAD":
  6960  		return pathItem.Head
  6961  	case "OPTIONS":
  6962  		return pathItem.Options
  6963  	default:
  6964  		return nil
  6965  	}
  6966  }
  6967  
  6968  func TestSingleServiceTemplateWithDuplicateInAllSupportedHttp1Operations(t *testing.T) {
  6969  	supportedMethods := []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"}
  6970  
  6971  	for _, method := range supportedMethods {
  6972  		fieldType := descriptorpb.FieldDescriptorProto_TYPE_STRING
  6973  		field1 := &descriptorpb.FieldDescriptorProto{
  6974  			Name:   proto.String("name"),
  6975  			Number: proto.Int32(1),
  6976  			Type:   &fieldType,
  6977  		}
  6978  
  6979  		methodFooMsgDesc := &descriptorpb.DescriptorProto{
  6980  			Name: proto.String(method + "FooRequest"),
  6981  			Field: []*descriptorpb.FieldDescriptorProto{
  6982  				field1,
  6983  			},
  6984  		}
  6985  		methodFooMsg := &descriptor.Message{
  6986  			DescriptorProto: methodFooMsgDesc,
  6987  		}
  6988  		methodFoo := &descriptorpb.MethodDescriptorProto{
  6989  			Name:       proto.String(method + "Foo"),
  6990  			InputType:  proto.String(method + "FooRequest"),
  6991  			OutputType: proto.String("EmptyMessage"),
  6992  		}
  6993  
  6994  		methodBarMsgDesc := &descriptorpb.DescriptorProto{
  6995  			Name: proto.String(method + "BarRequest"),
  6996  			Field: []*descriptorpb.FieldDescriptorProto{
  6997  				field1,
  6998  			},
  6999  		}
  7000  		methodBarMsg := &descriptor.Message{
  7001  			DescriptorProto: methodBarMsgDesc,
  7002  		}
  7003  		methodBar := &descriptorpb.MethodDescriptorProto{
  7004  			Name:       proto.String(method + "Bar"),
  7005  			InputType:  proto.String(method + "BarRequest"),
  7006  			OutputType: proto.String("EmptyMessage"),
  7007  		}
  7008  
  7009  		svc1 := &descriptorpb.ServiceDescriptorProto{
  7010  			Name:   proto.String("Service1"),
  7011  			Method: []*descriptorpb.MethodDescriptorProto{methodFoo, methodBar},
  7012  		}
  7013  
  7014  		emptyMsgDesc := &descriptorpb.DescriptorProto{
  7015  			Name: proto.String("EmptyMessage"),
  7016  		}
  7017  		emptyMsg := &descriptor.Message{
  7018  			DescriptorProto: emptyMsgDesc,
  7019  		}
  7020  
  7021  		file := descriptor.File{
  7022  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  7023  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  7024  				Name:           proto.String("service1.proto"),
  7025  				Package:        proto.String("example"),
  7026  				MessageType:    []*descriptorpb.DescriptorProto{methodBarMsgDesc, methodFooMsgDesc, emptyMsgDesc},
  7027  				Service:        []*descriptorpb.ServiceDescriptorProto{svc1},
  7028  				Options: &descriptorpb.FileOptions{
  7029  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  7030  				},
  7031  			},
  7032  			GoPkg: descriptor.GoPackage{
  7033  				Path: "example.com/path/to/example/example.pb",
  7034  				Name: "example_pb",
  7035  			},
  7036  			Messages: []*descriptor.Message{methodFooMsg, methodBarMsg, emptyMsg},
  7037  			Services: []*descriptor.Service{
  7038  				{
  7039  					ServiceDescriptorProto: svc1,
  7040  					Methods: []*descriptor.Method{
  7041  						{
  7042  							MethodDescriptorProto: methodFoo,
  7043  							RequestType:           methodFooMsg,
  7044  							ResponseType:          methodFooMsg,
  7045  							Bindings: []*descriptor.Binding{
  7046  								{
  7047  									HTTPMethod: method,
  7048  									PathTmpl: httprule.Template{
  7049  										Version:  1,
  7050  										OpCodes:  []int{0, 0},
  7051  										Template: "/v1/{name=foos/*}",
  7052  									},
  7053  									PathParams: []descriptor.Parameter{
  7054  										{
  7055  											Target: &descriptor.Field{
  7056  												FieldDescriptorProto: field1,
  7057  												Message:              methodFooMsg,
  7058  											},
  7059  											FieldPath: descriptor.FieldPath{
  7060  												{
  7061  													Name: "name",
  7062  												},
  7063  											},
  7064  										},
  7065  									},
  7066  									Body: &descriptor.Body{
  7067  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  7068  									},
  7069  								},
  7070  							},
  7071  						},
  7072  						{
  7073  							MethodDescriptorProto: methodBar,
  7074  							RequestType:           methodBarMsg,
  7075  							ResponseType:          methodBarMsg,
  7076  							Bindings: []*descriptor.Binding{
  7077  								{
  7078  									HTTPMethod: method,
  7079  									PathTmpl: httprule.Template{
  7080  										Version:  1,
  7081  										OpCodes:  []int{0, 0},
  7082  										Template: "/v1/{name=bars/*}",
  7083  									},
  7084  									PathParams: []descriptor.Parameter{
  7085  										{
  7086  											Target: &descriptor.Field{
  7087  												FieldDescriptorProto: field1,
  7088  												Message:              methodBarMsg,
  7089  											},
  7090  											FieldPath: descriptor.FieldPath{
  7091  												{
  7092  													Name: "name",
  7093  												},
  7094  											},
  7095  										},
  7096  									},
  7097  									Body: &descriptor.Body{
  7098  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  7099  									},
  7100  								},
  7101  							},
  7102  						},
  7103  					},
  7104  				},
  7105  			},
  7106  		}
  7107  		reg := descriptor.NewRegistry()
  7108  		err := reg.Load(reqFromFile(&file))
  7109  		if err != nil {
  7110  			t.Fatalf("failed to reg.Load(): %v", err)
  7111  		}
  7112  		result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  7113  		if err != nil {
  7114  			t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  7115  		}
  7116  
  7117  		if got, want := len(result.Paths), 2; got != want {
  7118  			t.Fatalf("Results path length differed, got %d want %d", got, want)
  7119  		}
  7120  
  7121  		firstOpMethod := getOperation(result.getPathItemObject("/v1/{name}"), method)
  7122  		if got, want := firstOpMethod.OperationID, "Service1_"+method+"Foo"; got != want {
  7123  			t.Fatalf("First operation %s id differed, got %s want %s", method, got, want)
  7124  		}
  7125  		if got, want := len(firstOpMethod.Parameters), 2; got != want {
  7126  			t.Fatalf("First operation %s params length differed, got %d want %d", method, got, want)
  7127  		}
  7128  		if got, want := firstOpMethod.Parameters[0].Name, "name"; got != want {
  7129  			t.Fatalf("First operation %s first param name differed, got %s want %s", method, got, want)
  7130  		}
  7131  		if got, want := firstOpMethod.Parameters[0].Pattern, "foos/[^/]+"; got != want {
  7132  			t.Fatalf("First operation %s first param pattern differed, got %s want %s", method, got, want)
  7133  		}
  7134  		if got, want := firstOpMethod.Parameters[1].In, "body"; got != want {
  7135  			t.Fatalf("First operation %s second param 'in' differed, got %s want %s", method, got, want)
  7136  		}
  7137  
  7138  		secondOpMethod := getOperation(result.getPathItemObject("/v1/{name"+pathParamUniqueSuffixDeliminator+"1}"), method)
  7139  		if got, want := secondOpMethod.OperationID, "Service1_"+method+"Bar"; got != want {
  7140  			t.Fatalf("Second operation id %s differed, got %s want %s", method, got, want)
  7141  		}
  7142  		if got, want := len(secondOpMethod.Parameters), 2; got != want {
  7143  			t.Fatalf("Second operation %s params length differed, got %d want %d", method, got, want)
  7144  		}
  7145  		if got, want := secondOpMethod.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"1"; got != want {
  7146  			t.Fatalf("Second operation %s first param name differed, got %s want %s", method, got, want)
  7147  		}
  7148  		if got, want := secondOpMethod.Parameters[0].Pattern, "bars/[^/]+"; got != want {
  7149  			t.Fatalf("Second operation %s first param pattern differed, got %s want %s", method, got, want)
  7150  		}
  7151  		if got, want := secondOpMethod.Parameters[1].In, "body"; got != want {
  7152  			t.Fatalf("Second operation %s second param 'in' differed, got %s want %s", method, got, want)
  7153  		}
  7154  	}
  7155  }
  7156  
  7157  func TestSingleServiceTemplateWithDuplicateHttp1UnsupportedOperations(t *testing.T) {
  7158  	fieldType := descriptorpb.FieldDescriptorProto_TYPE_STRING
  7159  	field1 := &descriptorpb.FieldDescriptorProto{
  7160  		Name:   proto.String("name"),
  7161  		Number: proto.Int32(1),
  7162  		Type:   &fieldType,
  7163  	}
  7164  
  7165  	unsupportedFooMsgDesc := &descriptorpb.DescriptorProto{
  7166  		Name: proto.String("UnsupportedFooRequest"),
  7167  		Field: []*descriptorpb.FieldDescriptorProto{
  7168  			field1,
  7169  		},
  7170  	}
  7171  	unsupportedFooMsg := &descriptor.Message{
  7172  		DescriptorProto: unsupportedFooMsgDesc,
  7173  	}
  7174  	unsupportedFoo := &descriptorpb.MethodDescriptorProto{
  7175  		Name:       proto.String("UnsupportedFoo"),
  7176  		InputType:  proto.String("UnsupportedFooRequest"),
  7177  		OutputType: proto.String("EmptyMessage"),
  7178  	}
  7179  
  7180  	unsupportedBarMsgDesc := &descriptorpb.DescriptorProto{
  7181  		Name: proto.String("UnsupportedBarRequest"),
  7182  		Field: []*descriptorpb.FieldDescriptorProto{
  7183  			field1,
  7184  		},
  7185  	}
  7186  	unsupportedBarMsg := &descriptor.Message{
  7187  		DescriptorProto: unsupportedBarMsgDesc,
  7188  	}
  7189  	unsupportedBar := &descriptorpb.MethodDescriptorProto{
  7190  		Name:       proto.String("UnsupportedBar"),
  7191  		InputType:  proto.String("UnsupportedBarRequest"),
  7192  		OutputType: proto.String("EmptyMessage"),
  7193  	}
  7194  
  7195  	svc1 := &descriptorpb.ServiceDescriptorProto{
  7196  		Name:   proto.String("Service1"),
  7197  		Method: []*descriptorpb.MethodDescriptorProto{unsupportedFoo, unsupportedBar},
  7198  	}
  7199  
  7200  	emptyMsgDesc := &descriptorpb.DescriptorProto{
  7201  		Name: proto.String("EmptyMessage"),
  7202  	}
  7203  	emptyMsg := &descriptor.Message{
  7204  		DescriptorProto: emptyMsgDesc,
  7205  	}
  7206  
  7207  	file := descriptor.File{
  7208  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  7209  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  7210  			Name:           proto.String("service1.proto"),
  7211  			Package:        proto.String("example"),
  7212  			MessageType:    []*descriptorpb.DescriptorProto{unsupportedBarMsgDesc, unsupportedFooMsgDesc, emptyMsgDesc},
  7213  			Service:        []*descriptorpb.ServiceDescriptorProto{svc1},
  7214  			Options: &descriptorpb.FileOptions{
  7215  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  7216  			},
  7217  		},
  7218  		GoPkg: descriptor.GoPackage{
  7219  			Path: "example.com/path/to/example/example.pb",
  7220  			Name: "example_pb",
  7221  		},
  7222  		Messages: []*descriptor.Message{unsupportedFooMsg, unsupportedBarMsg, emptyMsg},
  7223  		Services: []*descriptor.Service{
  7224  			{
  7225  				ServiceDescriptorProto: svc1,
  7226  				Methods: []*descriptor.Method{
  7227  					{
  7228  						MethodDescriptorProto: unsupportedFoo,
  7229  						RequestType:           unsupportedFooMsg,
  7230  						ResponseType:          unsupportedFooMsg,
  7231  						Bindings: []*descriptor.Binding{
  7232  							{
  7233  								HTTPMethod: "UNSUPPORTED",
  7234  								PathTmpl: httprule.Template{
  7235  									Version:  1,
  7236  									OpCodes:  []int{0, 0},
  7237  									Template: "/v1/{name=foos/*}",
  7238  								},
  7239  								PathParams: []descriptor.Parameter{
  7240  									{
  7241  										Target: &descriptor.Field{
  7242  											FieldDescriptorProto: field1,
  7243  											Message:              unsupportedFooMsg,
  7244  										},
  7245  										FieldPath: descriptor.FieldPath{
  7246  											{
  7247  												Name: "name",
  7248  											},
  7249  										},
  7250  									},
  7251  								},
  7252  								Body: &descriptor.Body{
  7253  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  7254  								},
  7255  							},
  7256  						},
  7257  					},
  7258  					{
  7259  						MethodDescriptorProto: unsupportedBar,
  7260  						RequestType:           unsupportedBarMsg,
  7261  						ResponseType:          unsupportedBarMsg,
  7262  						Bindings: []*descriptor.Binding{
  7263  							{
  7264  								HTTPMethod: "UNSUPPORTED",
  7265  								PathTmpl: httprule.Template{
  7266  									Version:  1,
  7267  									OpCodes:  []int{0, 0},
  7268  									Template: "/v1/{name=bars/*}",
  7269  								},
  7270  								PathParams: []descriptor.Parameter{
  7271  									{
  7272  										Target: &descriptor.Field{
  7273  											FieldDescriptorProto: field1,
  7274  											Message:              unsupportedBarMsg,
  7275  										},
  7276  										FieldPath: descriptor.FieldPath{
  7277  											{
  7278  												Name: "name",
  7279  											},
  7280  										},
  7281  									},
  7282  								},
  7283  								Body: &descriptor.Body{
  7284  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  7285  								},
  7286  							},
  7287  						},
  7288  					},
  7289  				},
  7290  			},
  7291  		},
  7292  	}
  7293  	reg := descriptor.NewRegistry()
  7294  	err := reg.Load(reqFromFile(&file))
  7295  	if err != nil {
  7296  		t.Fatalf("failed to reg.Load(): %v", err)
  7297  	}
  7298  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  7299  	if err != nil {
  7300  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  7301  	}
  7302  
  7303  	// Just should not crash, no special handling of unsupported HTTP methods
  7304  	if got, want := len(result.Paths), 1; got != want {
  7305  		t.Fatalf("Results path length differed, got %d want %d", got, want)
  7306  	}
  7307  }
  7308  
  7309  func TestTemplateWithDuplicateHttp1Operations(t *testing.T) {
  7310  	fieldType := descriptorpb.FieldDescriptorProto_TYPE_STRING
  7311  	field1 := &descriptorpb.FieldDescriptorProto{
  7312  		Name:   proto.String("name"),
  7313  		Number: proto.Int32(1),
  7314  		Type:   &fieldType,
  7315  	}
  7316  	field2 := &descriptorpb.FieldDescriptorProto{
  7317  		Name:   proto.String("role"),
  7318  		Number: proto.Int32(2),
  7319  		Type:   &fieldType,
  7320  	}
  7321  	msgdesc := &descriptorpb.DescriptorProto{
  7322  		Name: proto.String("ExampleMessage"),
  7323  		Field: []*descriptorpb.FieldDescriptorProto{
  7324  			field1,
  7325  			field2,
  7326  		},
  7327  	}
  7328  	meth1 := &descriptorpb.MethodDescriptorProto{
  7329  		Name:       proto.String("Method1"),
  7330  		InputType:  proto.String("ExampleMessage"),
  7331  		OutputType: proto.String("ExampleMessage"),
  7332  	}
  7333  	meth2 := &descriptorpb.MethodDescriptorProto{
  7334  		Name:       proto.String("Method2"),
  7335  		InputType:  proto.String("ExampleMessage"),
  7336  		OutputType: proto.String("ExampleMessage"),
  7337  	}
  7338  	svc1 := &descriptorpb.ServiceDescriptorProto{
  7339  		Name:   proto.String("Service1"),
  7340  		Method: []*descriptorpb.MethodDescriptorProto{meth1, meth2},
  7341  	}
  7342  	meth3 := &descriptorpb.MethodDescriptorProto{
  7343  		Name:       proto.String("Method3"),
  7344  		InputType:  proto.String("ExampleMessage"),
  7345  		OutputType: proto.String("ExampleMessage"),
  7346  	}
  7347  	meth4 := &descriptorpb.MethodDescriptorProto{
  7348  		Name:       proto.String("Method4"),
  7349  		InputType:  proto.String("ExampleMessage"),
  7350  		OutputType: proto.String("ExampleMessage"),
  7351  	}
  7352  	svc2 := &descriptorpb.ServiceDescriptorProto{
  7353  		Name:   proto.String("Service2"),
  7354  		Method: []*descriptorpb.MethodDescriptorProto{meth3, meth4},
  7355  	}
  7356  	msg := &descriptor.Message{
  7357  		DescriptorProto: msgdesc,
  7358  	}
  7359  
  7360  	file := descriptor.File{
  7361  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  7362  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  7363  			Name:           proto.String("service1.proto"),
  7364  			Package:        proto.String("example"),
  7365  			MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
  7366  			Service:        []*descriptorpb.ServiceDescriptorProto{svc1, svc2},
  7367  			Options: &descriptorpb.FileOptions{
  7368  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  7369  			},
  7370  		},
  7371  		GoPkg: descriptor.GoPackage{
  7372  			Path: "example.com/path/to/example/example.pb",
  7373  			Name: "example_pb",
  7374  		},
  7375  		Messages: []*descriptor.Message{msg},
  7376  		Services: []*descriptor.Service{
  7377  			{
  7378  				ServiceDescriptorProto: svc1,
  7379  				Methods: []*descriptor.Method{
  7380  					{
  7381  						MethodDescriptorProto: meth1,
  7382  						RequestType:           msg,
  7383  						ResponseType:          msg,
  7384  						Bindings: []*descriptor.Binding{
  7385  							{
  7386  								HTTPMethod: "GET",
  7387  								PathTmpl: httprule.Template{
  7388  									Version:  1,
  7389  									OpCodes:  []int{0, 0},
  7390  									Template: "/v1/{name=organizations/*}/{role=roles/*}",
  7391  								},
  7392  								PathParams: []descriptor.Parameter{
  7393  									{
  7394  										Target: &descriptor.Field{
  7395  											FieldDescriptorProto: field1,
  7396  											Message:              msg,
  7397  										},
  7398  										FieldPath: descriptor.FieldPath{
  7399  											{
  7400  												Name: "name",
  7401  											},
  7402  										},
  7403  									},
  7404  									{
  7405  										Target: &descriptor.Field{
  7406  											FieldDescriptorProto: field2,
  7407  											Message:              msg,
  7408  										},
  7409  										FieldPath: descriptor.FieldPath{
  7410  											{
  7411  												Name: "role",
  7412  											},
  7413  										},
  7414  									},
  7415  								},
  7416  								Body: &descriptor.Body{
  7417  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  7418  								},
  7419  							},
  7420  						},
  7421  					},
  7422  					{
  7423  						MethodDescriptorProto: meth2,
  7424  						RequestType:           msg,
  7425  						ResponseType:          msg,
  7426  						Bindings: []*descriptor.Binding{
  7427  							{
  7428  								HTTPMethod: "GET",
  7429  								PathTmpl: httprule.Template{
  7430  									Version:  1,
  7431  									OpCodes:  []int{0, 0},
  7432  									Template: "/v1/{name=users/*}/{role=roles/*}",
  7433  								},
  7434  								PathParams: []descriptor.Parameter{
  7435  									{
  7436  										Target: &descriptor.Field{
  7437  											FieldDescriptorProto: field1,
  7438  											Message:              msg,
  7439  										},
  7440  										FieldPath: descriptor.FieldPath{
  7441  											{
  7442  												Name: "name",
  7443  											},
  7444  										},
  7445  									},
  7446  									{
  7447  										Target: &descriptor.Field{
  7448  											FieldDescriptorProto: field2,
  7449  											Message:              msg,
  7450  										},
  7451  										FieldPath: descriptor.FieldPath{
  7452  											{
  7453  												Name: "role",
  7454  											},
  7455  										},
  7456  									},
  7457  								},
  7458  								Body: &descriptor.Body{
  7459  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  7460  								},
  7461  							},
  7462  						},
  7463  					},
  7464  				},
  7465  			},
  7466  			{
  7467  				ServiceDescriptorProto: svc2,
  7468  				Methods: []*descriptor.Method{
  7469  					{
  7470  						MethodDescriptorProto: meth3,
  7471  						RequestType:           msg,
  7472  						ResponseType:          msg,
  7473  						Bindings: []*descriptor.Binding{
  7474  							{
  7475  								HTTPMethod: "GET",
  7476  								PathTmpl: httprule.Template{
  7477  									Version:  1,
  7478  									OpCodes:  []int{0, 0},
  7479  									Template: "/v1/{name=users/*}/roles",
  7480  								},
  7481  								PathParams: []descriptor.Parameter{
  7482  									{
  7483  										Target: &descriptor.Field{
  7484  											FieldDescriptorProto: field1,
  7485  											Message:              msg,
  7486  										},
  7487  										FieldPath: descriptor.FieldPath{
  7488  											{
  7489  												Name: "name",
  7490  											},
  7491  										},
  7492  									},
  7493  								},
  7494  								Body: &descriptor.Body{
  7495  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  7496  								},
  7497  							},
  7498  						},
  7499  					},
  7500  					{
  7501  						MethodDescriptorProto: meth4,
  7502  						RequestType:           msg,
  7503  						ResponseType:          msg,
  7504  						Bindings: []*descriptor.Binding{
  7505  							{
  7506  								HTTPMethod: "GET",
  7507  								PathTmpl: httprule.Template{
  7508  									Version:  1,
  7509  									OpCodes:  []int{0, 0},
  7510  									Template: "/v1/{name=groups/*}/{role=roles/*}",
  7511  								},
  7512  								PathParams: []descriptor.Parameter{
  7513  									{
  7514  										Target: &descriptor.Field{
  7515  											FieldDescriptorProto: field1,
  7516  											Message:              msg,
  7517  										},
  7518  										FieldPath: descriptor.FieldPath{
  7519  											{
  7520  												Name: "name",
  7521  											},
  7522  										},
  7523  									},
  7524  									{
  7525  										Target: &descriptor.Field{
  7526  											FieldDescriptorProto: field2,
  7527  											Message:              msg,
  7528  										},
  7529  										FieldPath: descriptor.FieldPath{
  7530  											{
  7531  												Name: "role",
  7532  											},
  7533  										},
  7534  									},
  7535  								},
  7536  								Body: &descriptor.Body{
  7537  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  7538  								},
  7539  							},
  7540  						},
  7541  					},
  7542  				},
  7543  			},
  7544  		},
  7545  	}
  7546  	reg := descriptor.NewRegistry()
  7547  	err := reg.Load(reqFromFile(&file))
  7548  	if err != nil {
  7549  		t.Fatalf("failed to reg.Load(): %v", err)
  7550  	}
  7551  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  7552  	if err != nil {
  7553  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  7554  	}
  7555  
  7556  	if got, want := len(result.Paths), 4; got != want {
  7557  		t.Fatalf("Results path length differed, got %d want %d", got, want)
  7558  	}
  7559  
  7560  	firstOp := result.getPathItemObject("/v1/{name}/{role}").Get
  7561  	if got, want := firstOp.OperationID, "Service1_Method1"; got != want {
  7562  		t.Fatalf("First operation id differed, got %s want %s", got, want)
  7563  	}
  7564  	if got, want := len(firstOp.Parameters), 3; got != want {
  7565  		t.Fatalf("First operation params length differed, got %d want %d", got, want)
  7566  	}
  7567  	if got, want := firstOp.Parameters[0].Name, "name"; got != want {
  7568  		t.Fatalf("First operation first param name differed, got %s want %s", got, want)
  7569  	}
  7570  	if got, want := firstOp.Parameters[0].Pattern, "organizations/[^/]+"; got != want {
  7571  		t.Fatalf("First operation first param pattern differed, got %s want %s", got, want)
  7572  	}
  7573  	if got, want := firstOp.Parameters[1].Name, "role"; got != want {
  7574  		t.Fatalf("First operation second param name differed, got %s want %s", got, want)
  7575  	}
  7576  	if got, want := firstOp.Parameters[1].Pattern, "roles/[^/]+"; got != want {
  7577  		t.Fatalf("First operation second param pattern differed, got %s want %s", got, want)
  7578  	}
  7579  	if got, want := firstOp.Parameters[2].In, "body"; got != want {
  7580  		t.Fatalf("First operation third param 'in' differed, got %s want %s", got, want)
  7581  	}
  7582  
  7583  	secondOp := result.getPathItemObject("/v1/{name" + pathParamUniqueSuffixDeliminator + "1}/{role}").Get
  7584  	if got, want := secondOp.OperationID, "Service1_Method2"; got != want {
  7585  		t.Fatalf("Second operation id differed, got %s want %s", got, want)
  7586  	}
  7587  	if got, want := len(secondOp.Parameters), 3; got != want {
  7588  		t.Fatalf("Second operation params length differed, got %d want %d", got, want)
  7589  	}
  7590  	if got, want := secondOp.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"1"; got != want {
  7591  		t.Fatalf("Second operation first param name differed, got %s want %s", got, want)
  7592  	}
  7593  	if got, want := secondOp.Parameters[0].Pattern, "users/[^/]+"; got != want {
  7594  		t.Fatalf("Second operation first param pattern differed, got %s want %s", got, want)
  7595  	}
  7596  	if got, want := secondOp.Parameters[1].Name, "role"; got != want {
  7597  		t.Fatalf("Second operation second param name differed, got %s want %s", got, want)
  7598  	}
  7599  	if got, want := secondOp.Parameters[1].Pattern, "roles/[^/]+"; got != want {
  7600  		t.Fatalf("Second operation second param pattern differed, got %s want %s", got, want)
  7601  	}
  7602  	if got, want := secondOp.Parameters[2].In, "body"; got != want {
  7603  		t.Fatalf("Second operation third param 'in' differed, got %s want %s", got, want)
  7604  	}
  7605  
  7606  	thirdOp := result.getPathItemObject("/v1/{name}/roles").Get
  7607  	if got, want := thirdOp.OperationID, "Service2_Method3"; got != want {
  7608  		t.Fatalf("Third operation id differed, got %s want %s", got, want)
  7609  	}
  7610  	if got, want := len(thirdOp.Parameters), 2; got != want {
  7611  		t.Fatalf("Third operation params length differed, got %d want %d", got, want)
  7612  	}
  7613  	if got, want := thirdOp.Parameters[0].Name, "name"; got != want {
  7614  		t.Fatalf("Third operation first param name differed, got %s want %s", got, want)
  7615  	}
  7616  	if got, want := thirdOp.Parameters[0].Pattern, "users/[^/]+"; got != want {
  7617  		t.Fatalf("Third operation first param pattern differed, got %s want %s", got, want)
  7618  	}
  7619  	if got, want := thirdOp.Parameters[1].In, "body"; got != want {
  7620  		t.Fatalf("Third operation second param 'in' differed, got %s want %s", got, want)
  7621  	}
  7622  
  7623  	forthOp := result.getPathItemObject("/v1/{name" + pathParamUniqueSuffixDeliminator + "2}/{role}").Get
  7624  	if got, want := forthOp.OperationID, "Service2_Method4"; got != want {
  7625  		t.Fatalf("Fourth operation id differed, got %s want %s", got, want)
  7626  	}
  7627  	if got, want := len(forthOp.Parameters), 3; got != want {
  7628  		t.Fatalf("Fourth operation params length differed, got %d want %d", got, want)
  7629  	}
  7630  	if got, want := forthOp.Parameters[0].Name, "name"+pathParamUniqueSuffixDeliminator+"2"; got != want {
  7631  		t.Fatalf("Fourth operation first param name differed, got %s want %s", got, want)
  7632  	}
  7633  	if got, want := forthOp.Parameters[0].Pattern, "groups/[^/]+"; got != want {
  7634  		t.Fatalf("Fourth operation first param pattern differed, got %s want %s", got, want)
  7635  	}
  7636  	if got, want := forthOp.Parameters[1].Name, "role"; got != want {
  7637  		t.Fatalf("Fourth operation second param name differed, got %s want %s", got, want)
  7638  	}
  7639  	if got, want := forthOp.Parameters[1].Pattern, "roles/[^/]+"; got != want {
  7640  		t.Fatalf("Fourth operation second param pattern differed, got %s want %s", got, want)
  7641  	}
  7642  	if got, want := forthOp.Parameters[2].In, "body"; got != want {
  7643  		t.Fatalf("Fourth operation second param 'in' differed, got %s want %s", got, want)
  7644  	}
  7645  }
  7646  
  7647  func Test_getReservedJsonName(t *testing.T) {
  7648  	type args struct {
  7649  		fieldName                     string
  7650  		messageNameToFieldsToJSONName map[string]map[string]string
  7651  		fieldNameToType               map[string]string
  7652  	}
  7653  	tests := []struct {
  7654  		name string
  7655  		args args
  7656  		want string
  7657  	}{
  7658  		{
  7659  			"test case 1: single dot use case",
  7660  			args{
  7661  				fieldName: "abc.a_1",
  7662  				messageNameToFieldsToJSONName: map[string]map[string]string{
  7663  					"Msg": {
  7664  						"a_1": "a1JSONNAME",
  7665  						"b_1": "b1JSONNAME",
  7666  					},
  7667  				},
  7668  				fieldNameToType: map[string]string{
  7669  					"abc": "pkg1.test.Msg",
  7670  					"bcd": "pkg1.test.Msg",
  7671  				},
  7672  			},
  7673  			"a1JSONNAME",
  7674  		},
  7675  		{
  7676  			"test case 2: single dot use case with no existing field",
  7677  			args{
  7678  				fieldName: "abc.d_1",
  7679  				messageNameToFieldsToJSONName: map[string]map[string]string{
  7680  					"Msg": {
  7681  						"a_1": "a1JSONNAME",
  7682  						"b_1": "b1JSONNAME",
  7683  					},
  7684  				},
  7685  				fieldNameToType: map[string]string{
  7686  					"abc": "pkg1.test.Msg",
  7687  					"bcd": "pkg1.test.Msg",
  7688  				},
  7689  			},
  7690  			"",
  7691  		},
  7692  		{
  7693  			"test case 3: double dot use case",
  7694  			args{
  7695  				fieldName: "pkg.abc.a_1",
  7696  				messageNameToFieldsToJSONName: map[string]map[string]string{
  7697  					"Msg": {
  7698  						"a_1": "a1JSONNAME",
  7699  						"b_1": "b1JSONNAME",
  7700  					},
  7701  				},
  7702  				fieldNameToType: map[string]string{
  7703  					"abc": "pkg1.test.Msg",
  7704  					"bcd": "pkg1.test.Msg",
  7705  				},
  7706  			},
  7707  			"a1JSONNAME",
  7708  		},
  7709  		{
  7710  			"test case 4: double dot use case with a not existed field",
  7711  			args{
  7712  				fieldName: "pkg.abc.c_1",
  7713  				messageNameToFieldsToJSONName: map[string]map[string]string{
  7714  					"Msg": {
  7715  						"a_1": "a1JSONNAME",
  7716  						"b_1": "b1JSONNAME",
  7717  					},
  7718  				},
  7719  				fieldNameToType: map[string]string{
  7720  					"abc": "pkg1.test.Msg",
  7721  					"bcd": "pkg1.test.Msg",
  7722  				},
  7723  			},
  7724  			"",
  7725  		},
  7726  	}
  7727  	for _, tt := range tests {
  7728  		t.Run(tt.name, func(t *testing.T) {
  7729  			if got := getReservedJSONName(tt.args.fieldName, tt.args.messageNameToFieldsToJSONName, tt.args.fieldNameToType); got != tt.want {
  7730  				t.Errorf("getReservedJSONName() = %v, want %v", got, tt.want)
  7731  			}
  7732  		})
  7733  	}
  7734  }
  7735  
  7736  func TestParseIncompleteSecurityRequirement(t *testing.T) {
  7737  	swagger := openapi_options.Swagger{
  7738  		Security: []*openapi_options.SecurityRequirement{
  7739  			{
  7740  				SecurityRequirement: map[string]*openapi_options.SecurityRequirement_SecurityRequirementValue{
  7741  					"key": nil,
  7742  				},
  7743  			},
  7744  		},
  7745  	}
  7746  	file := descriptor.File{
  7747  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  7748  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  7749  			Name:           proto.String("example.proto"),
  7750  			Package:        proto.String("example"),
  7751  			Options: &descriptorpb.FileOptions{
  7752  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  7753  			},
  7754  		},
  7755  	}
  7756  	proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), openapi_options.E_Openapiv2Swagger, &swagger)
  7757  	reg := descriptor.NewRegistry()
  7758  	err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  7759  	if err != nil {
  7760  		t.Errorf("failed to reg.Load(): %v", err)
  7761  		return
  7762  	}
  7763  	_, err = applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  7764  	if err == nil {
  7765  		t.Errorf("applyTemplate(%#v) did not error as expected", file)
  7766  		return
  7767  	}
  7768  }
  7769  
  7770  func TestSubPathParams(t *testing.T) {
  7771  	outerParams := []descriptor.Parameter{
  7772  		{
  7773  			FieldPath: []descriptor.FieldPathComponent{
  7774  				{
  7775  					Name: "prefix",
  7776  				},
  7777  				{
  7778  					Name: "first",
  7779  				},
  7780  			},
  7781  		},
  7782  		{
  7783  			FieldPath: []descriptor.FieldPathComponent{
  7784  				{
  7785  					Name: "prefix",
  7786  				},
  7787  				{
  7788  					Name: "second",
  7789  				},
  7790  				{
  7791  					Name: "deeper",
  7792  				},
  7793  			},
  7794  		},
  7795  		{
  7796  			FieldPath: []descriptor.FieldPathComponent{
  7797  				{
  7798  					Name: "otherprefix",
  7799  				},
  7800  				{
  7801  					Name: "third",
  7802  				},
  7803  			},
  7804  		},
  7805  	}
  7806  	subParams := subPathParams("prefix", outerParams)
  7807  
  7808  	if got, want := len(subParams), 2; got != want {
  7809  		t.Fatalf("Wrong number of path params, got %d want %d", got, want)
  7810  	}
  7811  	if got, want := len(subParams[0].FieldPath), 1; got != want {
  7812  		t.Fatalf("Wrong length of path param 0, got %d want %d", got, want)
  7813  	}
  7814  	if got, want := subParams[0].FieldPath[0].Name, "first"; got != want {
  7815  		t.Fatalf("Wrong path param 0, element 0, got %s want %s", got, want)
  7816  	}
  7817  	if got, want := len(subParams[1].FieldPath), 2; got != want {
  7818  		t.Fatalf("Wrong length of path param 1 got %d want %d", got, want)
  7819  	}
  7820  	if got, want := subParams[1].FieldPath[0].Name, "second"; got != want {
  7821  		t.Fatalf("Wrong path param 1, element 0, got %s want %s", got, want)
  7822  	}
  7823  	if got, want := subParams[1].FieldPath[1].Name, "deeper"; got != want {
  7824  		t.Fatalf("Wrong path param 1, element 1, got %s want %s", got, want)
  7825  	}
  7826  }
  7827  
  7828  func TestRenderServicesParameterDescriptionNoFieldBody(t *testing.T) {
  7829  
  7830  	optionsRaw :=
  7831  		`{
  7832  			"[grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema]": {
  7833  			  "jsonSchema": {
  7834  				"title": "aMessage title",
  7835  				"description": "aMessage description"
  7836  			  }
  7837  			}
  7838        	}`
  7839  
  7840  	options := &descriptorpb.MessageOptions{}
  7841  	err := protojson.Unmarshal([]byte(optionsRaw), options)
  7842  	if err != nil {
  7843  		t.Fatalf("Error while unmarshalling options: %s", err.Error())
  7844  	}
  7845  
  7846  	aMessageDesc := &descriptorpb.DescriptorProto{
  7847  		Name: proto.String("AMessage"),
  7848  		Field: []*descriptorpb.FieldDescriptorProto{
  7849  			{
  7850  				Name:   proto.String("project_id"),
  7851  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  7852  				Number: proto.Int32(1),
  7853  			},
  7854  			{
  7855  				Name:   proto.String("other_field"),
  7856  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  7857  				Number: proto.Int32(2),
  7858  			},
  7859  		},
  7860  		Options: options,
  7861  	}
  7862  	someResponseDesc := &descriptorpb.DescriptorProto{
  7863  		Name: proto.String("SomeResponse"),
  7864  	}
  7865  	aMeth := &descriptorpb.MethodDescriptorProto{
  7866  		Name:       proto.String("AMethod"),
  7867  		InputType:  proto.String("AMessage"),
  7868  		OutputType: proto.String("SomeResponse"),
  7869  	}
  7870  	svc := &descriptorpb.ServiceDescriptorProto{
  7871  		Name:   proto.String("Test"),
  7872  		Method: []*descriptorpb.MethodDescriptorProto{aMeth},
  7873  	}
  7874  	aMessage := &descriptor.Message{
  7875  		DescriptorProto: aMessageDesc,
  7876  	}
  7877  	someResponseMessage := &descriptor.Message{
  7878  		DescriptorProto: someResponseDesc,
  7879  	}
  7880  
  7881  	file := descriptor.File{
  7882  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  7883  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  7884  			Package:        proto.String("api"),
  7885  			Name:           proto.String("test.proto"),
  7886  			MessageType:    []*descriptorpb.DescriptorProto{aMessageDesc, someResponseDesc},
  7887  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  7888  			Options: &descriptorpb.FileOptions{
  7889  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  7890  			},
  7891  		},
  7892  		GoPkg: descriptor.GoPackage{
  7893  			Path: "example.com/path/to/example/example.pb",
  7894  			Name: "example_pb",
  7895  		},
  7896  		Messages: []*descriptor.Message{aMessage, someResponseMessage},
  7897  		Services: []*descriptor.Service{
  7898  			{
  7899  				ServiceDescriptorProto: svc,
  7900  				Methods: []*descriptor.Method{
  7901  					{
  7902  						MethodDescriptorProto: aMeth,
  7903  						RequestType:           aMessage,
  7904  						ResponseType:          someResponseMessage,
  7905  						Bindings: []*descriptor.Binding{
  7906  							{
  7907  								HTTPMethod: "POST",
  7908  								PathTmpl: httprule.Template{
  7909  									Version:  1,
  7910  									OpCodes:  []int{0, 0},
  7911  									Template: "/v1/projects/someotherpath",
  7912  								},
  7913  								Body: &descriptor.Body{},
  7914  							},
  7915  						},
  7916  					},
  7917  				},
  7918  			},
  7919  		},
  7920  	}
  7921  	reg := descriptor.NewRegistry()
  7922  	reg.SetUseJSONNamesForFields(true)
  7923  	err = reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  7924  	if err != nil {
  7925  		t.Fatalf("failed to reg.Load(): %v", err)
  7926  	}
  7927  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  7928  	if err != nil {
  7929  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  7930  	}
  7931  
  7932  	got := result.getPathItemObject("/v1/projects/someotherpath").Post.Parameters[0].Description
  7933  	want := "aMessage description"
  7934  
  7935  	if got != want {
  7936  		t.Fatalf("Wrong description for body parameter, got %s want %s", got, want)
  7937  	}
  7938  
  7939  }
  7940  
  7941  func TestRenderServicesWithBodyFieldNameInCamelCase(t *testing.T) {
  7942  	userDesc := &descriptorpb.DescriptorProto{
  7943  		Name: proto.String("User"),
  7944  		Field: []*descriptorpb.FieldDescriptorProto{
  7945  			{
  7946  				Name:   proto.String("name"),
  7947  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  7948  				Number: proto.Int32(1),
  7949  			},
  7950  			{
  7951  				Name:   proto.String("role"),
  7952  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  7953  				Number: proto.Int32(2),
  7954  			},
  7955  		},
  7956  	}
  7957  	updateDesc := &descriptorpb.DescriptorProto{
  7958  		Name: proto.String("UpdateUserRequest"),
  7959  		Field: []*descriptorpb.FieldDescriptorProto{
  7960  			{
  7961  				Name:     proto.String("user_object"),
  7962  				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  7963  				TypeName: proto.String(".example.User"),
  7964  				Number:   proto.Int32(1),
  7965  			},
  7966  		},
  7967  	}
  7968  	meth := &descriptorpb.MethodDescriptorProto{
  7969  		Name:       proto.String("UpdateUser"),
  7970  		InputType:  proto.String("UpdateUserRequest"),
  7971  		OutputType: proto.String("User"),
  7972  	}
  7973  	svc := &descriptorpb.ServiceDescriptorProto{
  7974  		Name:   proto.String("UserService"),
  7975  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  7976  	}
  7977  	userMsg := &descriptor.Message{
  7978  		DescriptorProto: userDesc,
  7979  	}
  7980  	updateMsg := &descriptor.Message{
  7981  		DescriptorProto: updateDesc,
  7982  	}
  7983  	nameField := &descriptor.Field{
  7984  		Message:              userMsg,
  7985  		FieldDescriptorProto: userMsg.GetField()[0],
  7986  	}
  7987  	nameField.JsonName = proto.String("name")
  7988  	roleField := &descriptor.Field{
  7989  		Message:              userMsg,
  7990  		FieldDescriptorProto: userMsg.GetField()[1],
  7991  	}
  7992  	roleField.JsonName = proto.String("role")
  7993  	userMsg.Fields = []*descriptor.Field{nameField, roleField}
  7994  	userField := &descriptor.Field{
  7995  		Message:              updateMsg,
  7996  		FieldMessage:         userMsg,
  7997  		FieldDescriptorProto: updateMsg.GetField()[0],
  7998  	}
  7999  	userField.JsonName = proto.String("userObject")
  8000  	updateMsg.Fields = []*descriptor.Field{userField}
  8001  
  8002  	file := descriptor.File{
  8003  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  8004  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  8005  			Package:        proto.String("example"),
  8006  			Name:           proto.String("user_service.proto"),
  8007  			MessageType:    []*descriptorpb.DescriptorProto{userDesc, updateDesc},
  8008  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  8009  			Options: &descriptorpb.FileOptions{
  8010  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  8011  			},
  8012  		},
  8013  		GoPkg: descriptor.GoPackage{
  8014  			Path: "example.com/path/to/example/example.pb",
  8015  			Name: "example_pb",
  8016  		},
  8017  		Messages: []*descriptor.Message{userMsg, updateMsg},
  8018  		Services: []*descriptor.Service{
  8019  			{
  8020  				ServiceDescriptorProto: svc,
  8021  				Methods: []*descriptor.Method{
  8022  					{
  8023  						MethodDescriptorProto: meth,
  8024  						RequestType:           updateMsg,
  8025  						ResponseType:          userMsg,
  8026  						Bindings: []*descriptor.Binding{
  8027  							{
  8028  								HTTPMethod: "POST",
  8029  								PathTmpl: httprule.Template{
  8030  									Version:  1,
  8031  									OpCodes:  []int{0, 0},
  8032  									Template: "/v1/users/{user_object.name}",
  8033  								},
  8034  								PathParams: []descriptor.Parameter{
  8035  									{
  8036  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  8037  											{
  8038  												Name: "user_object",
  8039  											},
  8040  											{
  8041  												Name: "name",
  8042  											},
  8043  										}),
  8044  										Target: nameField,
  8045  									},
  8046  								},
  8047  								Body: &descriptor.Body{
  8048  									FieldPath: []descriptor.FieldPathComponent{
  8049  										{
  8050  											Name:   "user_object",
  8051  											Target: userField,
  8052  										},
  8053  									},
  8054  								},
  8055  							},
  8056  						},
  8057  					},
  8058  				},
  8059  			},
  8060  		},
  8061  	}
  8062  	reg := descriptor.NewRegistry()
  8063  	reg.SetUseJSONNamesForFields(true)
  8064  	err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  8065  	if err != nil {
  8066  		t.Fatalf("failed to reg.Load(): %v", err)
  8067  	}
  8068  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  8069  	if err != nil {
  8070  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  8071  	}
  8072  
  8073  	paths := GetPaths(result)
  8074  	if got, want := len(paths), 1; got != want {
  8075  		t.Fatalf("Results path length differed, got %d want %d", got, want)
  8076  	}
  8077  
  8078  	if got, want := paths[0], "/v1/users/{userObject.name}"; got != want {
  8079  		t.Fatalf("Wrong results path, got %s want %s", got, want)
  8080  	}
  8081  
  8082  	var operation = *result.getPathItemObject("/v1/users/{userObject.name}").Post
  8083  	if got, want := len(operation.Parameters), 2; got != want {
  8084  		t.Fatalf("Parameters length differed, got %d want %d", got, want)
  8085  	}
  8086  
  8087  	if got, want := operation.Parameters[0].Name, "userObject.name"; got != want {
  8088  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8089  	}
  8090  
  8091  	if got, want := operation.Parameters[0].In, "path"; got != want {
  8092  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8093  	}
  8094  
  8095  	if got, want := operation.Parameters[1].Name, "userObject"; got != want {
  8096  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8097  	}
  8098  
  8099  	if got, want := operation.Parameters[1].In, "body"; got != want {
  8100  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8101  	}
  8102  
  8103  	// The body parameter should be inlined and not contain 'name', as this is a path parameter.
  8104  	schema := operation.Parameters[1].Schema
  8105  	if got, want := schema.Ref, ""; got != want {
  8106  		t.Fatalf("Wrong reference, got %s want %s", got, want)
  8107  	}
  8108  	props := schema.Properties
  8109  	if props == nil {
  8110  		t.Fatal("No properties on body parameter")
  8111  	}
  8112  	if got, want := len(*props), 1; got != want {
  8113  		t.Fatalf("Properties length differed, got %d want %d", got, want)
  8114  	}
  8115  	for _, v := range *props {
  8116  		if got, want := v.Key, "role"; got != want {
  8117  			t.Fatalf("Wrong key for property, got %s want %s", got, want)
  8118  		}
  8119  	}
  8120  }
  8121  
  8122  func TestRenderServicesWithBodyFieldHasFieldMask(t *testing.T) {
  8123  	userDesc := &descriptorpb.DescriptorProto{
  8124  		Name: proto.String("User"),
  8125  		Field: []*descriptorpb.FieldDescriptorProto{
  8126  			{
  8127  				Name:   proto.String("name"),
  8128  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8129  				Number: proto.Int32(1),
  8130  			},
  8131  			{
  8132  				Name:   proto.String("role"),
  8133  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8134  				Number: proto.Int32(2),
  8135  			},
  8136  		},
  8137  	}
  8138  	updateDesc := &descriptorpb.DescriptorProto{
  8139  		Name: proto.String("UpdateUserRequest"),
  8140  		Field: []*descriptorpb.FieldDescriptorProto{
  8141  			{
  8142  				Name:     proto.String("user_object"),
  8143  				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  8144  				TypeName: proto.String(".example.User"),
  8145  				Number:   proto.Int32(1),
  8146  			},
  8147  			{
  8148  				Name:     proto.String("update_mask"),
  8149  				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  8150  				TypeName: proto.String(".google.protobuf.FieldMask"),
  8151  				Number:   proto.Int32(2),
  8152  			},
  8153  		},
  8154  	}
  8155  
  8156  	meth := &descriptorpb.MethodDescriptorProto{
  8157  		Name:       proto.String("UpdateUser"),
  8158  		InputType:  proto.String("UpdateUserRequest"),
  8159  		OutputType: proto.String("User"),
  8160  	}
  8161  	svc := &descriptorpb.ServiceDescriptorProto{
  8162  		Name:   proto.String("UserService"),
  8163  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  8164  	}
  8165  	userMsg := &descriptor.Message{
  8166  		DescriptorProto: userDesc,
  8167  	}
  8168  	updateMsg := &descriptor.Message{
  8169  		DescriptorProto: updateDesc,
  8170  	}
  8171  	nameField := &descriptor.Field{
  8172  		Message:              userMsg,
  8173  		FieldDescriptorProto: userMsg.GetField()[0],
  8174  	}
  8175  	nameField.JsonName = proto.String("name")
  8176  	roleField := &descriptor.Field{
  8177  		Message:              userMsg,
  8178  		FieldDescriptorProto: userMsg.GetField()[1],
  8179  	}
  8180  	roleField.JsonName = proto.String("role")
  8181  	userMsg.Fields = []*descriptor.Field{nameField, roleField}
  8182  	userField := &descriptor.Field{
  8183  		Message:              updateMsg,
  8184  		FieldMessage:         userMsg,
  8185  		FieldDescriptorProto: updateMsg.GetField()[0],
  8186  	}
  8187  	userField.JsonName = proto.String("userObject")
  8188  	updateMaskField := &descriptor.Field{
  8189  		Message:              updateMsg,
  8190  		FieldDescriptorProto: updateMsg.GetField()[1],
  8191  	}
  8192  	updateMaskField.JsonName = proto.String("updateMask")
  8193  	updateMsg.Fields = []*descriptor.Field{userField, updateMaskField}
  8194  
  8195  	file := descriptor.File{
  8196  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  8197  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  8198  			Package:        proto.String("example"),
  8199  			Name:           proto.String("user_service.proto"),
  8200  			Dependency:     []string{"google/well_known.proto"},
  8201  			MessageType:    []*descriptorpb.DescriptorProto{userDesc, updateDesc},
  8202  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  8203  			Options: &descriptorpb.FileOptions{
  8204  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  8205  			},
  8206  		},
  8207  		GoPkg: descriptor.GoPackage{
  8208  			Path: "example.com/path/to/example/example.pb",
  8209  			Name: "example_pb",
  8210  		},
  8211  		Messages: []*descriptor.Message{userMsg, updateMsg},
  8212  		Services: []*descriptor.Service{
  8213  			{
  8214  				ServiceDescriptorProto: svc,
  8215  				Methods: []*descriptor.Method{
  8216  					{
  8217  						MethodDescriptorProto: meth,
  8218  						RequestType:           updateMsg,
  8219  						ResponseType:          userMsg,
  8220  						Bindings: []*descriptor.Binding{
  8221  							{
  8222  								HTTPMethod: "PATCH",
  8223  								PathTmpl: httprule.Template{
  8224  									Version:  1,
  8225  									OpCodes:  []int{0, 0},
  8226  									Template: "/v1/users/{user_object.name}",
  8227  								},
  8228  								PathParams: []descriptor.Parameter{
  8229  									{
  8230  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  8231  											{
  8232  												Name: "user_object",
  8233  											},
  8234  											{
  8235  												Name: "name",
  8236  											},
  8237  										}),
  8238  										Target: nameField,
  8239  									},
  8240  								},
  8241  								Body: &descriptor.Body{
  8242  									FieldPath: []descriptor.FieldPathComponent{
  8243  										{
  8244  											Name:   "user_object",
  8245  											Target: userField,
  8246  										},
  8247  									},
  8248  								},
  8249  							},
  8250  						},
  8251  					},
  8252  				},
  8253  			},
  8254  		},
  8255  	}
  8256  	reg := descriptor.NewRegistry()
  8257  	reg.SetUseJSONNamesForFields(true)
  8258  	reg.SetAllowPatchFeature(true)
  8259  	err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{
  8260  		{
  8261  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  8262  			Name:           proto.String("google/well_known.proto"),
  8263  			Package:        proto.String("google.protobuf"),
  8264  			Dependency:     []string{},
  8265  			MessageType: []*descriptorpb.DescriptorProto{
  8266  				protodesc.ToDescriptorProto((&field_mask.FieldMask{}).ProtoReflect().Descriptor()),
  8267  			},
  8268  			Service: []*descriptorpb.ServiceDescriptorProto{},
  8269  			Options: &descriptorpb.FileOptions{
  8270  				GoPackage: proto.String("google/well_known"),
  8271  			},
  8272  		},
  8273  		file.FileDescriptorProto,
  8274  	}})
  8275  	if err != nil {
  8276  		t.Fatalf("failed to reg.Load(): %v", err)
  8277  	}
  8278  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  8279  	if err != nil {
  8280  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  8281  	}
  8282  
  8283  	paths := GetPaths(result)
  8284  	if got, want := len(paths), 1; got != want {
  8285  		t.Fatalf("Results path length differed, got %d want %d", got, want)
  8286  	}
  8287  
  8288  	if got, want := paths[0], "/v1/users/{userObject.name}"; got != want {
  8289  		t.Fatalf("Wrong results path, got %s want %s", got, want)
  8290  	}
  8291  
  8292  	var operation = *result.getPathItemObject("/v1/users/{userObject.name}").Patch
  8293  	if got, want := len(operation.Parameters), 2; got != want {
  8294  		t.Fatalf("Parameters length differed, got %d want %d", got, want)
  8295  	}
  8296  
  8297  	if got, want := operation.Parameters[0].Name, "userObject.name"; got != want {
  8298  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8299  	}
  8300  
  8301  	if got, want := operation.Parameters[0].In, "path"; got != want {
  8302  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8303  	}
  8304  
  8305  	if got, want := operation.Parameters[1].Name, "userObject"; got != want {
  8306  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8307  	}
  8308  
  8309  	if got, want := operation.Parameters[1].In, "body"; got != want {
  8310  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8311  	}
  8312  }
  8313  
  8314  func TestRenderServicesWithColonInPath(t *testing.T) {
  8315  	jsonSchema := &openapi_options.JSONSchema{
  8316  		FieldConfiguration: &openapi_options.JSONSchema_FieldConfiguration{
  8317  			PathParamName: "overrideField",
  8318  		},
  8319  	}
  8320  	var fieldOptions = new(descriptorpb.FieldOptions)
  8321  	proto.SetExtension(fieldOptions, openapi_options.E_Openapiv2Field, jsonSchema)
  8322  
  8323  	reqDesc := &descriptorpb.DescriptorProto{
  8324  		Name: proto.String("MyRequest"),
  8325  		Field: []*descriptorpb.FieldDescriptorProto{
  8326  			{
  8327  				Name:    proto.String("field"),
  8328  				Type:    descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8329  				Number:  proto.Int32(1),
  8330  				Options: fieldOptions,
  8331  			},
  8332  		},
  8333  	}
  8334  	resDesc := &descriptorpb.DescriptorProto{
  8335  		Name: proto.String("MyResponse"),
  8336  		Field: []*descriptorpb.FieldDescriptorProto{
  8337  			{
  8338  				Name:   proto.String("field"),
  8339  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8340  				Number: proto.Int32(1),
  8341  			},
  8342  		},
  8343  	}
  8344  	meth := &descriptorpb.MethodDescriptorProto{
  8345  		Name:       proto.String("MyMethod"),
  8346  		InputType:  proto.String("MyRequest"),
  8347  		OutputType: proto.String("MyResponse"),
  8348  	}
  8349  	svc := &descriptorpb.ServiceDescriptorProto{
  8350  		Name:   proto.String("MyService"),
  8351  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  8352  	}
  8353  	reqMsg := &descriptor.Message{
  8354  		DescriptorProto: reqDesc,
  8355  	}
  8356  	resMsg := &descriptor.Message{
  8357  		DescriptorProto: resDesc,
  8358  	}
  8359  	reqField := &descriptor.Field{
  8360  		Message:              reqMsg,
  8361  		FieldDescriptorProto: reqMsg.GetField()[0],
  8362  	}
  8363  	resField := &descriptor.Field{
  8364  		Message:              resMsg,
  8365  		FieldDescriptorProto: resMsg.GetField()[0],
  8366  	}
  8367  	reqField.JsonName = proto.String("field")
  8368  	resField.JsonName = proto.String("field")
  8369  	reqMsg.Fields = []*descriptor.Field{reqField}
  8370  	resMsg.Fields = []*descriptor.Field{resField}
  8371  
  8372  	file := descriptor.File{
  8373  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  8374  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  8375  			Package:        proto.String("example"),
  8376  			Name:           proto.String(",my_service.proto"),
  8377  			MessageType:    []*descriptorpb.DescriptorProto{reqDesc, resDesc},
  8378  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  8379  			Options: &descriptorpb.FileOptions{
  8380  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  8381  			},
  8382  		},
  8383  		GoPkg: descriptor.GoPackage{
  8384  			Path: "example.com/path/to/example/example.pb",
  8385  			Name: "example_pb",
  8386  		},
  8387  		Messages: []*descriptor.Message{reqMsg, resMsg},
  8388  		Services: []*descriptor.Service{
  8389  			{
  8390  				ServiceDescriptorProto: svc,
  8391  				Methods: []*descriptor.Method{
  8392  					{
  8393  						MethodDescriptorProto: meth,
  8394  						RequestType:           reqMsg,
  8395  						ResponseType:          resMsg,
  8396  						Bindings: []*descriptor.Binding{
  8397  							{
  8398  								HTTPMethod: "POST",
  8399  								PathTmpl: httprule.Template{
  8400  									Version:  1,
  8401  									OpCodes:  []int{0, 0},
  8402  									Template: "/my/{field}:foo",
  8403  								},
  8404  								PathParams: []descriptor.Parameter{
  8405  									{
  8406  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  8407  											{
  8408  												Name: "field",
  8409  											},
  8410  										}),
  8411  										Target: reqField,
  8412  									},
  8413  								},
  8414  								Body: &descriptor.Body{
  8415  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  8416  								},
  8417  							},
  8418  						},
  8419  					},
  8420  				},
  8421  			},
  8422  		},
  8423  	}
  8424  	reg := descriptor.NewRegistry()
  8425  	reg.SetUseJSONNamesForFields(true)
  8426  	err := reg.Load(reqFromFile(&file))
  8427  	if err != nil {
  8428  		t.Fatalf("failed to reg.Load(): %v", err)
  8429  	}
  8430  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  8431  	if err != nil {
  8432  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  8433  	}
  8434  
  8435  	paths := GetPaths(result)
  8436  	if got, want := len(paths), 1; got != want {
  8437  		t.Fatalf("Results path length differed, got %d want %d", got, want)
  8438  	}
  8439  
  8440  	if got, want := paths[0], "/my/{overrideField}:foo"; got != want {
  8441  		t.Fatalf("Wrong results path, got %s want %s", got, want)
  8442  	}
  8443  
  8444  	var operation = *result.getPathItemObject("/my/{overrideField}:foo").Post
  8445  	if got, want := len(operation.Parameters), 2; got != want {
  8446  		t.Fatalf("Parameters length differed, got %d want %d", got, want)
  8447  	}
  8448  
  8449  	if got, want := operation.Parameters[0].Name, "overrideField"; got != want {
  8450  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8451  	}
  8452  
  8453  	if got, want := operation.Parameters[0].In, "path"; got != want {
  8454  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8455  	}
  8456  
  8457  	if got, want := operation.Parameters[0].Type, "string"; got != want {
  8458  		t.Fatalf("Wrong parameter type, got %s want %s", got, want)
  8459  	}
  8460  
  8461  	if got, want := operation.Parameters[1].Name, "body"; got != want {
  8462  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8463  	}
  8464  
  8465  	if got, want := operation.Parameters[1].In, "body"; got != want {
  8466  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8467  	}
  8468  }
  8469  
  8470  func TestRenderServicesWithDoubleColonInPath(t *testing.T) {
  8471  	reqDesc := &descriptorpb.DescriptorProto{
  8472  		Name: proto.String("MyRequest"),
  8473  		Field: []*descriptorpb.FieldDescriptorProto{
  8474  			{
  8475  				Name:   proto.String("field"),
  8476  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8477  				Number: proto.Int32(1),
  8478  			},
  8479  		},
  8480  	}
  8481  	resDesc := &descriptorpb.DescriptorProto{
  8482  		Name: proto.String("MyResponse"),
  8483  		Field: []*descriptorpb.FieldDescriptorProto{
  8484  			{
  8485  				Name:   proto.String("field"),
  8486  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8487  				Number: proto.Int32(1),
  8488  			},
  8489  		},
  8490  	}
  8491  	meth := &descriptorpb.MethodDescriptorProto{
  8492  		Name:       proto.String("MyMethod"),
  8493  		InputType:  proto.String("MyRequest"),
  8494  		OutputType: proto.String("MyResponse"),
  8495  	}
  8496  	svc := &descriptorpb.ServiceDescriptorProto{
  8497  		Name:   proto.String("MyService"),
  8498  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  8499  	}
  8500  	reqMsg := &descriptor.Message{
  8501  		DescriptorProto: reqDesc,
  8502  	}
  8503  	resMsg := &descriptor.Message{
  8504  		DescriptorProto: resDesc,
  8505  	}
  8506  	reqField := &descriptor.Field{
  8507  		Message:              reqMsg,
  8508  		FieldDescriptorProto: reqMsg.GetField()[0],
  8509  	}
  8510  	resField := &descriptor.Field{
  8511  		Message:              resMsg,
  8512  		FieldDescriptorProto: resMsg.GetField()[0],
  8513  	}
  8514  	reqField.JsonName = proto.String("field")
  8515  	resField.JsonName = proto.String("field")
  8516  	reqMsg.Fields = []*descriptor.Field{reqField}
  8517  	resMsg.Fields = []*descriptor.Field{resField}
  8518  
  8519  	file := descriptor.File{
  8520  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  8521  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  8522  			Package:        proto.String("example"),
  8523  			Name:           proto.String(",my_service.proto"),
  8524  			MessageType:    []*descriptorpb.DescriptorProto{reqDesc, resDesc},
  8525  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  8526  			Options: &descriptorpb.FileOptions{
  8527  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  8528  			},
  8529  		},
  8530  		GoPkg: descriptor.GoPackage{
  8531  			Path: "example.com/path/to/example/example.pb",
  8532  			Name: "example_pb",
  8533  		},
  8534  		Messages: []*descriptor.Message{reqMsg, resMsg},
  8535  		Services: []*descriptor.Service{
  8536  			{
  8537  				ServiceDescriptorProto: svc,
  8538  				Methods: []*descriptor.Method{
  8539  					{
  8540  						MethodDescriptorProto: meth,
  8541  						RequestType:           reqMsg,
  8542  						ResponseType:          resMsg,
  8543  						Bindings: []*descriptor.Binding{
  8544  							{
  8545  								HTTPMethod: "POST",
  8546  								PathTmpl: httprule.Template{
  8547  									Version:  1,
  8548  									OpCodes:  []int{0, 0},
  8549  									Template: "/my/{field}:foo:bar",
  8550  								},
  8551  								PathParams: []descriptor.Parameter{
  8552  									{
  8553  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  8554  											{
  8555  												Name: "field",
  8556  											},
  8557  										}),
  8558  										Target: reqField,
  8559  									},
  8560  								},
  8561  								Body: &descriptor.Body{
  8562  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  8563  								},
  8564  							},
  8565  						},
  8566  					},
  8567  				},
  8568  			},
  8569  		},
  8570  	}
  8571  	reg := descriptor.NewRegistry()
  8572  	reg.SetUseJSONNamesForFields(true)
  8573  	err := reg.Load(reqFromFile(&file))
  8574  	if err != nil {
  8575  		t.Fatalf("failed to reg.Load(): %v", err)
  8576  	}
  8577  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  8578  	if err != nil {
  8579  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  8580  	}
  8581  
  8582  	paths := GetPaths(result)
  8583  	if got, want := len(paths), 1; got != want {
  8584  		t.Fatalf("Results path length differed, got %d want %d", got, want)
  8585  	}
  8586  
  8587  	if got, want := paths[0], "/my/{field}:foo:bar"; got != want {
  8588  		t.Fatalf("Wrong results path, got %s want %s", got, want)
  8589  	}
  8590  
  8591  	var operation = *result.getPathItemObject("/my/{field}:foo:bar").Post
  8592  	if got, want := len(operation.Parameters), 2; got != want {
  8593  		t.Fatalf("Parameters length differed, got %d want %d", got, want)
  8594  	}
  8595  
  8596  	if got, want := operation.Parameters[0].Name, "field"; got != want {
  8597  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8598  	}
  8599  
  8600  	if got, want := operation.Parameters[0].In, "path"; got != want {
  8601  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8602  	}
  8603  
  8604  	if got, want := operation.Parameters[0].Type, "string"; got != want {
  8605  		t.Fatalf("Wrong parameter type, got %s want %s", got, want)
  8606  	}
  8607  
  8608  	if got, want := operation.Parameters[1].Name, "body"; got != want {
  8609  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8610  	}
  8611  
  8612  	if got, want := operation.Parameters[1].In, "body"; got != want {
  8613  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8614  	}
  8615  }
  8616  
  8617  func TestRenderServicesWithColonLastInPath(t *testing.T) {
  8618  	reqDesc := &descriptorpb.DescriptorProto{
  8619  		Name: proto.String("MyRequest"),
  8620  		Field: []*descriptorpb.FieldDescriptorProto{
  8621  			{
  8622  				Name:   proto.String("field"),
  8623  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8624  				Number: proto.Int32(1),
  8625  			},
  8626  		},
  8627  	}
  8628  	resDesc := &descriptorpb.DescriptorProto{
  8629  		Name: proto.String("MyResponse"),
  8630  		Field: []*descriptorpb.FieldDescriptorProto{
  8631  			{
  8632  				Name:   proto.String("field"),
  8633  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8634  				Number: proto.Int32(1),
  8635  			},
  8636  		},
  8637  	}
  8638  	meth := &descriptorpb.MethodDescriptorProto{
  8639  		Name:       proto.String("MyMethod"),
  8640  		InputType:  proto.String("MyRequest"),
  8641  		OutputType: proto.String("MyResponse"),
  8642  	}
  8643  	svc := &descriptorpb.ServiceDescriptorProto{
  8644  		Name:   proto.String("MyService"),
  8645  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  8646  	}
  8647  	reqMsg := &descriptor.Message{
  8648  		DescriptorProto: reqDesc,
  8649  	}
  8650  	resMsg := &descriptor.Message{
  8651  		DescriptorProto: resDesc,
  8652  	}
  8653  	reqField := &descriptor.Field{
  8654  		Message:              reqMsg,
  8655  		FieldDescriptorProto: reqMsg.GetField()[0],
  8656  	}
  8657  	resField := &descriptor.Field{
  8658  		Message:              resMsg,
  8659  		FieldDescriptorProto: resMsg.GetField()[0],
  8660  	}
  8661  	reqField.JsonName = proto.String("field")
  8662  	resField.JsonName = proto.String("field")
  8663  	reqMsg.Fields = []*descriptor.Field{reqField}
  8664  	resMsg.Fields = []*descriptor.Field{resField}
  8665  
  8666  	file := descriptor.File{
  8667  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  8668  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  8669  			Package:        proto.String("example"),
  8670  			Name:           proto.String(",my_service.proto"),
  8671  			MessageType:    []*descriptorpb.DescriptorProto{reqDesc, resDesc},
  8672  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  8673  			Options: &descriptorpb.FileOptions{
  8674  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  8675  			},
  8676  		},
  8677  		GoPkg: descriptor.GoPackage{
  8678  			Path: "example.com/path/to/example/example.pb",
  8679  			Name: "example_pb",
  8680  		},
  8681  		Messages: []*descriptor.Message{reqMsg, resMsg},
  8682  		Services: []*descriptor.Service{
  8683  			{
  8684  				ServiceDescriptorProto: svc,
  8685  				Methods: []*descriptor.Method{
  8686  					{
  8687  						MethodDescriptorProto: meth,
  8688  						RequestType:           reqMsg,
  8689  						ResponseType:          resMsg,
  8690  						Bindings: []*descriptor.Binding{
  8691  							{
  8692  								HTTPMethod: "POST",
  8693  								PathTmpl: httprule.Template{
  8694  									Version:  1,
  8695  									OpCodes:  []int{0, 0},
  8696  									Template: "/my/{field}:",
  8697  								},
  8698  								PathParams: []descriptor.Parameter{
  8699  									{
  8700  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  8701  											{
  8702  												Name: "field",
  8703  											},
  8704  										}),
  8705  										Target: reqField,
  8706  									},
  8707  								},
  8708  								Body: &descriptor.Body{
  8709  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  8710  								},
  8711  							},
  8712  						},
  8713  					},
  8714  				},
  8715  			},
  8716  		},
  8717  	}
  8718  	reg := descriptor.NewRegistry()
  8719  	reg.SetUseJSONNamesForFields(true)
  8720  	err := reg.Load(reqFromFile(&file))
  8721  	if err != nil {
  8722  		t.Fatalf("failed to reg.Load(): %v", err)
  8723  	}
  8724  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  8725  	if err != nil {
  8726  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  8727  	}
  8728  
  8729  	paths := GetPaths(result)
  8730  	if got, want := len(paths), 1; got != want {
  8731  		t.Fatalf("Results path length differed, got %d want %d", got, want)
  8732  	}
  8733  
  8734  	if got, want := paths[0], "/my/{field}:"; got != want {
  8735  		t.Fatalf("Wrong results path, got %s want %s", got, want)
  8736  	}
  8737  
  8738  	var operation = *result.getPathItemObject("/my/{field}:").Post
  8739  	if got, want := len(operation.Parameters), 2; got != want {
  8740  		t.Fatalf("Parameters length differed, got %d want %d", got, want)
  8741  	}
  8742  
  8743  	if got, want := operation.Parameters[0].Name, "field"; got != want {
  8744  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8745  	}
  8746  
  8747  	if got, want := operation.Parameters[0].In, "path"; got != want {
  8748  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8749  	}
  8750  
  8751  	if got, want := operation.Parameters[0].Type, "string"; got != want {
  8752  		t.Fatalf("Wrong parameter type, got %s want %s", got, want)
  8753  	}
  8754  
  8755  	if got, want := operation.Parameters[1].Name, "body"; got != want {
  8756  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8757  	}
  8758  
  8759  	if got, want := operation.Parameters[1].In, "body"; got != want {
  8760  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8761  	}
  8762  }
  8763  
  8764  func TestRenderServicesWithColonInSegment(t *testing.T) {
  8765  	reqDesc := &descriptorpb.DescriptorProto{
  8766  		Name: proto.String("MyRequest"),
  8767  		Field: []*descriptorpb.FieldDescriptorProto{
  8768  			{
  8769  				Name:   proto.String("field"),
  8770  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8771  				Number: proto.Int32(1),
  8772  			},
  8773  		},
  8774  	}
  8775  	resDesc := &descriptorpb.DescriptorProto{
  8776  		Name: proto.String("MyResponse"),
  8777  		Field: []*descriptorpb.FieldDescriptorProto{
  8778  			{
  8779  				Name:   proto.String("field"),
  8780  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  8781  				Number: proto.Int32(1),
  8782  			},
  8783  		},
  8784  	}
  8785  	meth := &descriptorpb.MethodDescriptorProto{
  8786  		Name:       proto.String("MyMethod"),
  8787  		InputType:  proto.String("MyRequest"),
  8788  		OutputType: proto.String("MyResponse"),
  8789  	}
  8790  	svc := &descriptorpb.ServiceDescriptorProto{
  8791  		Name:   proto.String("MyService"),
  8792  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  8793  	}
  8794  	reqMsg := &descriptor.Message{
  8795  		DescriptorProto: reqDesc,
  8796  	}
  8797  	resMsg := &descriptor.Message{
  8798  		DescriptorProto: resDesc,
  8799  	}
  8800  	reqField := &descriptor.Field{
  8801  		Message:              reqMsg,
  8802  		FieldDescriptorProto: reqMsg.GetField()[0],
  8803  	}
  8804  	resField := &descriptor.Field{
  8805  		Message:              resMsg,
  8806  		FieldDescriptorProto: resMsg.GetField()[0],
  8807  	}
  8808  	reqField.JsonName = proto.String("field")
  8809  	resField.JsonName = proto.String("field")
  8810  	reqMsg.Fields = []*descriptor.Field{reqField}
  8811  	resMsg.Fields = []*descriptor.Field{resField}
  8812  
  8813  	file := descriptor.File{
  8814  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  8815  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  8816  			Package:        proto.String("example"),
  8817  			Name:           proto.String(",my_service.proto"),
  8818  			MessageType:    []*descriptorpb.DescriptorProto{reqDesc, resDesc},
  8819  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  8820  			Options: &descriptorpb.FileOptions{
  8821  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  8822  			},
  8823  		},
  8824  		GoPkg: descriptor.GoPackage{
  8825  			Path: "example.com/path/to/example/example.pb",
  8826  			Name: "example_pb",
  8827  		},
  8828  		Messages: []*descriptor.Message{reqMsg, resMsg},
  8829  		Services: []*descriptor.Service{
  8830  			{
  8831  				ServiceDescriptorProto: svc,
  8832  				Methods: []*descriptor.Method{
  8833  					{
  8834  						MethodDescriptorProto: meth,
  8835  						RequestType:           reqMsg,
  8836  						ResponseType:          resMsg,
  8837  						Bindings: []*descriptor.Binding{
  8838  							{
  8839  								HTTPMethod: "POST",
  8840  								PathTmpl: httprule.Template{
  8841  									Version:  1,
  8842  									OpCodes:  []int{0, 0},
  8843  									Template: "/my/{field=segment/wi:th}",
  8844  								},
  8845  								PathParams: []descriptor.Parameter{
  8846  									{
  8847  										FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  8848  											{
  8849  												Name: "field",
  8850  											},
  8851  										}),
  8852  										Target: reqField,
  8853  									},
  8854  								},
  8855  								Body: &descriptor.Body{
  8856  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  8857  								},
  8858  							},
  8859  						},
  8860  					},
  8861  				},
  8862  			},
  8863  		},
  8864  	}
  8865  	reg := descriptor.NewRegistry()
  8866  	reg.SetUseJSONNamesForFields(true)
  8867  	err := reg.Load(reqFromFile(&file))
  8868  	if err != nil {
  8869  		t.Fatalf("failed to reg.Load(): %v", err)
  8870  	}
  8871  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  8872  	if err != nil {
  8873  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  8874  	}
  8875  
  8876  	paths := GetPaths(result)
  8877  	if got, want := len(paths), 1; got != want {
  8878  		t.Fatalf("Results path length differed, got %d want %d", got, want)
  8879  	}
  8880  
  8881  	if got, want := paths[0], "/my/{field}"; got != want {
  8882  		t.Fatalf("Wrong results path, got %s want %s", got, want)
  8883  	}
  8884  
  8885  	var operation = *result.getPathItemObject("/my/{field}").Post
  8886  	if got, want := len(operation.Parameters), 2; got != want {
  8887  		t.Fatalf("Parameters length differed, got %d want %d", got, want)
  8888  	}
  8889  
  8890  	if got, want := operation.Parameters[0].Name, "field"; got != want {
  8891  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8892  	}
  8893  
  8894  	if got, want := operation.Parameters[0].In, "path"; got != want {
  8895  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8896  	}
  8897  
  8898  	if got, want := operation.Parameters[0].Type, "string"; got != want {
  8899  		t.Fatalf("Wrong parameter type, got %s want %s", got, want)
  8900  	}
  8901  
  8902  	if got, want := operation.Parameters[1].Name, "body"; got != want {
  8903  		t.Fatalf("Wrong parameter name, got %s want %s", got, want)
  8904  	}
  8905  
  8906  	if got, want := operation.Parameters[1].In, "body"; got != want {
  8907  		t.Fatalf("Wrong parameter location, got %s want %s", got, want)
  8908  	}
  8909  }
  8910  
  8911  func TestRenderServiceWithHeaderParameters(t *testing.T) {
  8912  	file := func() descriptor.File {
  8913  		msgdesc := &descriptorpb.DescriptorProto{
  8914  			Name: proto.String("ExampleMessage"),
  8915  		}
  8916  
  8917  		meth := &descriptorpb.MethodDescriptorProto{
  8918  			Name:       proto.String("Example"),
  8919  			InputType:  proto.String("ExampleMessage"),
  8920  			OutputType: proto.String("ExampleMessage"),
  8921  			Options:    &descriptorpb.MethodOptions{},
  8922  		}
  8923  
  8924  		svc := &descriptorpb.ServiceDescriptorProto{
  8925  			Name:   proto.String("ExampleService"),
  8926  			Method: []*descriptorpb.MethodDescriptorProto{meth},
  8927  		}
  8928  
  8929  		msg := &descriptor.Message{
  8930  			DescriptorProto: msgdesc,
  8931  		}
  8932  
  8933  		return descriptor.File{
  8934  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  8935  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  8936  				Name:           proto.String("example.proto"),
  8937  				Package:        proto.String("example"),
  8938  				MessageType:    []*descriptorpb.DescriptorProto{msgdesc},
  8939  				Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  8940  				Options: &descriptorpb.FileOptions{
  8941  					GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  8942  				},
  8943  			},
  8944  			GoPkg: descriptor.GoPackage{
  8945  				Path: "example.com/path/to/example/example.pb",
  8946  				Name: "example_pb",
  8947  			},
  8948  			Messages: []*descriptor.Message{msg},
  8949  			Services: []*descriptor.Service{
  8950  				{
  8951  					ServiceDescriptorProto: svc,
  8952  					Methods: []*descriptor.Method{
  8953  						{
  8954  							MethodDescriptorProto: meth,
  8955  							RequestType:           msg,
  8956  							ResponseType:          msg,
  8957  							Bindings: []*descriptor.Binding{
  8958  								{
  8959  									HTTPMethod: "GET",
  8960  									PathTmpl: httprule.Template{
  8961  										Version:  1,
  8962  										OpCodes:  []int{0, 0},
  8963  										Template: "/v1/echo",
  8964  									},
  8965  								},
  8966  							},
  8967  						},
  8968  					},
  8969  				},
  8970  			},
  8971  		}
  8972  	}
  8973  
  8974  	type test struct {
  8975  		file             func() descriptor.File
  8976  		openapiOperation *openapi_options.Operation
  8977  		parameters       openapiParametersObject
  8978  	}
  8979  
  8980  	tests := map[string]*test{
  8981  		"type string": {
  8982  			file: file,
  8983  			openapiOperation: &openapi_options.Operation{
  8984  				Parameters: &openapi_options.Parameters{
  8985  					Headers: []*openapi_options.HeaderParameter{
  8986  						{
  8987  							Name: "X-Custom-Header",
  8988  							Type: openapi_options.HeaderParameter_STRING,
  8989  						},
  8990  					},
  8991  				},
  8992  			},
  8993  			parameters: openapiParametersObject{
  8994  				{
  8995  					Name: "X-Custom-Header",
  8996  					In:   "header",
  8997  					Type: "string",
  8998  				},
  8999  			},
  9000  		},
  9001  		"type string with format": {
  9002  			file: file,
  9003  			openapiOperation: &openapi_options.Operation{
  9004  				Parameters: &openapi_options.Parameters{
  9005  					Headers: []*openapi_options.HeaderParameter{
  9006  						{
  9007  							Name:   "X-Custom-Header",
  9008  							Type:   openapi_options.HeaderParameter_STRING,
  9009  							Format: "uuid",
  9010  						},
  9011  					},
  9012  				},
  9013  			},
  9014  			parameters: openapiParametersObject{
  9015  				{
  9016  					Name:   "X-Custom-Header",
  9017  					In:     "header",
  9018  					Type:   "string",
  9019  					Format: "uuid",
  9020  				},
  9021  			},
  9022  		},
  9023  		"type integer": {
  9024  			file: file,
  9025  			openapiOperation: &openapi_options.Operation{
  9026  				Parameters: &openapi_options.Parameters{
  9027  					Headers: []*openapi_options.HeaderParameter{
  9028  						{
  9029  							Name: "X-Custom-Header",
  9030  							Type: openapi_options.HeaderParameter_INTEGER,
  9031  						},
  9032  					},
  9033  				},
  9034  			},
  9035  			parameters: openapiParametersObject{
  9036  				{
  9037  					Name: "X-Custom-Header",
  9038  					In:   "header",
  9039  					Type: "integer",
  9040  				},
  9041  			},
  9042  		},
  9043  		"type number": {
  9044  			file: file,
  9045  			openapiOperation: &openapi_options.Operation{
  9046  				Parameters: &openapi_options.Parameters{
  9047  					Headers: []*openapi_options.HeaderParameter{
  9048  						{
  9049  							Name: "X-Custom-Header",
  9050  							Type: openapi_options.HeaderParameter_NUMBER,
  9051  						},
  9052  					},
  9053  				},
  9054  			},
  9055  			parameters: openapiParametersObject{
  9056  				{
  9057  					Name: "X-Custom-Header",
  9058  					In:   "header",
  9059  					Type: "number",
  9060  				},
  9061  			},
  9062  		},
  9063  		"type boolean": {
  9064  			file: file,
  9065  			openapiOperation: &openapi_options.Operation{
  9066  				Parameters: &openapi_options.Parameters{
  9067  					Headers: []*openapi_options.HeaderParameter{
  9068  						{
  9069  							Name: "X-Custom-Header",
  9070  							Type: openapi_options.HeaderParameter_BOOLEAN,
  9071  						},
  9072  					},
  9073  				},
  9074  			},
  9075  			parameters: openapiParametersObject{
  9076  				{
  9077  					Name: "X-Custom-Header",
  9078  					In:   "header",
  9079  					Type: "boolean",
  9080  				},
  9081  			},
  9082  		},
  9083  		"header required": {
  9084  			file: file,
  9085  			openapiOperation: &openapi_options.Operation{
  9086  				Parameters: &openapi_options.Parameters{
  9087  					Headers: []*openapi_options.HeaderParameter{
  9088  						{
  9089  							Name:     "X-Custom-Header",
  9090  							Required: true,
  9091  							Type:     openapi_options.HeaderParameter_STRING,
  9092  						},
  9093  					},
  9094  				},
  9095  			},
  9096  			parameters: openapiParametersObject{
  9097  				{
  9098  					Name:     "X-Custom-Header",
  9099  					In:       "header",
  9100  					Required: true,
  9101  					Type:     "string",
  9102  				},
  9103  			},
  9104  		},
  9105  	}
  9106  
  9107  	for name, test := range tests {
  9108  		test := test
  9109  
  9110  		t.Run(name, func(t *testing.T) {
  9111  			file := test.file()
  9112  
  9113  			proto.SetExtension(
  9114  				proto.Message(file.Services[0].Methods[0].Options),
  9115  				openapi_options.E_Openapiv2Operation,
  9116  				test.openapiOperation)
  9117  
  9118  			reg := descriptor.NewRegistry()
  9119  
  9120  			fileCL := crossLinkFixture(&file)
  9121  
  9122  			err := reg.Load(reqFromFile(fileCL))
  9123  			if err != nil {
  9124  				t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  9125  			}
  9126  
  9127  			result, err := applyTemplate(param{File: fileCL, reg: reg})
  9128  			if err != nil {
  9129  				t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  9130  			}
  9131  
  9132  			params := result.getPathItemObject("/v1/echo").Get.Parameters
  9133  
  9134  			if !reflect.DeepEqual(params, test.parameters) {
  9135  				t.Errorf("expected %+v, got %+v", test.parameters, params)
  9136  			}
  9137  		})
  9138  	}
  9139  }
  9140  
  9141  func GetPaths(req *openapiSwaggerObject) []string {
  9142  	paths := make([]string, len(req.Paths))
  9143  	i := 0
  9144  	for _, k := range req.Paths {
  9145  		paths[i] = k.Path
  9146  		i++
  9147  	}
  9148  	return paths
  9149  }
  9150  
  9151  func TestRenderServicesOpenapiPathsOrderPreserved(t *testing.T) {
  9152  	reqDesc := &descriptorpb.DescriptorProto{
  9153  		Name: proto.String("MyRequest"),
  9154  		Field: []*descriptorpb.FieldDescriptorProto{
  9155  			{
  9156  				Name:   proto.String("field"),
  9157  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  9158  				Number: proto.Int32(1),
  9159  			},
  9160  		},
  9161  	}
  9162  
  9163  	resDesc := &descriptorpb.DescriptorProto{
  9164  		Name: proto.String("MyResponse"),
  9165  		Field: []*descriptorpb.FieldDescriptorProto{
  9166  			{
  9167  				Name:   proto.String("field"),
  9168  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  9169  				Number: proto.Int32(1),
  9170  			},
  9171  		},
  9172  	}
  9173  	meth1 := &descriptorpb.MethodDescriptorProto{
  9174  		Name:       proto.String("MyMethod1"),
  9175  		InputType:  proto.String("MyRequest"),
  9176  		OutputType: proto.String("MyResponse"),
  9177  	}
  9178  	meth2 := &descriptorpb.MethodDescriptorProto{
  9179  		Name:       proto.String("MyMethod2"),
  9180  		InputType:  proto.String("MyRequest"),
  9181  		OutputType: proto.String("MyResponse"),
  9182  	}
  9183  
  9184  	svc := &descriptorpb.ServiceDescriptorProto{
  9185  		Name:   proto.String("MyService"),
  9186  		Method: []*descriptorpb.MethodDescriptorProto{meth1, meth2},
  9187  	}
  9188  	reqMsg := &descriptor.Message{
  9189  		DescriptorProto: reqDesc,
  9190  	}
  9191  	resMsg := &descriptor.Message{
  9192  		DescriptorProto: resDesc,
  9193  	}
  9194  	reqField := &descriptor.Field{
  9195  		Message:              reqMsg,
  9196  		FieldDescriptorProto: reqMsg.GetField()[0],
  9197  	}
  9198  	resField := &descriptor.Field{
  9199  		Message:              resMsg,
  9200  		FieldDescriptorProto: resMsg.GetField()[0],
  9201  	}
  9202  	reqField.JsonName = proto.String("field")
  9203  	resField.JsonName = proto.String("field")
  9204  	reqMsg.Fields = []*descriptor.Field{reqField}
  9205  	resMsg.Fields = []*descriptor.Field{resField}
  9206  
  9207  	file := descriptor.File{
  9208  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  9209  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  9210  			Package:        proto.String("example"),
  9211  			Name:           proto.String(",my_service.proto"),
  9212  			MessageType:    []*descriptorpb.DescriptorProto{reqDesc, resDesc},
  9213  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  9214  			Options: &descriptorpb.FileOptions{
  9215  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  9216  			},
  9217  		},
  9218  		GoPkg: descriptor.GoPackage{
  9219  			Path: "example.com/path/to/example/example.pb",
  9220  			Name: "example_pb",
  9221  		},
  9222  		Messages: []*descriptor.Message{reqMsg, resMsg},
  9223  		Services: []*descriptor.Service{
  9224  			{
  9225  				ServiceDescriptorProto: svc,
  9226  				Methods: []*descriptor.Method{
  9227  					{
  9228  						MethodDescriptorProto: meth1,
  9229  						RequestType:           reqMsg,
  9230  						ResponseType:          resMsg,
  9231  						Bindings: []*descriptor.Binding{
  9232  							{
  9233  								HTTPMethod: "POST",
  9234  								PathTmpl: httprule.Template{
  9235  									Version:  1,
  9236  									OpCodes:  []int{0, 0},
  9237  									Template: "/c/cpath",
  9238  								},
  9239  							},
  9240  						},
  9241  					}, {
  9242  						MethodDescriptorProto: meth2,
  9243  						RequestType:           reqMsg,
  9244  						ResponseType:          resMsg,
  9245  						Bindings: []*descriptor.Binding{
  9246  							{
  9247  								HTTPMethod: "POST",
  9248  								PathTmpl: httprule.Template{
  9249  									Version:  1,
  9250  									OpCodes:  []int{0, 0},
  9251  									Template: "/b/bpath",
  9252  								},
  9253  							},
  9254  						},
  9255  					},
  9256  				},
  9257  			},
  9258  		},
  9259  	}
  9260  	reg := descriptor.NewRegistry()
  9261  	reg.SetPreserveRPCOrder(true)
  9262  	err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  9263  	if err != nil {
  9264  		t.Fatalf("failed to reg.Load(): %v", err)
  9265  	}
  9266  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  9267  	if err != nil {
  9268  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  9269  	}
  9270  
  9271  	paths := result.Paths
  9272  
  9273  	firstRPCPath := file.Services[0].Methods[0].Bindings[0].PathTmpl.Template
  9274  	secondRPCPath := file.Services[0].Methods[1].Bindings[0].PathTmpl.Template
  9275  	for i, pathData := range paths {
  9276  		switch i {
  9277  		case 0:
  9278  			if got, want := pathData.Path, firstRPCPath; got != want {
  9279  				t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
  9280  			}
  9281  		case 1:
  9282  			if got, want := pathData.Path, secondRPCPath; got != want {
  9283  				t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
  9284  			}
  9285  		}
  9286  	}
  9287  }
  9288  
  9289  func TestRenderServicesOpenapiPathsOrderPreservedMultipleServices(t *testing.T) {
  9290  	reqDesc := &descriptorpb.DescriptorProto{
  9291  		Name: proto.String("MyRequest"),
  9292  		Field: []*descriptorpb.FieldDescriptorProto{
  9293  			{
  9294  				Name:   proto.String("field"),
  9295  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  9296  				Number: proto.Int32(1),
  9297  			},
  9298  		},
  9299  	}
  9300  
  9301  	resDesc := &descriptorpb.DescriptorProto{
  9302  		Name: proto.String("MyResponse"),
  9303  		Field: []*descriptorpb.FieldDescriptorProto{
  9304  			{
  9305  				Name:   proto.String("field"),
  9306  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  9307  				Number: proto.Int32(1),
  9308  			},
  9309  		},
  9310  	}
  9311  	meth1 := &descriptorpb.MethodDescriptorProto{
  9312  		Name:       proto.String("MyMethod1"),
  9313  		InputType:  proto.String("MyRequest"),
  9314  		OutputType: proto.String("MyResponse"),
  9315  	}
  9316  	meth2 := &descriptorpb.MethodDescriptorProto{
  9317  		Name:       proto.String("MyMethod2"),
  9318  		InputType:  proto.String("MyRequest"),
  9319  		OutputType: proto.String("MyResponse"),
  9320  	}
  9321  	meth3 := &descriptorpb.MethodDescriptorProto{
  9322  		Name:       proto.String("MyMethod3"),
  9323  		InputType:  proto.String("MyRequest"),
  9324  		OutputType: proto.String("MyResponse"),
  9325  	}
  9326  	meth4 := &descriptorpb.MethodDescriptorProto{
  9327  		Name:       proto.String("MyMethod4"),
  9328  		InputType:  proto.String("MyRequest"),
  9329  		OutputType: proto.String("MyResponse"),
  9330  	}
  9331  
  9332  	svc1 := &descriptorpb.ServiceDescriptorProto{
  9333  		Name:   proto.String("MyServiceOne"),
  9334  		Method: []*descriptorpb.MethodDescriptorProto{meth1, meth2},
  9335  	}
  9336  	svc2 := &descriptorpb.ServiceDescriptorProto{
  9337  		Name:   proto.String("MyServiceTwo"),
  9338  		Method: []*descriptorpb.MethodDescriptorProto{meth3, meth4},
  9339  	}
  9340  	reqMsg := &descriptor.Message{
  9341  		DescriptorProto: reqDesc,
  9342  	}
  9343  	resMsg := &descriptor.Message{
  9344  		DescriptorProto: resDesc,
  9345  	}
  9346  	reqField := &descriptor.Field{
  9347  		Message:              reqMsg,
  9348  		FieldDescriptorProto: reqMsg.GetField()[0],
  9349  	}
  9350  	resField := &descriptor.Field{
  9351  		Message:              resMsg,
  9352  		FieldDescriptorProto: resMsg.GetField()[0],
  9353  	}
  9354  	reqField.JsonName = proto.String("field")
  9355  	resField.JsonName = proto.String("field")
  9356  	reqMsg.Fields = []*descriptor.Field{reqField}
  9357  	resMsg.Fields = []*descriptor.Field{resField}
  9358  
  9359  	file := descriptor.File{
  9360  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  9361  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  9362  			Package:        proto.String("example"),
  9363  			Name:           proto.String(",my_service.proto"),
  9364  			MessageType:    []*descriptorpb.DescriptorProto{reqDesc, resDesc},
  9365  			Service:        []*descriptorpb.ServiceDescriptorProto{svc1, svc2},
  9366  			Options: &descriptorpb.FileOptions{
  9367  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  9368  			},
  9369  		},
  9370  		GoPkg: descriptor.GoPackage{
  9371  			Path: "example.com/path/to/example/example.pb",
  9372  			Name: "example_pb",
  9373  		},
  9374  		Messages: []*descriptor.Message{reqMsg, resMsg},
  9375  		Services: []*descriptor.Service{
  9376  			{
  9377  				ServiceDescriptorProto: svc1,
  9378  				Methods: []*descriptor.Method{
  9379  					{
  9380  						MethodDescriptorProto: meth1,
  9381  						RequestType:           reqMsg,
  9382  						ResponseType:          resMsg,
  9383  						Bindings: []*descriptor.Binding{
  9384  							{
  9385  								HTTPMethod: "POST",
  9386  								PathTmpl: httprule.Template{
  9387  									Version:  1,
  9388  									OpCodes:  []int{0, 0},
  9389  									Template: "/g/gpath",
  9390  								},
  9391  							},
  9392  						},
  9393  					}, {
  9394  						MethodDescriptorProto: meth2,
  9395  						RequestType:           reqMsg,
  9396  						ResponseType:          resMsg,
  9397  						Bindings: []*descriptor.Binding{
  9398  							{
  9399  								HTTPMethod: "POST",
  9400  								PathTmpl: httprule.Template{
  9401  									Version:  1,
  9402  									OpCodes:  []int{0, 0},
  9403  									Template: "/f/fpath",
  9404  								},
  9405  							},
  9406  						},
  9407  					},
  9408  				},
  9409  			}, {
  9410  				ServiceDescriptorProto: svc1,
  9411  				Methods: []*descriptor.Method{
  9412  					{
  9413  						MethodDescriptorProto: meth3,
  9414  						RequestType:           reqMsg,
  9415  						ResponseType:          resMsg,
  9416  						Bindings: []*descriptor.Binding{
  9417  							{
  9418  								HTTPMethod: "POST",
  9419  								PathTmpl: httprule.Template{
  9420  									Version:  1,
  9421  									OpCodes:  []int{0, 0},
  9422  									Template: "/c/cpath",
  9423  								},
  9424  							},
  9425  						},
  9426  					}, {
  9427  						MethodDescriptorProto: meth4,
  9428  						RequestType:           reqMsg,
  9429  						ResponseType:          resMsg,
  9430  						Bindings: []*descriptor.Binding{
  9431  							{
  9432  								HTTPMethod: "POST",
  9433  								PathTmpl: httprule.Template{
  9434  									Version:  1,
  9435  									OpCodes:  []int{0, 0},
  9436  									Template: "/b/bpath",
  9437  								},
  9438  							},
  9439  						},
  9440  					},
  9441  				},
  9442  			},
  9443  		},
  9444  	}
  9445  	reg := descriptor.NewRegistry()
  9446  	reg.SetPreserveRPCOrder(true)
  9447  	reg.SetUseJSONNamesForFields(true)
  9448  	err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  9449  	if err != nil {
  9450  		t.Fatalf("failed to reg.Load(): %v", err)
  9451  	}
  9452  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  9453  	if err != nil {
  9454  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  9455  	}
  9456  
  9457  	paths := result.Paths
  9458  
  9459  	firstRPCPath := file.Services[0].Methods[0].Bindings[0].PathTmpl.Template
  9460  	secondRPCPath := file.Services[0].Methods[1].Bindings[0].PathTmpl.Template
  9461  	thirdRPCPath := file.Services[1].Methods[0].Bindings[0].PathTmpl.Template
  9462  	fourthRPCPath := file.Services[1].Methods[1].Bindings[0].PathTmpl.Template
  9463  	for i, pathData := range paths {
  9464  		switch i {
  9465  		case 0:
  9466  			if got, want := pathData.Path, firstRPCPath; got != want {
  9467  				t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
  9468  			}
  9469  		case 1:
  9470  			if got, want := pathData.Path, secondRPCPath; got != want {
  9471  				t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
  9472  			}
  9473  		case 2:
  9474  			if got, want := pathData.Path, thirdRPCPath; got != want {
  9475  				t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
  9476  			}
  9477  		case 3:
  9478  			if got, want := pathData.Path, fourthRPCPath; got != want {
  9479  				t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
  9480  			}
  9481  		}
  9482  	}
  9483  }
  9484  
  9485  func TestRenderServicesOpenapiPathsOrderPreservedAdditionalBindings(t *testing.T) {
  9486  	reqDesc := &descriptorpb.DescriptorProto{
  9487  		Name: proto.String("MyRequest"),
  9488  		Field: []*descriptorpb.FieldDescriptorProto{
  9489  			{
  9490  				Name:   proto.String("field"),
  9491  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  9492  				Number: proto.Int32(1),
  9493  			},
  9494  		},
  9495  	}
  9496  
  9497  	resDesc := &descriptorpb.DescriptorProto{
  9498  		Name: proto.String("MyResponse"),
  9499  		Field: []*descriptorpb.FieldDescriptorProto{
  9500  			{
  9501  				Name:   proto.String("field"),
  9502  				Type:   descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  9503  				Number: proto.Int32(1),
  9504  			},
  9505  		},
  9506  	}
  9507  	meth1 := &descriptorpb.MethodDescriptorProto{
  9508  		Name:       proto.String("MyMethod1"),
  9509  		InputType:  proto.String("MyRequest"),
  9510  		OutputType: proto.String("MyResponse"),
  9511  	}
  9512  	meth2 := &descriptorpb.MethodDescriptorProto{
  9513  		Name:       proto.String("MyMethod2"),
  9514  		InputType:  proto.String("MyRequest"),
  9515  		OutputType: proto.String("MyResponse"),
  9516  	}
  9517  
  9518  	svc := &descriptorpb.ServiceDescriptorProto{
  9519  		Name:   proto.String("MyService"),
  9520  		Method: []*descriptorpb.MethodDescriptorProto{meth1, meth2},
  9521  	}
  9522  	reqMsg := &descriptor.Message{
  9523  		DescriptorProto: reqDesc,
  9524  	}
  9525  	resMsg := &descriptor.Message{
  9526  		DescriptorProto: resDesc,
  9527  	}
  9528  	reqField := &descriptor.Field{
  9529  		Message:              reqMsg,
  9530  		FieldDescriptorProto: reqMsg.GetField()[0],
  9531  	}
  9532  	resField := &descriptor.Field{
  9533  		Message:              resMsg,
  9534  		FieldDescriptorProto: resMsg.GetField()[0],
  9535  	}
  9536  	reqField.JsonName = proto.String("field")
  9537  	resField.JsonName = proto.String("field")
  9538  	reqMsg.Fields = []*descriptor.Field{reqField}
  9539  	resMsg.Fields = []*descriptor.Field{resField}
  9540  
  9541  	file := descriptor.File{
  9542  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  9543  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  9544  			Package:        proto.String("example"),
  9545  			Name:           proto.String(",my_service.proto"),
  9546  			MessageType:    []*descriptorpb.DescriptorProto{reqDesc, resDesc},
  9547  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  9548  			Options: &descriptorpb.FileOptions{
  9549  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  9550  			},
  9551  		},
  9552  		GoPkg: descriptor.GoPackage{
  9553  			Path: "example.com/path/to/example/example.pb",
  9554  			Name: "example_pb",
  9555  		},
  9556  		Messages: []*descriptor.Message{reqMsg, resMsg},
  9557  		Services: []*descriptor.Service{
  9558  			{
  9559  				ServiceDescriptorProto: svc,
  9560  				Methods: []*descriptor.Method{
  9561  					{
  9562  						MethodDescriptorProto: meth1,
  9563  						RequestType:           reqMsg,
  9564  						ResponseType:          resMsg,
  9565  						Bindings: []*descriptor.Binding{
  9566  							{
  9567  								HTTPMethod: "POST",
  9568  								PathTmpl: httprule.Template{
  9569  									Version:  1,
  9570  									OpCodes:  []int{0, 0},
  9571  									Template: "/c/cpath",
  9572  								},
  9573  							}, {
  9574  								HTTPMethod: "GET",
  9575  								PathTmpl: httprule.Template{
  9576  									Version:  1,
  9577  									OpCodes:  []int{0, 0},
  9578  									Template: "/additionalbinding",
  9579  								},
  9580  							},
  9581  						},
  9582  					}, {
  9583  						MethodDescriptorProto: meth2,
  9584  						RequestType:           reqMsg,
  9585  						ResponseType:          resMsg,
  9586  						Bindings: []*descriptor.Binding{
  9587  							{
  9588  								HTTPMethod: "POST",
  9589  								PathTmpl: httprule.Template{
  9590  									Version:  1,
  9591  									OpCodes:  []int{0, 0},
  9592  									Template: "/b/bpath",
  9593  								},
  9594  							},
  9595  						},
  9596  					},
  9597  				},
  9598  			},
  9599  		},
  9600  	}
  9601  	reg := descriptor.NewRegistry()
  9602  	reg.SetPreserveRPCOrder(true)
  9603  	reg.SetUseJSONNamesForFields(true)
  9604  	err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  9605  	if err != nil {
  9606  		t.Fatalf("failed to reg.Load(): %v", err)
  9607  	}
  9608  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  9609  	if err != nil {
  9610  		t.Fatalf("applyTemplate(%#v) failed with %v; want success", file, err)
  9611  	}
  9612  
  9613  	paths := result.Paths
  9614  	if err != nil {
  9615  		t.Fatalf("failed to obtain extension paths: %v", err)
  9616  	}
  9617  
  9618  	firstRPCPath := file.Services[0].Methods[0].Bindings[0].PathTmpl.Template
  9619  	firstRPCPathAdditionalBinding := file.Services[0].Methods[0].Bindings[1].PathTmpl.Template
  9620  	secondRPCPath := file.Services[0].Methods[1].Bindings[0].PathTmpl.Template
  9621  	for i, pathData := range paths {
  9622  		switch i {
  9623  		case 0:
  9624  			if got, want := pathData.Path, firstRPCPath; got != want {
  9625  				t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
  9626  			}
  9627  		case 1:
  9628  			if got, want := pathData.Path, firstRPCPathAdditionalBinding; got != want {
  9629  				t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
  9630  			}
  9631  		case 2:
  9632  			if got, want := pathData.Path, secondRPCPath; got != want {
  9633  				t.Fatalf("RPC path order not preserved, got %s want %s", got, want)
  9634  			}
  9635  		}
  9636  	}
  9637  }
  9638  
  9639  func TestArrayMessageItemsType(t *testing.T) {
  9640  
  9641  	msgDesc := &descriptorpb.DescriptorProto{
  9642  		Name: proto.String("ExampleMessage"),
  9643  		Field: []*descriptorpb.FieldDescriptorProto{
  9644  
  9645  			{
  9646  				Name:     proto.String("children"),
  9647  				Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  9648  				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  9649  				TypeName: proto.String(".example.ExampleMessage"),
  9650  				Number:   proto.Int32(1),
  9651  				JsonName: proto.String("children"),
  9652  			},
  9653  		},
  9654  	}
  9655  
  9656  	nestDesc := &descriptorpb.DescriptorProto{
  9657  		Name: proto.String("NestDescMessage"),
  9658  		Field: []*descriptorpb.FieldDescriptorProto{
  9659  			{
  9660  				Name:     proto.String("children"),
  9661  				Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  9662  				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  9663  				TypeName: proto.String(".example.ExampleMessage"),
  9664  				Number:   proto.Int32(1),
  9665  				JsonName: proto.String("children"),
  9666  			},
  9667  		},
  9668  	}
  9669  
  9670  	meth := &descriptorpb.MethodDescriptorProto{
  9671  		Name:       proto.String("Example"),
  9672  		InputType:  proto.String("ExampleMessage"),
  9673  		OutputType: proto.String("NestDescMessage"),
  9674  	}
  9675  	svc := &descriptorpb.ServiceDescriptorProto{
  9676  		Name:   proto.String("ExampleService"),
  9677  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  9678  	}
  9679  	msg := &descriptor.Message{
  9680  		DescriptorProto: msgDesc,
  9681  	}
  9682  	nsg := &descriptor.Message{
  9683  		DescriptorProto: nestDesc,
  9684  	}
  9685  	msg.Fields = []*descriptor.Field{
  9686  		{
  9687  			Message:              msg,
  9688  			FieldDescriptorProto: msg.GetField()[0],
  9689  		},
  9690  	}
  9691  	nsg.Fields = []*descriptor.Field{
  9692  		{
  9693  			Message:              nsg,
  9694  			FieldDescriptorProto: nsg.GetField()[0],
  9695  		},
  9696  	}
  9697  	file := descriptor.File{
  9698  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  9699  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  9700  			Name:           proto.String("example.proto"),
  9701  			Package:        proto.String("example"),
  9702  			MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nestDesc},
  9703  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  9704  			Options: &descriptorpb.FileOptions{
  9705  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  9706  			},
  9707  		},
  9708  		GoPkg: descriptor.GoPackage{
  9709  			Path: "example.com/path/to/example/example.pb",
  9710  			Name: "example_pb",
  9711  		},
  9712  		Messages: []*descriptor.Message{msg, nsg},
  9713  		Services: []*descriptor.Service{
  9714  			{
  9715  				ServiceDescriptorProto: svc,
  9716  				Methods: []*descriptor.Method{
  9717  					{
  9718  						MethodDescriptorProto: meth,
  9719  						RequestType:           msg,
  9720  						ResponseType:          nsg,
  9721  						Bindings: []*descriptor.Binding{
  9722  							{
  9723  								HTTPMethod: "POST",
  9724  								Body: &descriptor.Body{
  9725  									FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  9726  								},
  9727  								PathTmpl: httprule.Template{
  9728  									Version:  1,
  9729  									OpCodes:  []int{0, 0},
  9730  									Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  9731  								},
  9732  							},
  9733  						},
  9734  					},
  9735  				},
  9736  			},
  9737  		},
  9738  	}
  9739  	reg := descriptor.NewRegistry()
  9740  	reg.SetUseJSONNamesForFields(true)
  9741  	if err := AddErrorDefs(reg); err != nil {
  9742  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  9743  		return
  9744  	}
  9745  	fileCL := crossLinkFixture(&file)
  9746  	if err := reg.Load(&pluginpb.CodeGeneratorRequest{
  9747  		ProtoFile: []*descriptorpb.FileDescriptorProto{
  9748  			{
  9749  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  9750  				Name:           proto.String("acme/example.proto"),
  9751  				Package:        proto.String("example"),
  9752  				MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nestDesc},
  9753  				Service:        []*descriptorpb.ServiceDescriptorProto{},
  9754  				Options: &descriptorpb.FileOptions{
  9755  					GoPackage: proto.String("acme/example"),
  9756  				},
  9757  			},
  9758  		},
  9759  	}); err != nil {
  9760  		t.Errorf("reg.Load(%#v) failed with %v; want success", reg, err)
  9761  		return
  9762  	}
  9763  	expect := openapiDefinitionsObject{
  9764  		"rpcStatus": openapiSchemaObject{
  9765  			schemaCore: schemaCore{
  9766  				Type: "object",
  9767  			},
  9768  			Properties: &openapiSchemaObjectProperties{
  9769  				keyVal{
  9770  					Key: "code",
  9771  					Value: openapiSchemaObject{
  9772  						schemaCore: schemaCore{
  9773  							Type:   "integer",
  9774  							Format: "int32",
  9775  						},
  9776  					},
  9777  				},
  9778  				keyVal{
  9779  					Key: "message",
  9780  					Value: openapiSchemaObject{
  9781  						schemaCore: schemaCore{
  9782  							Type: "string",
  9783  						},
  9784  					},
  9785  				},
  9786  				keyVal{
  9787  					Key: "details",
  9788  					Value: openapiSchemaObject{
  9789  						schemaCore: schemaCore{
  9790  							Type: "array",
  9791  							Items: &openapiItemsObject{
  9792  								schemaCore: schemaCore{
  9793  									Type: "object",
  9794  									Ref:  "#/definitions/protobufAny",
  9795  								},
  9796  							},
  9797  						},
  9798  					},
  9799  				},
  9800  			},
  9801  		},
  9802  		"exampleExampleMessage": openapiSchemaObject{
  9803  			schemaCore: schemaCore{
  9804  				Type: "object",
  9805  			},
  9806  			Properties: &openapiSchemaObjectProperties{
  9807  				keyVal{
  9808  					Key: "children",
  9809  					Value: openapiSchemaObject{
  9810  						schemaCore: schemaCore{
  9811  							Type: "array",
  9812  							Items: &openapiItemsObject{
  9813  								schemaCore: schemaCore{
  9814  									Type: "object",
  9815  									Ref:  "#/definitions/exampleExampleMessage",
  9816  								},
  9817  							},
  9818  						},
  9819  					},
  9820  				},
  9821  			},
  9822  		},
  9823  		"exampleNestDescMessage": openapiSchemaObject{
  9824  			schemaCore: schemaCore{
  9825  				Type: "object",
  9826  			},
  9827  			Properties: &openapiSchemaObjectProperties{
  9828  				keyVal{
  9829  					Key: "children",
  9830  					Value: openapiSchemaObject{
  9831  						schemaCore: schemaCore{
  9832  							Type: "array",
  9833  							Items: &openapiItemsObject{
  9834  								schemaCore: schemaCore{
  9835  									Type: "object",
  9836  									Ref:  "#/definitions/exampleExampleMessage",
  9837  								},
  9838  							},
  9839  						},
  9840  					},
  9841  				},
  9842  			},
  9843  		},
  9844  		"protobufAny": openapiSchemaObject{
  9845  			schemaCore: schemaCore{
  9846  				Type: "object",
  9847  			},
  9848  			Properties: &openapiSchemaObjectProperties{
  9849  				keyVal{
  9850  					Key: "@type",
  9851  					Value: openapiSchemaObject{
  9852  						schemaCore: schemaCore{
  9853  							Type: "string",
  9854  						},
  9855  					},
  9856  				},
  9857  			},
  9858  			AdditionalProperties: &openapiSchemaObject{},
  9859  		},
  9860  	}
  9861  
  9862  	result, err := applyTemplate(param{File: fileCL, reg: reg})
  9863  	if err != nil {
  9864  		t.Errorf("applyTemplate(%#v) failed with %v; want success", reg, err)
  9865  		return
  9866  	}
  9867  	if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
  9868  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  9869  	}
  9870  	if want, is, name := expect, result.Definitions, "Produces"; !reflect.DeepEqual(is, want) {
  9871  
  9872  		t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, is, want)
  9873  	}
  9874  	// If there was a failure, print out the input and the json result for debugging.
  9875  	if t.Failed() {
  9876  		t.Errorf("had: %s", file)
  9877  		t.Errorf("got: %s", fmt.Sprint(result))
  9878  	}
  9879  }
  9880  
  9881  func TestQueryParameterType(t *testing.T) {
  9882  	ntDesc := &descriptorpb.DescriptorProto{
  9883  		Name: proto.String("AddressEntry"),
  9884  		Field: []*descriptorpb.FieldDescriptorProto{
  9885  			{
  9886  				Name:     proto.String("key"),
  9887  				Number:   proto.Int32(1),
  9888  				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  9889  				Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  9890  				JsonName: proto.String("key"),
  9891  			},
  9892  			{
  9893  				Name:     proto.String("value"),
  9894  				Number:   proto.Int32(2),
  9895  				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  9896  				Type:     descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  9897  				JsonName: proto.String("value"),
  9898  			},
  9899  		},
  9900  		Options: &descriptorpb.MessageOptions{
  9901  			MapEntry: proto.Bool(true),
  9902  		},
  9903  	}
  9904  
  9905  	msgDesc := &descriptorpb.DescriptorProto{
  9906  		Name: proto.String("Person"),
  9907  		Field: []*descriptorpb.FieldDescriptorProto{
  9908  			{
  9909  				Name:     proto.String("Address"),
  9910  				Number:   proto.Int32(1),
  9911  				Label:    descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  9912  				Type:     descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  9913  				TypeName: proto.String(".example.com.Person.AddressEntry"),
  9914  				JsonName: proto.String("Address"),
  9915  			},
  9916  		},
  9917  		NestedType: []*descriptorpb.DescriptorProto{
  9918  			ntDesc,
  9919  		},
  9920  	}
  9921  
  9922  	nesteDesc := &descriptorpb.DescriptorProto{
  9923  		Name: proto.String("ExampleResponse"),
  9924  		Field: []*descriptorpb.FieldDescriptorProto{
  9925  			{
  9926  				Name:     proto.String("Key"),
  9927  				Number:   proto.Int32(1),
  9928  				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  9929  				Type:     descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  9930  				JsonName: proto.String("Key"),
  9931  			},
  9932  			{
  9933  				Name:     proto.String("Value"),
  9934  				Number:   proto.Int32(2),
  9935  				Label:    descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  9936  				Type:     descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  9937  				JsonName: proto.String("Value"),
  9938  			},
  9939  		},
  9940  	}
  9941  
  9942  	meth := &descriptorpb.MethodDescriptorProto{
  9943  		Name:       proto.String("Example"),
  9944  		InputType:  proto.String("Person"),
  9945  		OutputType: proto.String("ExampleResponse"),
  9946  	}
  9947  	svc := &descriptorpb.ServiceDescriptorProto{
  9948  		Name:   proto.String("ExampleService"),
  9949  		Method: []*descriptorpb.MethodDescriptorProto{meth},
  9950  	}
  9951  	msg := &descriptor.Message{
  9952  		DescriptorProto: msgDesc,
  9953  	}
  9954  	nt := &descriptor.Message{
  9955  		DescriptorProto: ntDesc,
  9956  	}
  9957  	nest := &descriptor.Message{
  9958  		DescriptorProto: nesteDesc,
  9959  	}
  9960  	msg.Fields = []*descriptor.Field{
  9961  		{
  9962  			Message:              msg,
  9963  			FieldDescriptorProto: msg.GetField()[0],
  9964  		},
  9965  	}
  9966  	nt.Fields = []*descriptor.Field{
  9967  		{
  9968  			Message:              nt,
  9969  			FieldDescriptorProto: msg.GetField()[0],
  9970  		},
  9971  	}
  9972  	nest.Fields = []*descriptor.Field{
  9973  		{
  9974  			Message:              nest,
  9975  			FieldDescriptorProto: nest.GetField()[0],
  9976  		},
  9977  		{
  9978  			Message:              nest,
  9979  			FieldDescriptorProto: nest.GetField()[1],
  9980  		},
  9981  	}
  9982  	file := descriptor.File{
  9983  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  9984  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  9985  			Name:           proto.String("person.proto"),
  9986  			Package:        proto.String("example.com"),
  9987  			MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nesteDesc},
  9988  			Service:        []*descriptorpb.ServiceDescriptorProto{svc},
  9989  			Options: &descriptorpb.FileOptions{
  9990  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  9991  			},
  9992  		},
  9993  		GoPkg: descriptor.GoPackage{
  9994  			Path: "example.com/path/to/example/example.pb",
  9995  			Name: "example_pb",
  9996  		},
  9997  		Messages: []*descriptor.Message{msg, nest},
  9998  		Services: []*descriptor.Service{
  9999  			{
 10000  				ServiceDescriptorProto: svc,
 10001  				Methods: []*descriptor.Method{
 10002  					{
 10003  						MethodDescriptorProto: meth,
 10004  						RequestType:           msg,
 10005  						ResponseType:          nest,
 10006  						Bindings: []*descriptor.Binding{
 10007  							{
 10008  								HTTPMethod: "GET",
 10009  								PathTmpl: httprule.Template{
 10010  									Version:  1,
 10011  									OpCodes:  []int{0, 0},
 10012  									Template: "/v1/echo",
 10013  								},
 10014  							},
 10015  						},
 10016  					},
 10017  				},
 10018  			},
 10019  		},
 10020  	}
 10021  	expect := openapiPathsObject{{
 10022  		Path: "/v1/echo",
 10023  		PathItemObject: openapiPathItemObject{
 10024  			Get: &openapiOperationObject{
 10025  				Parameters: openapiParametersObject{
 10026  					{
 10027  						Name:        "Address[string]",
 10028  						Description: `This is a request variable of the map type. The query format is "map_name[key]=value", e.g. If the map name is Age, the key type is string, and the value type is integer, the query parameter is expressed as Age["bob"]=18`,
 10029  						In:          "query",
 10030  						Type:        "integer",
 10031  					},
 10032  				},
 10033  			},
 10034  		},
 10035  	}}
 10036  
 10037  	reg := descriptor.NewRegistry()
 10038  	reg.SetUseJSONNamesForFields(false)
 10039  	if err := AddErrorDefs(reg); err != nil {
 10040  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
 10041  		return
 10042  	}
 10043  	fileCL := crossLinkFixture(&file)
 10044  	err := reg.Load(&pluginpb.CodeGeneratorRequest{
 10045  		ProtoFile: []*descriptorpb.FileDescriptorProto{
 10046  			{
 10047  				Name:           proto.String("person.proto"),
 10048  				Package:        proto.String("example.com"),
 10049  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
 10050  				MessageType:    []*descriptorpb.DescriptorProto{msgDesc, nesteDesc},
 10051  				Service:        []*descriptorpb.ServiceDescriptorProto{},
 10052  				Options: &descriptorpb.FileOptions{
 10053  					GoPackage: proto.String("person.proto"),
 10054  				},
 10055  			},
 10056  		},
 10057  	})
 10058  	if err != nil {
 10059  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
 10060  		return
 10061  	}
 10062  	result, err := applyTemplate(param{File: fileCL, reg: reg})
 10063  	if err != nil {
 10064  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
 10065  		return
 10066  	}
 10067  	if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
 10068  		t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
 10069  	}
 10070  
 10071  	if want, is, name := expect[0].PathItemObject.Get.Parameters, result.getPathItemObject("/v1/echo").Get.Parameters, "Produces"; !reflect.DeepEqual(is, want) {
 10072  
 10073  		t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, is, want)
 10074  	}
 10075  	// If there was a failure, print out the input and the json result for debugging.
 10076  	if t.Failed() {
 10077  		t.Errorf("had: %s", file)
 10078  		t.Errorf("got: %s", fmt.Sprint(result))
 10079  	}
 10080  }
 10081  
 10082  func TestApplyTemplateRequestWithServerStreamingHttpBody(t *testing.T) {
 10083  	meth := &descriptorpb.MethodDescriptorProto{
 10084  		Name:            proto.String("Echo"),
 10085  		InputType:       proto.String(".google.api.HttpBody"),
 10086  		OutputType:      proto.String(".google.api.HttpBody"),
 10087  		ClientStreaming: proto.Bool(false),
 10088  		ServerStreaming: proto.Bool(true),
 10089  	}
 10090  	svc := &descriptorpb.ServiceDescriptorProto{
 10091  		Name:   proto.String("ExampleService"),
 10092  		Method: []*descriptorpb.MethodDescriptorProto{meth},
 10093  	}
 10094  	httpBodyFile, err := protoregistry.GlobalFiles.FindFileByPath("google/api/httpbody.proto")
 10095  	if err != nil {
 10096  		t.Fatal(err)
 10097  	}
 10098  	httpBodyFile.SourceLocations()
 10099  	desc, err := protoregistry.GlobalFiles.FindDescriptorByName("google.api.HttpBody")
 10100  	if err != nil {
 10101  		t.Fatal(err)
 10102  	}
 10103  	msg := &descriptor.Message{
 10104  		DescriptorProto: protodesc.ToDescriptorProto(desc.(protoreflect.MessageDescriptor)),
 10105  		File: &descriptor.File{
 10106  			FileDescriptorProto: protodesc.ToFileDescriptorProto(httpBodyFile),
 10107  		},
 10108  	}
 10109  	anyFile, err := protoregistry.GlobalFiles.FindFileByPath("google/protobuf/any.proto")
 10110  	if err != nil {
 10111  		t.Fatal(err)
 10112  	}
 10113  	file := descriptor.File{
 10114  		FileDescriptorProto: &descriptorpb.FileDescriptorProto{
 10115  			SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
 10116  			Name:           proto.String("example.proto"),
 10117  			Package:        proto.String("example"),
 10118  			Dependency: []string{
 10119  				"google/api/httpbody.proto",
 10120  			},
 10121  			Service: []*descriptorpb.ServiceDescriptorProto{svc},
 10122  			Options: &descriptorpb.FileOptions{
 10123  				GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
 10124  			},
 10125  		},
 10126  		GoPkg: descriptor.GoPackage{
 10127  			Path: "example.com/path/to/example/example.pb",
 10128  			Name: "example_pb",
 10129  		},
 10130  		Services: []*descriptor.Service{
 10131  			{
 10132  				ServiceDescriptorProto: svc,
 10133  				Methods: []*descriptor.Method{
 10134  					{
 10135  						MethodDescriptorProto: meth,
 10136  						RequestType:           msg,
 10137  						ResponseType:          msg,
 10138  						Bindings: []*descriptor.Binding{
 10139  							{
 10140  								HTTPMethod: "POST",
 10141  								PathTmpl: httprule.Template{
 10142  									Version:  1,
 10143  									OpCodes:  []int{0, 0},
 10144  									Template: "/v1/echo",
 10145  								},
 10146  							},
 10147  						},
 10148  					},
 10149  				},
 10150  			},
 10151  		},
 10152  	}
 10153  	reg := descriptor.NewRegistry()
 10154  	if err := AddErrorDefs(reg); err != nil {
 10155  		t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
 10156  		return
 10157  	}
 10158  	err = reg.Load(&pluginpb.CodeGeneratorRequest{
 10159  		ProtoFile: []*descriptorpb.FileDescriptorProto{
 10160  			protodesc.ToFileDescriptorProto(anyFile),
 10161  			protodesc.ToFileDescriptorProto(httpBodyFile),
 10162  			file.FileDescriptorProto,
 10163  		},
 10164  	})
 10165  	if err != nil {
 10166  		t.Fatalf("failed to load code generator request: %v", err)
 10167  	}
 10168  	result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
 10169  	if err != nil {
 10170  		t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
 10171  		return
 10172  	}
 10173  
 10174  	if want, got, name := 3, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
 10175  		t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
 10176  	}
 10177  
 10178  	if _, ok := result.getPathItemObject("/v1/echo").Post.Responses["200"]; !ok {
 10179  		t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.getPathItemObject("/v1/echo").Post.Responses["200"]`)
 10180  	} else {
 10181  		if want, got, name := "A successful response.(streaming responses)", result.getPathItemObject("/v1/echo").Post.Responses["200"].Description, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Description`; !reflect.DeepEqual(got, want) {
 10182  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
 10183  		}
 10184  		streamExampleExampleMessage := result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema
 10185  		if want, got, name := "string", streamExampleExampleMessage.Type, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Type`; !reflect.DeepEqual(got, want) {
 10186  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
 10187  		}
 10188  		if want, got, name := "binary", streamExampleExampleMessage.Format, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Format`; !reflect.DeepEqual(got, want) {
 10189  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
 10190  		}
 10191  		if want, got, name := "Free form byte stream", streamExampleExampleMessage.Title, `result.getPathItemObject("/v1/echo").Post.Responses["200"].Schema.Title`; !reflect.DeepEqual(got, want) {
 10192  			t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
 10193  		}
 10194  		if len(*streamExampleExampleMessage.Properties) != 0 {
 10195  			t.Errorf("applyTemplate(%#v).Properties should be empty", file)
 10196  		}
 10197  	}
 10198  
 10199  	// If there was a failure, print out the input and the json result for debugging.
 10200  	if t.Failed() {
 10201  		t.Errorf("had: %s", file)
 10202  		t.Errorf("got: %s", fmt.Sprint(result))
 10203  	}
 10204  }
 10205  
 10206  // Returns the openapiPathItemObject associated with a path.
 10207  func (so openapiSwaggerObject) getPathItemObject(path string) openapiPathItemObject {
 10208  	for _, pathData := range so.Paths {
 10209  		if pathData.Path == path {
 10210  			return pathData.PathItemObject
 10211  		}
 10212  	}
 10213  
 10214  	return openapiPathItemObject{}
 10215  }
 10216  
 10217  func TestGetPathItemObjectSwaggerObjectMethod(t *testing.T) {
 10218  	testCases := [...]struct {
 10219  		testName               string
 10220  		swaggerObject          openapiSwaggerObject
 10221  		path                   string
 10222  		expectedPathItemObject openapiPathItemObject
 10223  	}{
 10224  		{
 10225  			testName: "Path present in swagger object",
 10226  			swaggerObject: openapiSwaggerObject{Paths: openapiPathsObject{{
 10227  				Path: "a/path",
 10228  				PathItemObject: openapiPathItemObject{
 10229  					Get: &openapiOperationObject{
 10230  						Description: "A testful description",
 10231  					},
 10232  				},
 10233  			}}},
 10234  			path: "a/path",
 10235  			expectedPathItemObject: openapiPathItemObject{
 10236  				Get: &openapiOperationObject{
 10237  					Description: "A testful description",
 10238  				},
 10239  			},
 10240  		}, {
 10241  			testName: "Path not present in swaggerObject",
 10242  			swaggerObject: openapiSwaggerObject{Paths: openapiPathsObject{{
 10243  				Path: "a/path",
 10244  				PathItemObject: openapiPathItemObject{
 10245  					Get: &openapiOperationObject{
 10246  						Description: "A testful description",
 10247  					},
 10248  				},
 10249  			}}},
 10250  			path:                   "b/path",
 10251  			expectedPathItemObject: openapiPathItemObject{},
 10252  		}, {
 10253  			testName: "Path present in swaggerPathsObject with multiple paths",
 10254  			swaggerObject: openapiSwaggerObject{Paths: openapiPathsObject{{
 10255  				Path: "a/path",
 10256  				PathItemObject: openapiPathItemObject{
 10257  					Get: &openapiOperationObject{
 10258  						Description: "A testful description",
 10259  					},
 10260  				},
 10261  			}, {
 10262  				Path: "another/path",
 10263  				PathItemObject: openapiPathItemObject{
 10264  					Get: &openapiOperationObject{
 10265  						Description: "Another testful description",
 10266  					},
 10267  				},
 10268  			}}},
 10269  			path: "another/path",
 10270  			expectedPathItemObject: openapiPathItemObject{
 10271  				Get: &openapiOperationObject{
 10272  					Description: "Another testful description",
 10273  				},
 10274  			},
 10275  		}, {
 10276  			testName:               "Path not present in swaggerObject with no paths",
 10277  			swaggerObject:          openapiSwaggerObject{},
 10278  			path:                   "b/path",
 10279  			expectedPathItemObject: openapiPathItemObject{},
 10280  		},
 10281  	}
 10282  
 10283  	for _, tc := range testCases {
 10284  		tc := tc
 10285  
 10286  		t.Run(tc.testName, func(t *testing.T) {
 10287  			actualPathItemObject := tc.swaggerObject.getPathItemObject(tc.path)
 10288  			if isEqual := reflect.DeepEqual(actualPathItemObject, tc.expectedPathItemObject); !isEqual {
 10289  				t.Fatalf("Got pathItemObject: %#v, want pathItemObject: %#v", actualPathItemObject, tc.expectedPathItemObject)
 10290  			}
 10291  		})
 10292  	}
 10293  }
 10294  
 10295  func TestGetPathItemObjectFunction(t *testing.T) {
 10296  	testCases := [...]struct {
 10297  		testName               string
 10298  		paths                  openapiPathsObject
 10299  		path                   string
 10300  		expectedPathItemObject openapiPathItemObject
 10301  		expectedIsPathPresent  bool
 10302  	}{
 10303  		{
 10304  			testName: "Path present in openapiPathsObject",
 10305  			paths: openapiPathsObject{{
 10306  				Path: "a/path",
 10307  				PathItemObject: openapiPathItemObject{
 10308  					Get: &openapiOperationObject{
 10309  						Description: "A testful description",
 10310  					},
 10311  				},
 10312  			}},
 10313  			path: "a/path",
 10314  			expectedPathItemObject: openapiPathItemObject{
 10315  				Get: &openapiOperationObject{
 10316  					Description: "A testful description",
 10317  				},
 10318  			},
 10319  			expectedIsPathPresent: true,
 10320  		}, {
 10321  			testName: "Path not present in openapiPathsObject",
 10322  			paths: openapiPathsObject{{
 10323  				Path: "a/path",
 10324  				PathItemObject: openapiPathItemObject{
 10325  					Get: &openapiOperationObject{
 10326  						Description: "A testful description",
 10327  					},
 10328  				},
 10329  			}},
 10330  			path:                   "b/path",
 10331  			expectedPathItemObject: openapiPathItemObject{},
 10332  			expectedIsPathPresent:  false,
 10333  		}, {
 10334  			testName: "Path present in openapiPathsObject with multiple paths",
 10335  			paths: openapiPathsObject{{
 10336  				Path: "a/path",
 10337  				PathItemObject: openapiPathItemObject{
 10338  					Get: &openapiOperationObject{
 10339  						Description: "A testful description",
 10340  					},
 10341  				},
 10342  			}, {
 10343  				Path: "another/path",
 10344  				PathItemObject: openapiPathItemObject{
 10345  					Get: &openapiOperationObject{
 10346  						Description: "Another testful description",
 10347  					},
 10348  				},
 10349  			}},
 10350  			path: "another/path",
 10351  			expectedPathItemObject: openapiPathItemObject{
 10352  				Get: &openapiOperationObject{
 10353  					Description: "Another testful description",
 10354  				},
 10355  			},
 10356  			expectedIsPathPresent: true,
 10357  		}, {
 10358  			testName:               "Path not present in empty openapiPathsObject",
 10359  			paths:                  openapiPathsObject{},
 10360  			path:                   "b/path",
 10361  			expectedPathItemObject: openapiPathItemObject{},
 10362  			expectedIsPathPresent:  false,
 10363  		},
 10364  	}
 10365  
 10366  	for _, tc := range testCases {
 10367  		tc := tc
 10368  
 10369  		t.Run(tc.testName, func(t *testing.T) {
 10370  			actualPathItemObject, actualIsPathPresent := getPathItemObject(tc.paths, tc.path)
 10371  			if isEqual := reflect.DeepEqual(actualPathItemObject, tc.expectedPathItemObject); !isEqual {
 10372  				t.Fatalf("Got pathItemObject: %#v, want pathItemObject: %#v", actualPathItemObject, tc.expectedPathItemObject)
 10373  			}
 10374  			if actualIsPathPresent != tc.expectedIsPathPresent {
 10375  				t.Fatalf("Got isPathPresent bool: %t, want isPathPresent bool: %t", actualIsPathPresent, tc.expectedIsPathPresent)
 10376  			}
 10377  		})
 10378  	}
 10379  }
 10380  
 10381  func TestUpdatePaths(t *testing.T) {
 10382  	testCases := [...]struct {
 10383  		testName             string
 10384  		paths                openapiPathsObject
 10385  		pathToUpdate         string
 10386  		newPathItemObject    openapiPathItemObject
 10387  		expectedUpdatedPaths openapiPathsObject
 10388  	}{
 10389  		{
 10390  			testName: "Path present in openapiPathsObject, pathItemObject updated.",
 10391  			paths: openapiPathsObject{{
 10392  				Path: "a/path",
 10393  				PathItemObject: openapiPathItemObject{
 10394  					Get: &openapiOperationObject{
 10395  						Description: "A testful description",
 10396  					},
 10397  				},
 10398  			}},
 10399  			pathToUpdate: "a/path",
 10400  			newPathItemObject: openapiPathItemObject{
 10401  				Get: &openapiOperationObject{
 10402  					Description: "A newly updated testful description",
 10403  				},
 10404  			},
 10405  			expectedUpdatedPaths: openapiPathsObject{{
 10406  				Path: "a/path",
 10407  				PathItemObject: openapiPathItemObject{
 10408  					Get: &openapiOperationObject{
 10409  						Description: "A newly updated testful description",
 10410  					},
 10411  				},
 10412  			}},
 10413  		}, {
 10414  			testName: "Path not present in openapiPathsObject, new path data appended.",
 10415  			paths: openapiPathsObject{{
 10416  				Path: "c/path",
 10417  				PathItemObject: openapiPathItemObject{
 10418  					Get: &openapiOperationObject{
 10419  						Description: "A testful description",
 10420  					},
 10421  				},
 10422  			}},
 10423  			pathToUpdate: "b/path",
 10424  			newPathItemObject: openapiPathItemObject{
 10425  				Get: &openapiOperationObject{
 10426  					Description: "A new testful description to add",
 10427  				},
 10428  			},
 10429  			expectedUpdatedPaths: openapiPathsObject{{
 10430  				Path: "c/path",
 10431  				PathItemObject: openapiPathItemObject{
 10432  					Get: &openapiOperationObject{
 10433  						Description: "A testful description",
 10434  					},
 10435  				},
 10436  			}, {
 10437  				Path: "b/path",
 10438  				PathItemObject: openapiPathItemObject{
 10439  					Get: &openapiOperationObject{
 10440  						Description: "A new testful description to add",
 10441  					},
 10442  				},
 10443  			}},
 10444  		}, {
 10445  			testName:     "No paths present in openapiPathsObject, new path data appended.",
 10446  			paths:        openapiPathsObject{},
 10447  			pathToUpdate: "b/path",
 10448  			newPathItemObject: openapiPathItemObject{
 10449  				Get: &openapiOperationObject{
 10450  					Description: "A new testful description to add",
 10451  				},
 10452  			},
 10453  			expectedUpdatedPaths: openapiPathsObject{{
 10454  				Path: "b/path",
 10455  				PathItemObject: openapiPathItemObject{
 10456  					Get: &openapiOperationObject{
 10457  						Description: "A new testful description to add",
 10458  					},
 10459  				},
 10460  			}},
 10461  		},
 10462  	}
 10463  
 10464  	for _, tc := range testCases {
 10465  		tc := tc
 10466  
 10467  		t.Run(tc.testName, func(t *testing.T) {
 10468  			updatePaths(&tc.paths, tc.pathToUpdate, tc.newPathItemObject)
 10469  			if pathsCorrectlyUpdated := reflect.DeepEqual(tc.paths, tc.expectedUpdatedPaths); !pathsCorrectlyUpdated {
 10470  				t.Fatalf("Paths not correctly updated. Want %#v, got %#v", tc.expectedUpdatedPaths, tc.paths)
 10471  			}
 10472  		})
 10473  	}
 10474  }
 10475  
 10476  // Test that enum values have internal comments removed
 10477  func TestEnumValueProtoComments(t *testing.T) {
 10478  	reg := descriptor.NewRegistry()
 10479  	name := "kind"
 10480  	comments := "(-- this is a comment --)"
 10481  
 10482  	enum := &descriptor.Enum{
 10483  		EnumDescriptorProto: &descriptorpb.EnumDescriptorProto{
 10484  			Name: &name,
 10485  		},
 10486  		File: &descriptor.File{
 10487  			FileDescriptorProto: &descriptorpb.FileDescriptorProto{
 10488  				Name:    new(string),
 10489  				Package: new(string),
 10490  				SourceCodeInfo: &descriptorpb.SourceCodeInfo{
 10491  					Location: []*descriptorpb.SourceCodeInfo_Location{
 10492  						&descriptorpb.SourceCodeInfo_Location{
 10493  							LeadingComments: &comments,
 10494  						},
 10495  					},
 10496  				},
 10497  			},
 10498  		},
 10499  	}
 10500  	comments = enumValueProtoComments(reg, enum)
 10501  	if comments != "" {
 10502  		t.Errorf("expected '', got '%v'", comments)
 10503  	}
 10504  }
 10505  
 10506  func MustMarshal(v interface{}) []byte {
 10507  	b, err := json.Marshal(v)
 10508  	if err != nil {
 10509  		panic(err)
 10510  	}
 10511  	return b
 10512  }
 10513  
 10514  func TestMergeTags(t *testing.T) {
 10515  	testCases := [...]struct {
 10516  		testName           string
 10517  		existingTags       []openapiTagObject
 10518  		newTags            []openapiTagObject
 10519  		expectedMergedTags []openapiTagObject
 10520  	}{
 10521  		{
 10522  			testName: "Simple merge.",
 10523  			existingTags: []openapiTagObject{{
 10524  				Name:        "tag1",
 10525  				Description: "tag1 description",
 10526  			}},
 10527  			newTags: []openapiTagObject{{
 10528  				Name:        "tag2",
 10529  				Description: "tag2 description",
 10530  			}},
 10531  			expectedMergedTags: []openapiTagObject{{
 10532  				Name:        "tag1",
 10533  				Description: "tag1 description",
 10534  			}, {
 10535  				Name:        "tag2",
 10536  				Description: "tag2 description",
 10537  			}},
 10538  		},
 10539  		{
 10540  			testName: "Merge description",
 10541  			existingTags: []openapiTagObject{{
 10542  				Name:        "tag1",
 10543  				Description: "tag1 description",
 10544  			}, {
 10545  				Name: "tag2",
 10546  			}, {
 10547  				Name:        "tag3",
 10548  				Description: "tag3 description",
 10549  			}},
 10550  			newTags: []openapiTagObject{{
 10551  				Name:        "tag2",
 10552  				Description: "tag2 description",
 10553  			}},
 10554  			expectedMergedTags: []openapiTagObject{{
 10555  				Name:        "tag1",
 10556  				Description: "tag1 description",
 10557  			}, {
 10558  				Name:        "tag2",
 10559  				Description: "tag2 description",
 10560  			}, {
 10561  				Name:        "tag3",
 10562  				Description: "tag3 description",
 10563  			}},
 10564  		},
 10565  		{
 10566  			testName: "Merge external docs",
 10567  			existingTags: []openapiTagObject{{
 10568  				Name:         "tag1",
 10569  				ExternalDocs: &openapiExternalDocumentationObject{},
 10570  			}, {
 10571  				Name: "tag2",
 10572  			}, {
 10573  				Name: "tag3",
 10574  				ExternalDocs: &openapiExternalDocumentationObject{
 10575  					Description: "tag3 description",
 10576  				},
 10577  			}, {
 10578  				Name: "tag4",
 10579  				ExternalDocs: &openapiExternalDocumentationObject{
 10580  					URL: "tag4 url",
 10581  				},
 10582  			}},
 10583  			newTags: []openapiTagObject{{
 10584  				Name: "tag1",
 10585  				ExternalDocs: &openapiExternalDocumentationObject{
 10586  					Description: "tag1 description",
 10587  				},
 10588  			}, {
 10589  				Name: "tag2",
 10590  				ExternalDocs: &openapiExternalDocumentationObject{
 10591  					Description: "tag2 description",
 10592  					URL:         "tag2 url",
 10593  				},
 10594  			}, {
 10595  				Name: "tag3",
 10596  				ExternalDocs: &openapiExternalDocumentationObject{
 10597  					Description: "ignored tag3 description",
 10598  					URL:         "tag3 url",
 10599  				},
 10600  			}, {
 10601  				Name: "tag4",
 10602  				ExternalDocs: &openapiExternalDocumentationObject{
 10603  					Description: "tag4 description",
 10604  				},
 10605  			}},
 10606  			expectedMergedTags: []openapiTagObject{{
 10607  				Name: "tag1",
 10608  				ExternalDocs: &openapiExternalDocumentationObject{
 10609  					Description: "tag1 description",
 10610  				},
 10611  			}, {
 10612  				Name: "tag2",
 10613  				ExternalDocs: &openapiExternalDocumentationObject{
 10614  					Description: "tag2 description",
 10615  					URL:         "tag2 url",
 10616  				},
 10617  			}, {
 10618  				Name: "tag3",
 10619  				ExternalDocs: &openapiExternalDocumentationObject{
 10620  					Description: "tag3 description",
 10621  					URL:         "tag3 url",
 10622  				},
 10623  			}, {
 10624  				Name: "tag4",
 10625  				ExternalDocs: &openapiExternalDocumentationObject{
 10626  					Description: "tag4 description",
 10627  					URL:         "tag4 url",
 10628  				},
 10629  			}},
 10630  		},
 10631  		{
 10632  			testName: "Merge extensions",
 10633  			existingTags: []openapiTagObject{{
 10634  				Name:       "tag1",
 10635  				extensions: []extension{{key: "x-key1", value: MustMarshal("key1 extension")}},
 10636  			}, {
 10637  				Name: "tag2",
 10638  				extensions: []extension{
 10639  					{key: "x-key1", value: MustMarshal("key1 extension")},
 10640  					{key: "x-key2", value: MustMarshal("key2 extension")},
 10641  				},
 10642  			}, {
 10643  				Name: "tag3",
 10644  				extensions: []extension{
 10645  					{key: "x-key1", value: MustMarshal("key1 extension")},
 10646  				},
 10647  			}, {
 10648  				Name:       "tag4",
 10649  				extensions: nil,
 10650  			}},
 10651  			newTags: []openapiTagObject{{
 10652  				Name:       "tag1",
 10653  				extensions: []extension{{key: "x-key2", value: MustMarshal("key2 extension")}},
 10654  			}, {
 10655  				Name: "tag2",
 10656  				extensions: []extension{
 10657  					{key: "x-key1", value: MustMarshal("key1 extension")},
 10658  					{key: "x-key2", value: MustMarshal("ignored key2 extension")},
 10659  					{key: "x-key3", value: MustMarshal("key3 extension")},
 10660  				},
 10661  			}, {
 10662  				Name:       "tag3",
 10663  				extensions: nil,
 10664  			}, {
 10665  				Name: "tag4",
 10666  				extensions: []extension{
 10667  					{key: "x-key1", value: MustMarshal("key1 extension")},
 10668  				},
 10669  			}},
 10670  			expectedMergedTags: []openapiTagObject{{
 10671  				Name: "tag1",
 10672  				extensions: []extension{
 10673  					{key: "x-key1", value: MustMarshal("key1 extension")},
 10674  					{key: "x-key2", value: MustMarshal("key2 extension")},
 10675  				},
 10676  			}, {
 10677  				Name: "tag2",
 10678  				extensions: []extension{
 10679  					{key: "x-key1", value: MustMarshal("key1 extension")},
 10680  					{key: "x-key2", value: MustMarshal("key2 extension")},
 10681  					{key: "x-key3", value: MustMarshal("key3 extension")},
 10682  				},
 10683  			}, {
 10684  				Name: "tag3",
 10685  				extensions: []extension{
 10686  					{key: "x-key1", value: MustMarshal("key1 extension")},
 10687  				},
 10688  			}, {
 10689  				Name: "tag4",
 10690  				extensions: []extension{
 10691  					{key: "x-key1", value: MustMarshal("key1 extension")},
 10692  				},
 10693  			}},
 10694  		},
 10695  	}
 10696  	for _, tc := range testCases {
 10697  		tc := tc
 10698  		t.Run(tc.testName, func(t *testing.T) {
 10699  			mergedTags := mergeTags(tc.existingTags, tc.newTags)
 10700  			if !reflect.DeepEqual(tc.expectedMergedTags, mergedTags) {
 10701  				t.Fatalf("%s: Tags not correctly merged. Want %#v, got %#v", tc.testName, tc.expectedMergedTags, mergedTags)
 10702  			}
 10703  		})
 10704  	}
 10705  }