template_test.go 135 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635
  1. package genopenapi
  2. import (
  3. "encoding/json"
  4. "errors"
  5. "fmt"
  6. "math"
  7. "reflect"
  8. "strings"
  9. "testing"
  10. "git.ikuban.com/server/swagger-api/protoc-gen-openapiv2/internal/descriptor"
  11. "git.ikuban.com/server/swagger-api/protoc-gen-openapiv2/internal/descriptor/openapiconfig"
  12. "git.ikuban.com/server/swagger-api/protoc-gen-openapiv2/internal/httprule"
  13. "github.com/google/go-cmp/cmp"
  14. openapi_options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
  15. "github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
  16. "google.golang.org/genproto/googleapis/api/annotations"
  17. "google.golang.org/genproto/protobuf/field_mask"
  18. "google.golang.org/protobuf/proto"
  19. "google.golang.org/protobuf/reflect/protodesc"
  20. "google.golang.org/protobuf/types/descriptorpb"
  21. "google.golang.org/protobuf/types/known/durationpb"
  22. "google.golang.org/protobuf/types/known/structpb"
  23. "google.golang.org/protobuf/types/known/timestamppb"
  24. "google.golang.org/protobuf/types/known/wrapperspb"
  25. "google.golang.org/protobuf/types/pluginpb"
  26. )
  27. var marshaler = &runtime.JSONPb{}
  28. func crossLinkFixture(f *descriptor.File) *descriptor.File {
  29. for _, m := range f.Messages {
  30. m.File = f
  31. }
  32. for _, svc := range f.Services {
  33. svc.File = f
  34. for _, m := range svc.Methods {
  35. m.Service = svc
  36. for _, b := range m.Bindings {
  37. b.Method = m
  38. for _, param := range b.PathParams {
  39. param.Method = m
  40. }
  41. }
  42. }
  43. }
  44. return f
  45. }
  46. func reqFromFile(f *descriptor.File) *pluginpb.CodeGeneratorRequest {
  47. return &pluginpb.CodeGeneratorRequest{
  48. ProtoFile: []*descriptorpb.FileDescriptorProto{
  49. f.FileDescriptorProto,
  50. },
  51. FileToGenerate: []string{f.GetName()},
  52. }
  53. }
  54. func TestMessageToQueryParametersWithEnumAsInt(t *testing.T) {
  55. type test struct {
  56. MsgDescs []*descriptorpb.DescriptorProto
  57. Message string
  58. Params []openapiParameterObject
  59. }
  60. tests := []test{
  61. {
  62. MsgDescs: []*descriptorpb.DescriptorProto{
  63. {
  64. Name: proto.String("ExampleMessage"),
  65. Field: []*descriptorpb.FieldDescriptorProto{
  66. {
  67. Name: proto.String("a"),
  68. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  69. Number: proto.Int32(1),
  70. },
  71. {
  72. Name: proto.String("b"),
  73. Type: descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
  74. Number: proto.Int32(2),
  75. },
  76. {
  77. Name: proto.String("c"),
  78. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  79. Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  80. Number: proto.Int32(3),
  81. },
  82. },
  83. },
  84. },
  85. Message: "ExampleMessage",
  86. Params: []openapiParameterObject{
  87. {
  88. Name: "a",
  89. In: "query",
  90. Required: false,
  91. Type: "string",
  92. },
  93. {
  94. Name: "b",
  95. In: "query",
  96. Required: false,
  97. Type: "number",
  98. Format: "double",
  99. },
  100. {
  101. Name: "c",
  102. In: "query",
  103. Required: false,
  104. Type: "array",
  105. CollectionFormat: "multi",
  106. },
  107. },
  108. },
  109. {
  110. MsgDescs: []*descriptorpb.DescriptorProto{
  111. {
  112. Name: proto.String("ExampleMessage"),
  113. Field: []*descriptorpb.FieldDescriptorProto{
  114. {
  115. Name: proto.String("nested"),
  116. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  117. TypeName: proto.String(".example.Nested"),
  118. Number: proto.Int32(1),
  119. },
  120. },
  121. },
  122. {
  123. Name: proto.String("Nested"),
  124. Field: []*descriptorpb.FieldDescriptorProto{
  125. {
  126. Name: proto.String("a"),
  127. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  128. Number: proto.Int32(1),
  129. },
  130. {
  131. Name: proto.String("deep"),
  132. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  133. TypeName: proto.String(".example.Nested.DeepNested"),
  134. Number: proto.Int32(2),
  135. },
  136. },
  137. NestedType: []*descriptorpb.DescriptorProto{{
  138. Name: proto.String("DeepNested"),
  139. Field: []*descriptorpb.FieldDescriptorProto{
  140. {
  141. Name: proto.String("b"),
  142. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  143. Number: proto.Int32(1),
  144. },
  145. {
  146. Name: proto.String("c"),
  147. Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
  148. TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
  149. Number: proto.Int32(2),
  150. },
  151. },
  152. EnumType: []*descriptorpb.EnumDescriptorProto{
  153. {
  154. Name: proto.String("DeepEnum"),
  155. Value: []*descriptorpb.EnumValueDescriptorProto{
  156. {Name: proto.String("FALSE"), Number: proto.Int32(0)},
  157. {Name: proto.String("TRUE"), Number: proto.Int32(1)},
  158. },
  159. },
  160. },
  161. }},
  162. },
  163. },
  164. Message: "ExampleMessage",
  165. Params: []openapiParameterObject{
  166. {
  167. Name: "nested.a",
  168. In: "query",
  169. Required: false,
  170. Type: "string",
  171. },
  172. {
  173. Name: "nested.deep.b",
  174. In: "query",
  175. Required: false,
  176. Type: "string",
  177. },
  178. {
  179. Name: "nested.deep.c",
  180. In: "query",
  181. Required: false,
  182. Type: "integer",
  183. Enum: []string{"0", "1"},
  184. Default: "0",
  185. },
  186. },
  187. },
  188. }
  189. for _, test := range tests {
  190. reg := descriptor.NewRegistry()
  191. reg.SetEnumsAsInts(true)
  192. msgs := []*descriptor.Message{}
  193. for _, msgdesc := range test.MsgDescs {
  194. msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  195. }
  196. file := descriptor.File{
  197. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  198. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  199. Name: proto.String("example.proto"),
  200. Package: proto.String("example"),
  201. Dependency: []string{},
  202. MessageType: test.MsgDescs,
  203. Service: []*descriptorpb.ServiceDescriptorProto{},
  204. Options: &descriptorpb.FileOptions{
  205. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  206. },
  207. },
  208. GoPkg: descriptor.GoPackage{
  209. Path: "example.com/path/to/example/example.pb",
  210. Name: "example_pb",
  211. },
  212. Messages: msgs,
  213. }
  214. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  215. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  216. })
  217. if err != nil {
  218. t.Fatalf("failed to load code generator request: %v", err)
  219. }
  220. message, err := reg.LookupMsg("", ".example."+test.Message)
  221. if err != nil {
  222. t.Fatalf("failed to lookup message: %s", err)
  223. }
  224. params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
  225. if err != nil {
  226. t.Fatalf("failed to convert message to query parameters: %s", err)
  227. }
  228. // avoid checking Items for array types
  229. for i := range params {
  230. params[i].Items = nil
  231. }
  232. if !reflect.DeepEqual(params, test.Params) {
  233. t.Errorf("expected %v, got %v", test.Params, params)
  234. }
  235. }
  236. }
  237. func TestMessageToQueryParameters(t *testing.T) {
  238. type test struct {
  239. MsgDescs []*descriptorpb.DescriptorProto
  240. Message string
  241. Params []openapiParameterObject
  242. }
  243. tests := []test{
  244. {
  245. MsgDescs: []*descriptorpb.DescriptorProto{
  246. {
  247. Name: proto.String("ExampleMessage"),
  248. Field: []*descriptorpb.FieldDescriptorProto{
  249. {
  250. Name: proto.String("a"),
  251. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  252. Number: proto.Int32(1),
  253. },
  254. {
  255. Name: proto.String("b"),
  256. Type: descriptorpb.FieldDescriptorProto_TYPE_DOUBLE.Enum(),
  257. Number: proto.Int32(2),
  258. },
  259. {
  260. Name: proto.String("c"),
  261. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  262. Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  263. Number: proto.Int32(3),
  264. },
  265. },
  266. },
  267. },
  268. Message: "ExampleMessage",
  269. Params: []openapiParameterObject{
  270. {
  271. Name: "a",
  272. In: "query",
  273. Required: false,
  274. Type: "string",
  275. },
  276. {
  277. Name: "b",
  278. In: "query",
  279. Required: false,
  280. Type: "number",
  281. Format: "double",
  282. },
  283. {
  284. Name: "c",
  285. In: "query",
  286. Required: false,
  287. Type: "array",
  288. CollectionFormat: "multi",
  289. },
  290. },
  291. },
  292. {
  293. MsgDescs: []*descriptorpb.DescriptorProto{
  294. {
  295. Name: proto.String("ExampleMessage"),
  296. Field: []*descriptorpb.FieldDescriptorProto{
  297. {
  298. Name: proto.String("nested"),
  299. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  300. TypeName: proto.String(".example.Nested"),
  301. Number: proto.Int32(1),
  302. },
  303. },
  304. },
  305. {
  306. Name: proto.String("Nested"),
  307. Field: []*descriptorpb.FieldDescriptorProto{
  308. {
  309. Name: proto.String("a"),
  310. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  311. Number: proto.Int32(1),
  312. },
  313. {
  314. Name: proto.String("deep"),
  315. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  316. TypeName: proto.String(".example.Nested.DeepNested"),
  317. Number: proto.Int32(2),
  318. },
  319. },
  320. NestedType: []*descriptorpb.DescriptorProto{{
  321. Name: proto.String("DeepNested"),
  322. Field: []*descriptorpb.FieldDescriptorProto{
  323. {
  324. Name: proto.String("b"),
  325. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  326. Number: proto.Int32(1),
  327. },
  328. {
  329. Name: proto.String("c"),
  330. Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
  331. TypeName: proto.String(".example.Nested.DeepNested.DeepEnum"),
  332. Number: proto.Int32(2),
  333. },
  334. },
  335. EnumType: []*descriptorpb.EnumDescriptorProto{
  336. {
  337. Name: proto.String("DeepEnum"),
  338. Value: []*descriptorpb.EnumValueDescriptorProto{
  339. {Name: proto.String("FALSE"), Number: proto.Int32(0)},
  340. {Name: proto.String("TRUE"), Number: proto.Int32(1)},
  341. },
  342. },
  343. },
  344. }},
  345. },
  346. },
  347. Message: "ExampleMessage",
  348. Params: []openapiParameterObject{
  349. {
  350. Name: "nested.a",
  351. In: "query",
  352. Required: false,
  353. Type: "string",
  354. },
  355. {
  356. Name: "nested.deep.b",
  357. In: "query",
  358. Required: false,
  359. Type: "string",
  360. },
  361. {
  362. Name: "nested.deep.c",
  363. In: "query",
  364. Required: false,
  365. Type: "string",
  366. Enum: []string{"FALSE", "TRUE"},
  367. Default: "FALSE",
  368. },
  369. },
  370. },
  371. }
  372. for _, test := range tests {
  373. reg := descriptor.NewRegistry()
  374. msgs := []*descriptor.Message{}
  375. for _, msgdesc := range test.MsgDescs {
  376. msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  377. }
  378. file := descriptor.File{
  379. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  380. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  381. Name: proto.String("example.proto"),
  382. Package: proto.String("example"),
  383. Dependency: []string{},
  384. MessageType: test.MsgDescs,
  385. Service: []*descriptorpb.ServiceDescriptorProto{},
  386. Options: &descriptorpb.FileOptions{
  387. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  388. },
  389. },
  390. GoPkg: descriptor.GoPackage{
  391. Path: "example.com/path/to/example/example.pb",
  392. Name: "example_pb",
  393. },
  394. Messages: msgs,
  395. }
  396. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  397. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  398. })
  399. if err != nil {
  400. t.Fatalf("failed to load code generator request: %v", err)
  401. }
  402. message, err := reg.LookupMsg("", ".example."+test.Message)
  403. if err != nil {
  404. t.Fatalf("failed to lookup message: %s", err)
  405. }
  406. params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
  407. if err != nil {
  408. t.Fatalf("failed to convert message to query parameters: %s", err)
  409. }
  410. // avoid checking Items for array types
  411. for i := range params {
  412. params[i].Items = nil
  413. }
  414. if !reflect.DeepEqual(params, test.Params) {
  415. t.Errorf("expected %v, got %v", test.Params, params)
  416. }
  417. }
  418. }
  419. // TestMessagetoQueryParametersNoRecursive, is a check that cyclical references between messages
  420. //
  421. // are not falsely detected given previous known edge-cases.
  422. func TestMessageToQueryParametersNoRecursive(t *testing.T) {
  423. type test struct {
  424. MsgDescs []*descriptorpb.DescriptorProto
  425. Message string
  426. }
  427. tests := []test{
  428. // First test:
  429. // Here is a message that has two of another message adjacent to one another in a nested message.
  430. // There is no loop but this was previouly falsely flagged as a cycle.
  431. // Example proto:
  432. // message NonRecursiveMessage {
  433. // string field = 1;
  434. // }
  435. // message BaseMessage {
  436. // NonRecursiveMessage first = 1;
  437. // NonRecursiveMessage second = 2;
  438. // }
  439. // message QueryMessage {
  440. // BaseMessage first = 1;
  441. // string second = 2;
  442. // }
  443. {
  444. MsgDescs: []*descriptorpb.DescriptorProto{
  445. {
  446. Name: proto.String("QueryMessage"),
  447. Field: []*descriptorpb.FieldDescriptorProto{
  448. {
  449. Name: proto.String("first"),
  450. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  451. TypeName: proto.String(".example.BaseMessage"),
  452. Number: proto.Int32(1),
  453. },
  454. {
  455. Name: proto.String("second"),
  456. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  457. Number: proto.Int32(2),
  458. },
  459. },
  460. },
  461. {
  462. Name: proto.String("BaseMessage"),
  463. Field: []*descriptorpb.FieldDescriptorProto{
  464. {
  465. Name: proto.String("first"),
  466. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  467. TypeName: proto.String(".example.NonRecursiveMessage"),
  468. Number: proto.Int32(1),
  469. },
  470. {
  471. Name: proto.String("second"),
  472. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  473. TypeName: proto.String(".example.NonRecursiveMessage"),
  474. Number: proto.Int32(2),
  475. },
  476. },
  477. },
  478. // Note there is no recursive nature to this message
  479. {
  480. Name: proto.String("NonRecursiveMessage"),
  481. Field: []*descriptorpb.FieldDescriptorProto{
  482. {
  483. Name: proto.String("field"),
  484. //Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  485. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  486. Number: proto.Int32(1),
  487. },
  488. },
  489. },
  490. },
  491. Message: "QueryMessage",
  492. },
  493. }
  494. for _, test := range tests {
  495. reg := descriptor.NewRegistry()
  496. msgs := []*descriptor.Message{}
  497. for _, msgdesc := range test.MsgDescs {
  498. msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  499. }
  500. file := descriptor.File{
  501. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  502. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  503. Name: proto.String("example.proto"),
  504. Package: proto.String("example"),
  505. Dependency: []string{},
  506. MessageType: test.MsgDescs,
  507. Service: []*descriptorpb.ServiceDescriptorProto{},
  508. Options: &descriptorpb.FileOptions{
  509. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  510. },
  511. },
  512. GoPkg: descriptor.GoPackage{
  513. Path: "example.com/path/to/example/example.pb",
  514. Name: "example_pb",
  515. },
  516. Messages: msgs,
  517. }
  518. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  519. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  520. })
  521. if err != nil {
  522. t.Fatalf("failed to load code generator request: %v", err)
  523. }
  524. message, err := reg.LookupMsg("", ".example."+test.Message)
  525. if err != nil {
  526. t.Fatalf("failed to lookup message: %s", err)
  527. }
  528. _, err = messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
  529. if err != nil {
  530. t.Fatalf("No recursion error should be thrown: %s", err)
  531. }
  532. }
  533. }
  534. // TestMessagetoQueryParametersRecursive, is a check that cyclical references between messages
  535. //
  536. // are handled gracefully. The goal is to insure that attempts to add messages with cyclical
  537. // references to query-parameters returns an error message.
  538. func TestMessageToQueryParametersRecursive(t *testing.T) {
  539. type test struct {
  540. MsgDescs []*descriptorpb.DescriptorProto
  541. Message string
  542. }
  543. tests := []test{
  544. // First test:
  545. // Here we test that a message that references it self through a field will return an error.
  546. // Example proto:
  547. // message DirectRecursiveMessage {
  548. // DirectRecursiveMessage nested = 1;
  549. // }
  550. {
  551. MsgDescs: []*descriptorpb.DescriptorProto{
  552. {
  553. Name: proto.String("DirectRecursiveMessage"),
  554. Field: []*descriptorpb.FieldDescriptorProto{
  555. {
  556. Name: proto.String("nested"),
  557. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  558. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  559. TypeName: proto.String(".example.DirectRecursiveMessage"),
  560. Number: proto.Int32(1),
  561. },
  562. },
  563. },
  564. },
  565. Message: "DirectRecursiveMessage",
  566. },
  567. // Second test:
  568. // Here we test that a cycle through multiple messages is detected and that an error is returned.
  569. // Sample:
  570. // message Root { NodeMessage nested = 1; }
  571. // message NodeMessage { CycleMessage nested = 1; }
  572. // message CycleMessage { Root nested = 1; }
  573. {
  574. MsgDescs: []*descriptorpb.DescriptorProto{
  575. {
  576. Name: proto.String("RootMessage"),
  577. Field: []*descriptorpb.FieldDescriptorProto{
  578. {
  579. Name: proto.String("nested"),
  580. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  581. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  582. TypeName: proto.String(".example.NodeMessage"),
  583. Number: proto.Int32(1),
  584. },
  585. },
  586. },
  587. {
  588. Name: proto.String("NodeMessage"),
  589. Field: []*descriptorpb.FieldDescriptorProto{
  590. {
  591. Name: proto.String("nested"),
  592. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  593. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  594. TypeName: proto.String(".example.CycleMessage"),
  595. Number: proto.Int32(1),
  596. },
  597. },
  598. },
  599. {
  600. Name: proto.String("CycleMessage"),
  601. Field: []*descriptorpb.FieldDescriptorProto{
  602. {
  603. Name: proto.String("nested"),
  604. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  605. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  606. TypeName: proto.String(".example.RootMessage"),
  607. Number: proto.Int32(1),
  608. },
  609. },
  610. },
  611. },
  612. Message: "RootMessage",
  613. },
  614. }
  615. for _, test := range tests {
  616. reg := descriptor.NewRegistry()
  617. msgs := []*descriptor.Message{}
  618. for _, msgdesc := range test.MsgDescs {
  619. msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  620. }
  621. file := descriptor.File{
  622. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  623. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  624. Name: proto.String("example.proto"),
  625. Package: proto.String("example"),
  626. Dependency: []string{},
  627. MessageType: test.MsgDescs,
  628. Service: []*descriptorpb.ServiceDescriptorProto{},
  629. Options: &descriptorpb.FileOptions{
  630. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  631. },
  632. },
  633. GoPkg: descriptor.GoPackage{
  634. Path: "example.com/path/to/example/example.pb",
  635. Name: "example_pb",
  636. },
  637. Messages: msgs,
  638. }
  639. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  640. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  641. })
  642. if err != nil {
  643. t.Fatalf("failed to load code generator request: %v", err)
  644. }
  645. message, err := reg.LookupMsg("", ".example."+test.Message)
  646. if err != nil {
  647. t.Fatalf("failed to lookup message: %s", err)
  648. }
  649. _, err = messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
  650. if err == nil {
  651. t.Fatalf("It should not be allowed to have recursive query parameters")
  652. }
  653. }
  654. }
  655. func TestMessageToQueryParametersWithJsonName(t *testing.T) {
  656. type test struct {
  657. MsgDescs []*descriptorpb.DescriptorProto
  658. Message string
  659. Params []openapiParameterObject
  660. }
  661. tests := []test{
  662. {
  663. MsgDescs: []*descriptorpb.DescriptorProto{
  664. {
  665. Name: proto.String("ExampleMessage"),
  666. Field: []*descriptorpb.FieldDescriptorProto{
  667. {
  668. Name: proto.String("test_field_a"),
  669. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  670. Number: proto.Int32(1),
  671. JsonName: proto.String("testFieldA"),
  672. },
  673. },
  674. },
  675. },
  676. Message: "ExampleMessage",
  677. Params: []openapiParameterObject{
  678. {
  679. Name: "testFieldA",
  680. In: "query",
  681. Required: false,
  682. Type: "string",
  683. },
  684. },
  685. },
  686. {
  687. MsgDescs: []*descriptorpb.DescriptorProto{
  688. {
  689. Name: proto.String("SubMessage"),
  690. Field: []*descriptorpb.FieldDescriptorProto{
  691. {
  692. Name: proto.String("test_field_a"),
  693. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  694. Number: proto.Int32(1),
  695. JsonName: proto.String("testFieldA"),
  696. },
  697. },
  698. },
  699. {
  700. Name: proto.String("ExampleMessage"),
  701. Field: []*descriptorpb.FieldDescriptorProto{
  702. {
  703. Name: proto.String("sub_message"),
  704. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  705. TypeName: proto.String(".example.SubMessage"),
  706. Number: proto.Int32(1),
  707. JsonName: proto.String("subMessage"),
  708. },
  709. },
  710. },
  711. },
  712. Message: "ExampleMessage",
  713. Params: []openapiParameterObject{
  714. {
  715. Name: "subMessage.testFieldA",
  716. In: "query",
  717. Required: false,
  718. Type: "string",
  719. },
  720. },
  721. },
  722. }
  723. for _, test := range tests {
  724. reg := descriptor.NewRegistry()
  725. reg.SetUseJSONNamesForFields(true)
  726. msgs := []*descriptor.Message{}
  727. for _, msgdesc := range test.MsgDescs {
  728. msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  729. }
  730. file := descriptor.File{
  731. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  732. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  733. Name: proto.String("example.proto"),
  734. Package: proto.String("example"),
  735. Dependency: []string{},
  736. MessageType: test.MsgDescs,
  737. Service: []*descriptorpb.ServiceDescriptorProto{},
  738. Options: &descriptorpb.FileOptions{
  739. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  740. },
  741. },
  742. GoPkg: descriptor.GoPackage{
  743. Path: "example.com/path/to/example/example.pb",
  744. Name: "example_pb",
  745. },
  746. Messages: msgs,
  747. }
  748. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  749. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  750. })
  751. if err != nil {
  752. t.Fatalf("failed to load code generator request: %v", err)
  753. }
  754. message, err := reg.LookupMsg("", ".example."+test.Message)
  755. if err != nil {
  756. t.Fatalf("failed to lookup message: %s", err)
  757. }
  758. params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
  759. if err != nil {
  760. t.Fatalf("failed to convert message to query parameters: %s", err)
  761. }
  762. if !reflect.DeepEqual(params, test.Params) {
  763. t.Errorf("expected %v, got %v", test.Params, params)
  764. }
  765. }
  766. }
  767. func TestMessageToQueryParametersWellKnownTypes(t *testing.T) {
  768. type test struct {
  769. MsgDescs []*descriptorpb.DescriptorProto
  770. WellKnownMsgDescs []*descriptorpb.DescriptorProto
  771. Message string
  772. Params []openapiParameterObject
  773. }
  774. tests := []test{
  775. {
  776. MsgDescs: []*descriptorpb.DescriptorProto{
  777. {
  778. Name: proto.String("ExampleMessage"),
  779. Field: []*descriptorpb.FieldDescriptorProto{
  780. {
  781. Name: proto.String("a_field_mask"),
  782. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  783. TypeName: proto.String(".google.protobuf.FieldMask"),
  784. Number: proto.Int32(1),
  785. },
  786. {
  787. Name: proto.String("a_timestamp"),
  788. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  789. TypeName: proto.String(".google.protobuf.Timestamp"),
  790. Number: proto.Int32(2),
  791. },
  792. },
  793. },
  794. },
  795. WellKnownMsgDescs: []*descriptorpb.DescriptorProto{
  796. {
  797. Name: proto.String("FieldMask"),
  798. Field: []*descriptorpb.FieldDescriptorProto{
  799. {
  800. Name: proto.String("paths"),
  801. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  802. Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  803. Number: proto.Int32(1),
  804. },
  805. },
  806. },
  807. {
  808. Name: proto.String("Timestamp"),
  809. Field: []*descriptorpb.FieldDescriptorProto{
  810. {
  811. Name: proto.String("seconds"),
  812. Type: descriptorpb.FieldDescriptorProto_TYPE_INT64.Enum(),
  813. Number: proto.Int32(1),
  814. },
  815. {
  816. Name: proto.String("nanos"),
  817. Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  818. Number: proto.Int32(2),
  819. },
  820. },
  821. },
  822. },
  823. Message: "ExampleMessage",
  824. Params: []openapiParameterObject{
  825. {
  826. Name: "a_field_mask",
  827. In: "query",
  828. Required: false,
  829. Type: "string",
  830. },
  831. {
  832. Name: "a_timestamp",
  833. In: "query",
  834. Required: false,
  835. Type: "string",
  836. Format: "date-time",
  837. },
  838. },
  839. },
  840. }
  841. for _, test := range tests {
  842. reg := descriptor.NewRegistry()
  843. reg.SetEnumsAsInts(true)
  844. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  845. ProtoFile: []*descriptorpb.FileDescriptorProto{
  846. {
  847. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  848. Name: proto.String("google/well_known.proto"),
  849. Package: proto.String("google.protobuf"),
  850. Dependency: []string{},
  851. MessageType: test.WellKnownMsgDescs,
  852. Service: []*descriptorpb.ServiceDescriptorProto{},
  853. Options: &descriptorpb.FileOptions{
  854. GoPackage: proto.String("google/well_known"),
  855. },
  856. },
  857. {
  858. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  859. Name: proto.String("acme/example.proto"),
  860. Package: proto.String("example"),
  861. Dependency: []string{"google/well_known.proto"},
  862. MessageType: test.MsgDescs,
  863. Service: []*descriptorpb.ServiceDescriptorProto{},
  864. Options: &descriptorpb.FileOptions{
  865. GoPackage: proto.String("acme/example"),
  866. },
  867. },
  868. },
  869. })
  870. if err != nil {
  871. t.Fatalf("failed to load CodeGeneratorRequest: %v", err)
  872. }
  873. message, err := reg.LookupMsg("", ".example."+test.Message)
  874. if err != nil {
  875. t.Fatalf("failed to lookup message: %s", err)
  876. }
  877. params, err := messageToQueryParameters(message, reg, []descriptor.Parameter{}, nil)
  878. if err != nil {
  879. t.Fatalf("failed to convert message to query parameters: %s", err)
  880. }
  881. if !reflect.DeepEqual(params, test.Params) {
  882. t.Errorf("expected %v, got %v", test.Params, params)
  883. }
  884. }
  885. }
  886. func TestApplyTemplateSimple(t *testing.T) {
  887. msgdesc := &descriptorpb.DescriptorProto{
  888. Name: proto.String("ExampleMessage"),
  889. }
  890. meth := &descriptorpb.MethodDescriptorProto{
  891. Name: proto.String("Example"),
  892. InputType: proto.String("ExampleMessage"),
  893. OutputType: proto.String("ExampleMessage"),
  894. }
  895. svc := &descriptorpb.ServiceDescriptorProto{
  896. Name: proto.String("ExampleService"),
  897. Method: []*descriptorpb.MethodDescriptorProto{meth},
  898. }
  899. msg := &descriptor.Message{
  900. DescriptorProto: msgdesc,
  901. }
  902. file := descriptor.File{
  903. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  904. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  905. Name: proto.String("example.proto"),
  906. Package: proto.String("example"),
  907. MessageType: []*descriptorpb.DescriptorProto{msgdesc},
  908. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  909. Options: &descriptorpb.FileOptions{
  910. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  911. },
  912. },
  913. GoPkg: descriptor.GoPackage{
  914. Path: "example.com/path/to/example/example.pb",
  915. Name: "example_pb",
  916. },
  917. Messages: []*descriptor.Message{msg},
  918. Services: []*descriptor.Service{
  919. {
  920. ServiceDescriptorProto: svc,
  921. Methods: []*descriptor.Method{
  922. {
  923. MethodDescriptorProto: meth,
  924. RequestType: msg,
  925. ResponseType: msg,
  926. Bindings: []*descriptor.Binding{
  927. {
  928. HTTPMethod: "GET",
  929. Body: &descriptor.Body{FieldPath: nil},
  930. PathTmpl: httprule.Template{
  931. Version: 1,
  932. OpCodes: []int{0, 0},
  933. Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  934. },
  935. },
  936. },
  937. },
  938. },
  939. },
  940. },
  941. }
  942. reg := descriptor.NewRegistry()
  943. if err := AddErrorDefs(reg); err != nil {
  944. t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  945. return
  946. }
  947. fileCL := crossLinkFixture(&file)
  948. err := reg.Load(reqFromFile(fileCL))
  949. if err != nil {
  950. t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  951. return
  952. }
  953. result, err := applyTemplate(param{File: fileCL, reg: reg})
  954. if err != nil {
  955. t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  956. return
  957. }
  958. if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
  959. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  960. }
  961. if want, is, name := "", result.BasePath, "BasePath"; !reflect.DeepEqual(is, want) {
  962. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  963. }
  964. if want, is, name := ([]string)(nil), result.Schemes, "Schemes"; !reflect.DeepEqual(is, want) {
  965. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  966. }
  967. if want, is, name := []string{"application/json"}, result.Consumes, "Consumes"; !reflect.DeepEqual(is, want) {
  968. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  969. }
  970. if want, is, name := []string{"application/json"}, result.Produces, "Produces"; !reflect.DeepEqual(is, want) {
  971. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  972. }
  973. // If there was a failure, print out the input and the json result for debugging.
  974. if t.Failed() {
  975. t.Errorf("had: %s", file)
  976. t.Errorf("got: %s", fmt.Sprint(result))
  977. }
  978. }
  979. func TestApplyTemplateMultiService(t *testing.T) {
  980. msgdesc := &descriptorpb.DescriptorProto{
  981. Name: proto.String("ExampleMessage"),
  982. }
  983. meth := &descriptorpb.MethodDescriptorProto{
  984. Name: proto.String("Example"),
  985. InputType: proto.String("ExampleMessage"),
  986. OutputType: proto.String("ExampleMessage"),
  987. }
  988. // Create two services that have the same method name. We will test that the
  989. // operation IDs are different
  990. svc := &descriptorpb.ServiceDescriptorProto{
  991. Name: proto.String("ExampleService"),
  992. Method: []*descriptorpb.MethodDescriptorProto{meth},
  993. }
  994. svc2 := &descriptorpb.ServiceDescriptorProto{
  995. Name: proto.String("OtherService"),
  996. Method: []*descriptorpb.MethodDescriptorProto{meth},
  997. }
  998. msg := &descriptor.Message{
  999. DescriptorProto: msgdesc,
  1000. }
  1001. file := descriptor.File{
  1002. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1003. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1004. Name: proto.String("example.proto"),
  1005. Package: proto.String("example"),
  1006. MessageType: []*descriptorpb.DescriptorProto{msgdesc},
  1007. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  1008. Options: &descriptorpb.FileOptions{
  1009. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1010. },
  1011. },
  1012. GoPkg: descriptor.GoPackage{
  1013. Path: "example.com/path/to/example/example.pb",
  1014. Name: "example_pb",
  1015. },
  1016. Messages: []*descriptor.Message{msg},
  1017. Services: []*descriptor.Service{
  1018. {
  1019. ServiceDescriptorProto: svc,
  1020. Methods: []*descriptor.Method{
  1021. {
  1022. MethodDescriptorProto: meth,
  1023. RequestType: msg,
  1024. ResponseType: msg,
  1025. Bindings: []*descriptor.Binding{
  1026. {
  1027. HTTPMethod: "GET",
  1028. Body: &descriptor.Body{FieldPath: nil},
  1029. PathTmpl: httprule.Template{
  1030. Version: 1,
  1031. OpCodes: []int{0, 0},
  1032. Template: "/v1/echo",
  1033. },
  1034. },
  1035. },
  1036. },
  1037. },
  1038. },
  1039. {
  1040. ServiceDescriptorProto: svc2,
  1041. Methods: []*descriptor.Method{
  1042. {
  1043. MethodDescriptorProto: meth,
  1044. RequestType: msg,
  1045. ResponseType: msg,
  1046. Bindings: []*descriptor.Binding{
  1047. {
  1048. HTTPMethod: "GET",
  1049. Body: &descriptor.Body{FieldPath: nil},
  1050. PathTmpl: httprule.Template{
  1051. Version: 1,
  1052. OpCodes: []int{0, 0},
  1053. Template: "/v1/ping",
  1054. },
  1055. },
  1056. },
  1057. },
  1058. },
  1059. },
  1060. },
  1061. }
  1062. reg := descriptor.NewRegistry()
  1063. if err := AddErrorDefs(reg); err != nil {
  1064. t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  1065. return
  1066. }
  1067. fileCL := crossLinkFixture(&file)
  1068. err := reg.Load(reqFromFile(fileCL))
  1069. if err != nil {
  1070. t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  1071. return
  1072. }
  1073. result, err := applyTemplate(param{File: fileCL, reg: reg})
  1074. if err != nil {
  1075. t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1076. return
  1077. }
  1078. // Check that the two services have unique operation IDs even though they
  1079. // have the same method name.
  1080. if want, is := "ExampleService_Example", result.Paths["/v1/echo"].Get.OperationID; !reflect.DeepEqual(is, want) {
  1081. t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
  1082. }
  1083. if want, is := "OtherService_Example", result.Paths["/v1/ping"].Get.OperationID; !reflect.DeepEqual(is, want) {
  1084. t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", file, is, want)
  1085. }
  1086. // If there was a failure, print out the input and the json result for debugging.
  1087. if t.Failed() {
  1088. t.Errorf("had: %s", file)
  1089. t.Errorf("got: %s", fmt.Sprint(result))
  1090. }
  1091. }
  1092. func TestApplyTemplateOverrideOperationID(t *testing.T) {
  1093. newFile := func() *descriptor.File {
  1094. msgdesc := &descriptorpb.DescriptorProto{
  1095. Name: proto.String("ExampleMessage"),
  1096. }
  1097. meth := &descriptorpb.MethodDescriptorProto{
  1098. Name: proto.String("Example"),
  1099. InputType: proto.String("ExampleMessage"),
  1100. OutputType: proto.String("ExampleMessage"),
  1101. Options: &descriptorpb.MethodOptions{},
  1102. }
  1103. svc := &descriptorpb.ServiceDescriptorProto{
  1104. Name: proto.String("ExampleService"),
  1105. Method: []*descriptorpb.MethodDescriptorProto{meth},
  1106. }
  1107. msg := &descriptor.Message{
  1108. DescriptorProto: msgdesc,
  1109. }
  1110. return &descriptor.File{
  1111. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1112. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1113. Name: proto.String("example.proto"),
  1114. Package: proto.String("example"),
  1115. MessageType: []*descriptorpb.DescriptorProto{msgdesc},
  1116. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  1117. Options: &descriptorpb.FileOptions{
  1118. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1119. },
  1120. },
  1121. GoPkg: descriptor.GoPackage{
  1122. Path: "example.com/path/to/example/example.pb",
  1123. Name: "example_pb",
  1124. },
  1125. Messages: []*descriptor.Message{msg},
  1126. Services: []*descriptor.Service{
  1127. {
  1128. ServiceDescriptorProto: svc,
  1129. Methods: []*descriptor.Method{
  1130. {
  1131. MethodDescriptorProto: meth,
  1132. RequestType: msg,
  1133. ResponseType: msg,
  1134. Bindings: []*descriptor.Binding{
  1135. {
  1136. HTTPMethod: "GET",
  1137. Body: &descriptor.Body{FieldPath: nil},
  1138. PathTmpl: httprule.Template{
  1139. Version: 1,
  1140. OpCodes: []int{0, 0},
  1141. Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  1142. },
  1143. },
  1144. },
  1145. },
  1146. },
  1147. },
  1148. },
  1149. }
  1150. }
  1151. verifyTemplateFromReq := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File, opts *openapiconfig.OpenAPIOptions) {
  1152. if err := AddErrorDefs(reg); err != nil {
  1153. t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  1154. return
  1155. }
  1156. fileCL := crossLinkFixture(file)
  1157. err := reg.Load(reqFromFile(fileCL))
  1158. if err != nil {
  1159. t.Errorf("reg.Load(%#v) failed with %v; want success", *file, err)
  1160. return
  1161. }
  1162. if opts != nil {
  1163. if err := reg.RegisterOpenAPIOptions(opts); err != nil {
  1164. t.Fatalf("failed to register OpenAPI options: %s", err)
  1165. }
  1166. }
  1167. result, err := applyTemplate(param{File: fileCL, reg: reg})
  1168. if err != nil {
  1169. t.Errorf("applyTemplate(%#v) failed with %v; want success", *file, err)
  1170. return
  1171. }
  1172. if want, is := "MyExample", result.Paths["/v1/echo"].Get.OperationID; !reflect.DeepEqual(is, want) {
  1173. t.Errorf("applyTemplate(%#v).Paths[0].Get.OperationID = %s want to be %s", *file, is, want)
  1174. }
  1175. // If there was a failure, print out the input and the json result for debugging.
  1176. if t.Failed() {
  1177. t.Errorf("had: %s", *file)
  1178. t.Errorf("got: %s", fmt.Sprint(result))
  1179. }
  1180. }
  1181. openapiOperation := openapi_options.Operation{
  1182. OperationId: "MyExample",
  1183. }
  1184. t.Run("verify override via method option", func(t *testing.T) {
  1185. file := newFile()
  1186. proto.SetExtension(proto.Message(file.Services[0].Methods[0].MethodDescriptorProto.Options),
  1187. openapi_options.E_Openapiv2Operation, &openapiOperation)
  1188. reg := descriptor.NewRegistry()
  1189. verifyTemplateFromReq(t, reg, file, nil)
  1190. })
  1191. t.Run("verify override options annotations", func(t *testing.T) {
  1192. file := newFile()
  1193. reg := descriptor.NewRegistry()
  1194. opts := &openapiconfig.OpenAPIOptions{
  1195. Method: []*openapiconfig.OpenAPIMethodOption{
  1196. {
  1197. Method: "example.ExampleService.Example",
  1198. Option: &openapiOperation,
  1199. },
  1200. },
  1201. }
  1202. verifyTemplateFromReq(t, reg, file, opts)
  1203. })
  1204. }
  1205. func TestApplyTemplateExtensions(t *testing.T) {
  1206. newFile := func() *descriptor.File {
  1207. msgdesc := &descriptorpb.DescriptorProto{
  1208. Name: proto.String("ExampleMessage"),
  1209. }
  1210. meth := &descriptorpb.MethodDescriptorProto{
  1211. Name: proto.String("Example"),
  1212. InputType: proto.String("ExampleMessage"),
  1213. OutputType: proto.String("ExampleMessage"),
  1214. Options: &descriptorpb.MethodOptions{},
  1215. }
  1216. svc := &descriptorpb.ServiceDescriptorProto{
  1217. Name: proto.String("ExampleService"),
  1218. Method: []*descriptorpb.MethodDescriptorProto{meth},
  1219. }
  1220. msg := &descriptor.Message{
  1221. DescriptorProto: msgdesc,
  1222. }
  1223. return &descriptor.File{
  1224. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1225. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1226. Name: proto.String("example.proto"),
  1227. Package: proto.String("example"),
  1228. MessageType: []*descriptorpb.DescriptorProto{msgdesc},
  1229. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  1230. Options: &descriptorpb.FileOptions{
  1231. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1232. },
  1233. },
  1234. GoPkg: descriptor.GoPackage{
  1235. Path: "example.com/path/to/example/example.pb",
  1236. Name: "example_pb",
  1237. },
  1238. Messages: []*descriptor.Message{msg},
  1239. Services: []*descriptor.Service{
  1240. {
  1241. ServiceDescriptorProto: svc,
  1242. Methods: []*descriptor.Method{
  1243. {
  1244. MethodDescriptorProto: meth,
  1245. RequestType: msg,
  1246. ResponseType: msg,
  1247. Bindings: []*descriptor.Binding{
  1248. {
  1249. HTTPMethod: "GET",
  1250. Body: &descriptor.Body{FieldPath: nil},
  1251. PathTmpl: httprule.Template{
  1252. Version: 1,
  1253. OpCodes: []int{0, 0},
  1254. Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  1255. },
  1256. },
  1257. },
  1258. },
  1259. },
  1260. },
  1261. },
  1262. }
  1263. }
  1264. swagger := openapi_options.Swagger{
  1265. Info: &openapi_options.Info{
  1266. Title: "test",
  1267. Extensions: map[string]*structpb.Value{
  1268. "x-info-extension": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1269. },
  1270. },
  1271. Extensions: map[string]*structpb.Value{
  1272. "x-foo": {Kind: &structpb.Value_StringValue{StringValue: "bar"}},
  1273. "x-bar": {Kind: &structpb.Value_ListValue{ListValue: &structpb.ListValue{
  1274. Values: []*structpb.Value{{Kind: &structpb.Value_StringValue{StringValue: "baz"}}},
  1275. }}},
  1276. },
  1277. SecurityDefinitions: &openapi_options.SecurityDefinitions{
  1278. Security: map[string]*openapi_options.SecurityScheme{
  1279. "somescheme": {
  1280. Extensions: map[string]*structpb.Value{
  1281. "x-security-baz": {Kind: &structpb.Value_BoolValue{BoolValue: true}},
  1282. },
  1283. },
  1284. },
  1285. },
  1286. }
  1287. openapiOperation := openapi_options.Operation{
  1288. Responses: map[string]*openapi_options.Response{
  1289. "200": {
  1290. Extensions: map[string]*structpb.Value{
  1291. "x-resp-id": {Kind: &structpb.Value_StringValue{StringValue: "resp1000"}},
  1292. },
  1293. },
  1294. },
  1295. Extensions: map[string]*structpb.Value{
  1296. "x-op-foo": {Kind: &structpb.Value_StringValue{StringValue: "baz"}},
  1297. },
  1298. }
  1299. verifyTemplateExtensions := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File,
  1300. opts *openapiconfig.OpenAPIOptions) {
  1301. if err := AddErrorDefs(reg); err != nil {
  1302. t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  1303. return
  1304. }
  1305. fileCL := crossLinkFixture(file)
  1306. err := reg.Load(reqFromFile(fileCL))
  1307. if err != nil {
  1308. t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  1309. return
  1310. }
  1311. if opts != nil {
  1312. if err := reg.RegisterOpenAPIOptions(opts); err != nil {
  1313. t.Fatalf("failed to register OpenAPI annotations: %s", err)
  1314. }
  1315. }
  1316. result, err := applyTemplate(param{File: fileCL, reg: reg})
  1317. if err != nil {
  1318. t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1319. return
  1320. }
  1321. if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
  1322. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1323. }
  1324. if got, want := len(result.extensions), 2; got != want {
  1325. t.Fatalf("len(applyTemplate(%#v).Extensions) = %d want to be %d", file, got, want)
  1326. }
  1327. if got, want := result.extensions[0].key, "x-bar"; got != want {
  1328. t.Errorf("applyTemplate(%#v).Extensions[0].key = %s want to be %s", file, got, want)
  1329. }
  1330. if got, want := result.extensions[1].key, "x-foo"; got != want {
  1331. t.Errorf("applyTemplate(%#v).Extensions[1].key = %s want to be %s", file, got, want)
  1332. }
  1333. {
  1334. var got []string
  1335. err = marshaler.Unmarshal(result.extensions[0].value, &got)
  1336. if err != nil {
  1337. t.Fatalf("marshaler.Unmarshal failed: %v", err)
  1338. }
  1339. want := []string{"baz"}
  1340. if diff := cmp.Diff(got, want); diff != "" {
  1341. t.Errorf(diff)
  1342. }
  1343. }
  1344. {
  1345. var got string
  1346. err = marshaler.Unmarshal(result.extensions[1].value, &got)
  1347. if err != nil {
  1348. t.Fatalf("marshaler.Unmarshal failed: %v", err)
  1349. }
  1350. want := "bar"
  1351. if diff := cmp.Diff(got, want); diff != "" {
  1352. t.Errorf(diff)
  1353. }
  1354. }
  1355. var scheme openapiSecuritySchemeObject
  1356. for _, v := range result.SecurityDefinitions {
  1357. scheme = v
  1358. }
  1359. if want, is, name := []extension{
  1360. {key: "x-security-baz", value: json.RawMessage("true")},
  1361. }, scheme.extensions, "SecurityScheme.Extensions"; !reflect.DeepEqual(is, want) {
  1362. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1363. }
  1364. if want, is, name := []extension{
  1365. {key: "x-info-extension", value: json.RawMessage("\"bar\"")},
  1366. }, result.Info.extensions, "Info.Extensions"; !reflect.DeepEqual(is, want) {
  1367. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1368. }
  1369. var operation *openapiOperationObject
  1370. var response openapiResponseObject
  1371. for _, v := range result.Paths {
  1372. operation = v.Get
  1373. response = v.Get.Responses["200"]
  1374. }
  1375. if want, is, name := []extension{
  1376. {key: "x-op-foo", value: json.RawMessage("\"baz\"")},
  1377. }, operation.extensions, "operation.Extensions"; !reflect.DeepEqual(is, want) {
  1378. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1379. }
  1380. if want, is, name := []extension{
  1381. {key: "x-resp-id", value: json.RawMessage("\"resp1000\"")},
  1382. }, response.extensions, "response.Extensions"; !reflect.DeepEqual(is, want) {
  1383. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1384. }
  1385. }
  1386. t.Run("verify template options set via proto options", func(t *testing.T) {
  1387. file := newFile()
  1388. proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), openapi_options.E_Openapiv2Swagger, &swagger)
  1389. proto.SetExtension(proto.Message(file.Services[0].Methods[0].Options), openapi_options.E_Openapiv2Operation, &openapiOperation)
  1390. reg := descriptor.NewRegistry()
  1391. verifyTemplateExtensions(t, reg, file, nil)
  1392. })
  1393. t.Run("verify template options set via annotations", func(t *testing.T) {
  1394. file := newFile()
  1395. opts := &openapiconfig.OpenAPIOptions{
  1396. File: []*openapiconfig.OpenAPIFileOption{
  1397. {
  1398. File: "example.proto",
  1399. Option: &swagger,
  1400. },
  1401. },
  1402. Method: []*openapiconfig.OpenAPIMethodOption{
  1403. {
  1404. Method: "example.ExampleService.Example",
  1405. Option: &openapiOperation,
  1406. },
  1407. },
  1408. }
  1409. reg := descriptor.NewRegistry()
  1410. verifyTemplateExtensions(t, reg, file, opts)
  1411. })
  1412. }
  1413. func TestApplyTemplateHeaders(t *testing.T) {
  1414. newFile := func() *descriptor.File {
  1415. msgdesc := &descriptorpb.DescriptorProto{
  1416. Name: proto.String("ExampleMessage"),
  1417. }
  1418. meth := &descriptorpb.MethodDescriptorProto{
  1419. Name: proto.String("Example"),
  1420. InputType: proto.String("ExampleMessage"),
  1421. OutputType: proto.String("ExampleMessage"),
  1422. Options: &descriptorpb.MethodOptions{},
  1423. }
  1424. svc := &descriptorpb.ServiceDescriptorProto{
  1425. Name: proto.String("ExampleService"),
  1426. Method: []*descriptorpb.MethodDescriptorProto{meth},
  1427. }
  1428. msg := &descriptor.Message{
  1429. DescriptorProto: msgdesc,
  1430. }
  1431. return &descriptor.File{
  1432. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  1433. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  1434. Name: proto.String("example.proto"),
  1435. Package: proto.String("example"),
  1436. MessageType: []*descriptorpb.DescriptorProto{msgdesc},
  1437. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  1438. Options: &descriptorpb.FileOptions{
  1439. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  1440. },
  1441. },
  1442. GoPkg: descriptor.GoPackage{
  1443. Path: "example.com/path/to/example/example.pb",
  1444. Name: "example_pb",
  1445. },
  1446. Messages: []*descriptor.Message{msg},
  1447. Services: []*descriptor.Service{
  1448. {
  1449. ServiceDescriptorProto: svc,
  1450. Methods: []*descriptor.Method{
  1451. {
  1452. MethodDescriptorProto: meth,
  1453. RequestType: msg,
  1454. ResponseType: msg,
  1455. Bindings: []*descriptor.Binding{
  1456. {
  1457. HTTPMethod: "GET",
  1458. Body: &descriptor.Body{FieldPath: nil},
  1459. PathTmpl: httprule.Template{
  1460. Version: 1,
  1461. OpCodes: []int{0, 0},
  1462. Template: "/v1/echo", // TODO(achew22): Figure out what this should really be
  1463. },
  1464. },
  1465. },
  1466. },
  1467. },
  1468. },
  1469. },
  1470. }
  1471. }
  1472. openapiOperation := openapi_options.Operation{
  1473. Responses: map[string]*openapi_options.Response{
  1474. "200": &openapi_options.Response{
  1475. Description: "Testing Headers",
  1476. Headers: map[string]*openapi_options.Header{
  1477. "string": {
  1478. Description: "string header description",
  1479. Type: "string",
  1480. Format: "uuid",
  1481. Pattern: "",
  1482. },
  1483. "boolean": {
  1484. Description: "boolean header description",
  1485. Type: "boolean",
  1486. Default: "true",
  1487. Pattern: "^true|false$",
  1488. },
  1489. "integer": {
  1490. Description: "integer header description",
  1491. Type: "integer",
  1492. Default: "0",
  1493. Pattern: "^[0-9]$",
  1494. },
  1495. "number": {
  1496. Description: "number header description",
  1497. Type: "number",
  1498. Default: "1.2",
  1499. Pattern: "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$",
  1500. },
  1501. },
  1502. },
  1503. },
  1504. }
  1505. verifyTemplateHeaders := func(t *testing.T, reg *descriptor.Registry, file *descriptor.File,
  1506. opts *openapiconfig.OpenAPIOptions) {
  1507. if err := AddErrorDefs(reg); err != nil {
  1508. t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  1509. return
  1510. }
  1511. fileCL := crossLinkFixture(file)
  1512. err := reg.Load(reqFromFile(fileCL))
  1513. if err != nil {
  1514. t.Errorf("reg.Load(%#v) failed with %v; want success", file, err)
  1515. return
  1516. }
  1517. if opts != nil {
  1518. if err := reg.RegisterOpenAPIOptions(opts); err != nil {
  1519. t.Fatalf("failed to register OpenAPI annotations: %s", err)
  1520. }
  1521. }
  1522. result, err := applyTemplate(param{File: fileCL, reg: reg})
  1523. if err != nil {
  1524. t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  1525. return
  1526. }
  1527. if want, is, name := "2.0", result.Swagger, "Swagger"; !reflect.DeepEqual(is, want) {
  1528. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1529. }
  1530. var response openapiResponseObject
  1531. for _, v := range result.Paths {
  1532. response = v.Get.Responses["200"]
  1533. }
  1534. if want, is, name := []openapiHeadersObject{
  1535. {
  1536. "String": openapiHeaderObject{
  1537. Description: "string header description",
  1538. Type: "string",
  1539. Format: "uuid",
  1540. Pattern: "",
  1541. },
  1542. "Boolean": openapiHeaderObject{
  1543. Description: "boolean header description",
  1544. Type: "boolean",
  1545. Default: json.RawMessage("true"),
  1546. Pattern: "^true|false$",
  1547. },
  1548. "Integer": openapiHeaderObject{
  1549. Description: "integer header description",
  1550. Type: "integer",
  1551. Default: json.RawMessage("0"),
  1552. Pattern: "^[0-9]$",
  1553. },
  1554. "Number": openapiHeaderObject{
  1555. Description: "number header description",
  1556. Type: "number",
  1557. Default: json.RawMessage("1.2"),
  1558. Pattern: "^[-+]?[0-9]*\\.?[0-9]+([eE][-+]?[0-9]+)?$",
  1559. },
  1560. },
  1561. }[0], response.Headers, "response.Headers"; !reflect.DeepEqual(is, want) {
  1562. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, is, want)
  1563. }
  1564. }
  1565. t.Run("verify template options set via proto options", func(t *testing.T) {
  1566. file := newFile()
  1567. proto.SetExtension(proto.Message(file.Services[0].Methods[0].Options), openapi_options.E_Openapiv2Operation, &openapiOperation)
  1568. reg := descriptor.NewRegistry()
  1569. verifyTemplateHeaders(t, reg, file, nil)
  1570. })
  1571. }
  1572. func TestValidateHeaderType(t *testing.T) {
  1573. type test struct {
  1574. Type string
  1575. Format string
  1576. expectedError error
  1577. }
  1578. tests := []test{
  1579. {
  1580. "string",
  1581. "date-time",
  1582. nil,
  1583. },
  1584. {
  1585. "boolean",
  1586. "",
  1587. nil,
  1588. },
  1589. {
  1590. "integer",
  1591. "uint",
  1592. nil,
  1593. },
  1594. {
  1595. "integer",
  1596. "uint8",
  1597. nil,
  1598. },
  1599. {
  1600. "integer",
  1601. "uint16",
  1602. nil,
  1603. },
  1604. {
  1605. "integer",
  1606. "uint32",
  1607. nil,
  1608. },
  1609. {
  1610. "integer",
  1611. "uint64",
  1612. nil,
  1613. },
  1614. {
  1615. "integer",
  1616. "int",
  1617. nil,
  1618. },
  1619. {
  1620. "integer",
  1621. "int8",
  1622. nil,
  1623. },
  1624. {
  1625. "integer",
  1626. "int16",
  1627. nil,
  1628. },
  1629. {
  1630. "integer",
  1631. "int32",
  1632. nil,
  1633. },
  1634. {
  1635. "integer",
  1636. "int64",
  1637. nil,
  1638. },
  1639. {
  1640. "integer",
  1641. "float64",
  1642. errors.New("the provided format \"float64\" is not a valid extension of the type \"integer\""),
  1643. },
  1644. {
  1645. "integer",
  1646. "uuid",
  1647. errors.New("the provided format \"uuid\" is not a valid extension of the type \"integer\""),
  1648. },
  1649. {
  1650. "number",
  1651. "uint",
  1652. nil,
  1653. },
  1654. {
  1655. "number",
  1656. "uint8",
  1657. nil,
  1658. },
  1659. {
  1660. "number",
  1661. "uint16",
  1662. nil,
  1663. },
  1664. {
  1665. "number",
  1666. "uint32",
  1667. nil,
  1668. },
  1669. {
  1670. "number",
  1671. "uint64",
  1672. nil,
  1673. },
  1674. {
  1675. "number",
  1676. "int",
  1677. nil,
  1678. },
  1679. {
  1680. "number",
  1681. "int8",
  1682. nil,
  1683. },
  1684. {
  1685. "number",
  1686. "int16",
  1687. nil,
  1688. },
  1689. {
  1690. "number",
  1691. "int32",
  1692. nil,
  1693. },
  1694. {
  1695. "number",
  1696. "int64",
  1697. nil,
  1698. },
  1699. {
  1700. "number",
  1701. "float",
  1702. nil,
  1703. },
  1704. {
  1705. "number",
  1706. "float32",
  1707. nil,
  1708. },
  1709. {
  1710. "number",
  1711. "float64",
  1712. nil,
  1713. },
  1714. {
  1715. "number",
  1716. "complex64",
  1717. nil,
  1718. },
  1719. {
  1720. "number",
  1721. "complex128",
  1722. nil,
  1723. },
  1724. {
  1725. "number",
  1726. "double",
  1727. nil,
  1728. },
  1729. {
  1730. "number",
  1731. "byte",
  1732. nil,
  1733. },
  1734. {
  1735. "number",
  1736. "rune",
  1737. nil,
  1738. },
  1739. {
  1740. "number",
  1741. "uintptr",
  1742. nil,
  1743. },
  1744. {
  1745. "number",
  1746. "date",
  1747. errors.New("the provided format \"date\" is not a valid extension of the type \"number\""),
  1748. },
  1749. {
  1750. "array",
  1751. "",
  1752. errors.New("the provided header type \"array\" is not supported"),
  1753. },
  1754. {
  1755. "foo",
  1756. "",
  1757. errors.New("the provided header type \"foo\" is not supported"),
  1758. },
  1759. }
  1760. for _, v := range tests {
  1761. err := validateHeaderTypeAndFormat(v.Type, v.Format)
  1762. if v.expectedError == nil {
  1763. if err != nil {
  1764. t.Errorf("unexpected error %v", err)
  1765. }
  1766. } else {
  1767. if err == nil {
  1768. t.Fatal("expected header error not returned")
  1769. }
  1770. if err.Error() != v.expectedError.Error() {
  1771. t.Errorf("expected error malformed, expected %q, got %q", v.expectedError.Error(), err.Error())
  1772. }
  1773. }
  1774. }
  1775. }
  1776. func TestValidateDefaultValueType(t *testing.T) {
  1777. type test struct {
  1778. Type string
  1779. Value string
  1780. Format string
  1781. expectedError error
  1782. }
  1783. tests := []test{
  1784. {
  1785. "string",
  1786. `"string"`,
  1787. "",
  1788. nil,
  1789. },
  1790. {
  1791. "string",
  1792. "\"2012-11-01T22:08:41+00:00\"",
  1793. "date-time",
  1794. nil,
  1795. },
  1796. {
  1797. "string",
  1798. "\"2012-11-01\"",
  1799. "date",
  1800. nil,
  1801. },
  1802. {
  1803. "string",
  1804. "0",
  1805. "",
  1806. errors.New("the provided default value \"0\" does not match provider type \"string\", or is not properly quoted with escaped quotations"),
  1807. },
  1808. {
  1809. "string",
  1810. "false",
  1811. "",
  1812. errors.New("the provided default value \"false\" does not match provider type \"string\", or is not properly quoted with escaped quotations"),
  1813. },
  1814. {
  1815. "boolean",
  1816. "true",
  1817. "",
  1818. nil,
  1819. },
  1820. {
  1821. "boolean",
  1822. "0",
  1823. "",
  1824. errors.New("the provided default value \"0\" does not match provider type \"boolean\""),
  1825. },
  1826. {
  1827. "boolean",
  1828. `"string"`,
  1829. "",
  1830. errors.New("the provided default value \"\\\"string\\\"\" does not match provider type \"boolean\""),
  1831. },
  1832. {
  1833. "number",
  1834. "1.2",
  1835. "",
  1836. nil,
  1837. },
  1838. {
  1839. "number",
  1840. "123",
  1841. "",
  1842. nil,
  1843. },
  1844. {
  1845. "number",
  1846. "nan",
  1847. "",
  1848. errors.New("the provided number \"nan\" is not a valid JSON number"),
  1849. },
  1850. {
  1851. "number",
  1852. "NaN",
  1853. "",
  1854. errors.New("the provided number \"NaN\" is not a valid JSON number"),
  1855. },
  1856. {
  1857. "number",
  1858. "-459.67",
  1859. "",
  1860. nil,
  1861. },
  1862. {
  1863. "number",
  1864. "inf",
  1865. "",
  1866. errors.New("the provided number \"inf\" is not a valid JSON number"),
  1867. },
  1868. {
  1869. "number",
  1870. "infinity",
  1871. "",
  1872. errors.New("the provided number \"infinity\" is not a valid JSON number"),
  1873. },
  1874. {
  1875. "number",
  1876. "Inf",
  1877. "",
  1878. errors.New("the provided number \"Inf\" is not a valid JSON number"),
  1879. },
  1880. {
  1881. "number",
  1882. "Infinity",
  1883. "",
  1884. errors.New("the provided number \"Infinity\" is not a valid JSON number"),
  1885. },
  1886. {
  1887. "number",
  1888. "false",
  1889. "",
  1890. errors.New("the provided default value \"false\" does not match provider type \"number\""),
  1891. },
  1892. {
  1893. "number",
  1894. `"string"`,
  1895. "",
  1896. errors.New("the provided default value \"\\\"string\\\"\" does not match provider type \"number\""),
  1897. },
  1898. {
  1899. "integer",
  1900. "2",
  1901. "",
  1902. nil,
  1903. },
  1904. {
  1905. "integer",
  1906. fmt.Sprint(math.MaxInt32),
  1907. "int32",
  1908. nil,
  1909. },
  1910. {
  1911. "integer",
  1912. fmt.Sprint(math.MaxInt32 + 1),
  1913. "int32",
  1914. errors.New("the provided default value \"2147483648\" does not match provided format \"int32\""),
  1915. },
  1916. {
  1917. "integer",
  1918. fmt.Sprint(math.MaxInt64),
  1919. "int64",
  1920. nil,
  1921. },
  1922. {
  1923. "integer",
  1924. "9223372036854775808",
  1925. "int64",
  1926. errors.New("the provided default value \"9223372036854775808\" does not match provided format \"int64\""),
  1927. },
  1928. {
  1929. "integer",
  1930. "18446744073709551615",
  1931. "uint64",
  1932. nil,
  1933. },
  1934. {
  1935. "integer",
  1936. "false",
  1937. "",
  1938. errors.New("the provided default value \"false\" does not match provided type \"integer\""),
  1939. },
  1940. {
  1941. "integer",
  1942. "1.2",
  1943. "",
  1944. errors.New("the provided default value \"1.2\" does not match provided type \"integer\""),
  1945. },
  1946. {
  1947. "integer",
  1948. `"string"`,
  1949. "",
  1950. errors.New("the provided default value \"\\\"string\\\"\" does not match provided type \"integer\""),
  1951. },
  1952. }
  1953. for _, v := range tests {
  1954. err := validateDefaultValueTypeAndFormat(v.Type, v.Value, v.Format)
  1955. if v.expectedError == nil {
  1956. if err != nil {
  1957. t.Errorf("unexpected error '%v'", err)
  1958. }
  1959. } else {
  1960. if err == nil {
  1961. t.Error("expected update error not returned")
  1962. }
  1963. if err.Error() != v.expectedError.Error() {
  1964. t.Errorf("expected error malformed, expected %q, got %q", v.expectedError.Error(), err.Error())
  1965. }
  1966. }
  1967. }
  1968. }
  1969. func TestApplyTemplateRequestWithoutClientStreaming(t *testing.T) {
  1970. msgdesc := &descriptorpb.DescriptorProto{
  1971. Name: proto.String("ExampleMessage"),
  1972. Field: []*descriptorpb.FieldDescriptorProto{
  1973. {
  1974. Name: proto.String("nested"),
  1975. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  1976. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  1977. TypeName: proto.String("NestedMessage"),
  1978. Number: proto.Int32(1),
  1979. },
  1980. },
  1981. }
  1982. nesteddesc := &descriptorpb.DescriptorProto{
  1983. Name: proto.String("NestedMessage"),
  1984. Field: []*descriptorpb.FieldDescriptorProto{
  1985. {
  1986. Name: proto.String("int32"),
  1987. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  1988. Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  1989. Number: proto.Int32(1),
  1990. },
  1991. {
  1992. Name: proto.String("bool"),
  1993. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  1994. Type: descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
  1995. Number: proto.Int32(2),
  1996. },
  1997. },
  1998. }
  1999. meth := &descriptorpb.MethodDescriptorProto{
  2000. Name: proto.String("Echo"),
  2001. InputType: proto.String("ExampleMessage"),
  2002. OutputType: proto.String("ExampleMessage"),
  2003. ClientStreaming: proto.Bool(false),
  2004. }
  2005. svc := &descriptorpb.ServiceDescriptorProto{
  2006. Name: proto.String("ExampleService"),
  2007. Method: []*descriptorpb.MethodDescriptorProto{meth},
  2008. }
  2009. meth.ServerStreaming = proto.Bool(false)
  2010. msg := &descriptor.Message{
  2011. DescriptorProto: msgdesc,
  2012. }
  2013. nested := &descriptor.Message{
  2014. DescriptorProto: nesteddesc,
  2015. }
  2016. nestedField := &descriptor.Field{
  2017. Message: msg,
  2018. FieldDescriptorProto: msg.GetField()[0],
  2019. }
  2020. intField := &descriptor.Field{
  2021. Message: nested,
  2022. FieldDescriptorProto: nested.GetField()[0],
  2023. }
  2024. boolField := &descriptor.Field{
  2025. Message: nested,
  2026. FieldDescriptorProto: nested.GetField()[1],
  2027. }
  2028. file := descriptor.File{
  2029. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  2030. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  2031. Name: proto.String("example.proto"),
  2032. Package: proto.String("example"),
  2033. MessageType: []*descriptorpb.DescriptorProto{msgdesc, nesteddesc},
  2034. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  2035. Options: &descriptorpb.FileOptions{
  2036. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  2037. },
  2038. },
  2039. GoPkg: descriptor.GoPackage{
  2040. Path: "example.com/path/to/example/example.pb",
  2041. Name: "example_pb",
  2042. },
  2043. Messages: []*descriptor.Message{msg, nested},
  2044. Services: []*descriptor.Service{
  2045. {
  2046. ServiceDescriptorProto: svc,
  2047. Methods: []*descriptor.Method{
  2048. {
  2049. MethodDescriptorProto: meth,
  2050. RequestType: msg,
  2051. ResponseType: msg,
  2052. Bindings: []*descriptor.Binding{
  2053. {
  2054. HTTPMethod: "POST",
  2055. PathTmpl: httprule.Template{
  2056. Version: 1,
  2057. OpCodes: []int{0, 0},
  2058. Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be
  2059. },
  2060. PathParams: []descriptor.Parameter{
  2061. {
  2062. FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2063. {
  2064. Name: "nested",
  2065. Target: nestedField,
  2066. },
  2067. {
  2068. Name: "int32",
  2069. Target: intField,
  2070. },
  2071. }),
  2072. Target: intField,
  2073. },
  2074. },
  2075. Body: &descriptor.Body{
  2076. FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2077. {
  2078. Name: "nested",
  2079. Target: nestedField,
  2080. },
  2081. {
  2082. Name: "bool",
  2083. Target: boolField,
  2084. },
  2085. }),
  2086. },
  2087. },
  2088. },
  2089. },
  2090. },
  2091. },
  2092. },
  2093. }
  2094. reg := descriptor.NewRegistry()
  2095. if err := AddErrorDefs(reg); err != nil {
  2096. t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  2097. return
  2098. }
  2099. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  2100. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  2101. })
  2102. if err != nil {
  2103. t.Fatalf("failed to load code generator request: %v", err)
  2104. }
  2105. result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  2106. if err != nil {
  2107. t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2108. return
  2109. }
  2110. if want, got := "2.0", result.Swagger; !reflect.DeepEqual(got, want) {
  2111. t.Errorf("applyTemplate(%#v).Swagger = %s want to be %s", file, got, want)
  2112. }
  2113. if want, got := "", result.BasePath; !reflect.DeepEqual(got, want) {
  2114. t.Errorf("applyTemplate(%#v).BasePath = %s want to be %s", file, got, want)
  2115. }
  2116. if want, got := ([]string)(nil), result.Schemes; !reflect.DeepEqual(got, want) {
  2117. t.Errorf("applyTemplate(%#v).Schemes = %s want to be %s", file, got, want)
  2118. }
  2119. if want, got := []string{"application/json"}, result.Consumes; !reflect.DeepEqual(got, want) {
  2120. t.Errorf("applyTemplate(%#v).Consumes = %s want to be %s", file, got, want)
  2121. }
  2122. if want, got := []string{"application/json"}, result.Produces; !reflect.DeepEqual(got, want) {
  2123. t.Errorf("applyTemplate(%#v).Produces = %s want to be %s", file, got, want)
  2124. }
  2125. // If there was a failure, print out the input and the json result for debugging.
  2126. if t.Failed() {
  2127. t.Errorf("had: %s", file)
  2128. t.Errorf("got: %s", fmt.Sprint(result))
  2129. }
  2130. }
  2131. func TestApplyTemplateRequestWithClientStreaming(t *testing.T) {
  2132. msgdesc := &descriptorpb.DescriptorProto{
  2133. Name: proto.String("ExampleMessage"),
  2134. Field: []*descriptorpb.FieldDescriptorProto{
  2135. {
  2136. Name: proto.String("nested"),
  2137. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2138. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2139. TypeName: proto.String("NestedMessage"),
  2140. Number: proto.Int32(1),
  2141. },
  2142. },
  2143. }
  2144. nesteddesc := &descriptorpb.DescriptorProto{
  2145. Name: proto.String("NestedMessage"),
  2146. Field: []*descriptorpb.FieldDescriptorProto{
  2147. {
  2148. Name: proto.String("int32"),
  2149. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2150. Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  2151. Number: proto.Int32(1),
  2152. },
  2153. {
  2154. Name: proto.String("bool"),
  2155. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2156. Type: descriptorpb.FieldDescriptorProto_TYPE_BOOL.Enum(),
  2157. Number: proto.Int32(2),
  2158. },
  2159. },
  2160. }
  2161. meth := &descriptorpb.MethodDescriptorProto{
  2162. Name: proto.String("Echo"),
  2163. InputType: proto.String("ExampleMessage"),
  2164. OutputType: proto.String("ExampleMessage"),
  2165. ClientStreaming: proto.Bool(true),
  2166. ServerStreaming: proto.Bool(true),
  2167. }
  2168. svc := &descriptorpb.ServiceDescriptorProto{
  2169. Name: proto.String("ExampleService"),
  2170. Method: []*descriptorpb.MethodDescriptorProto{meth},
  2171. }
  2172. msg := &descriptor.Message{
  2173. DescriptorProto: msgdesc,
  2174. }
  2175. nested := &descriptor.Message{
  2176. DescriptorProto: nesteddesc,
  2177. }
  2178. nestedField := &descriptor.Field{
  2179. Message: msg,
  2180. FieldDescriptorProto: msg.GetField()[0],
  2181. }
  2182. intField := &descriptor.Field{
  2183. Message: nested,
  2184. FieldDescriptorProto: nested.GetField()[0],
  2185. }
  2186. boolField := &descriptor.Field{
  2187. Message: nested,
  2188. FieldDescriptorProto: nested.GetField()[1],
  2189. }
  2190. file := descriptor.File{
  2191. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  2192. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  2193. Name: proto.String("example.proto"),
  2194. Package: proto.String("example"),
  2195. MessageType: []*descriptorpb.DescriptorProto{msgdesc, nesteddesc},
  2196. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  2197. Options: &descriptorpb.FileOptions{
  2198. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  2199. },
  2200. },
  2201. GoPkg: descriptor.GoPackage{
  2202. Path: "example.com/path/to/example/example.pb",
  2203. Name: "example_pb",
  2204. },
  2205. Messages: []*descriptor.Message{msg, nested},
  2206. Services: []*descriptor.Service{
  2207. {
  2208. ServiceDescriptorProto: svc,
  2209. Methods: []*descriptor.Method{
  2210. {
  2211. MethodDescriptorProto: meth,
  2212. RequestType: msg,
  2213. ResponseType: msg,
  2214. Bindings: []*descriptor.Binding{
  2215. {
  2216. HTTPMethod: "POST",
  2217. PathTmpl: httprule.Template{
  2218. Version: 1,
  2219. OpCodes: []int{0, 0},
  2220. Template: "/v1/echo", // TODO(achew): Figure out what this hsould really be
  2221. },
  2222. PathParams: []descriptor.Parameter{
  2223. {
  2224. FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2225. {
  2226. Name: "nested",
  2227. Target: nestedField,
  2228. },
  2229. {
  2230. Name: "int32",
  2231. Target: intField,
  2232. },
  2233. }),
  2234. Target: intField,
  2235. },
  2236. },
  2237. Body: &descriptor.Body{
  2238. FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2239. {
  2240. Name: "nested",
  2241. Target: nestedField,
  2242. },
  2243. {
  2244. Name: "bool",
  2245. Target: boolField,
  2246. },
  2247. }),
  2248. },
  2249. },
  2250. },
  2251. },
  2252. },
  2253. },
  2254. },
  2255. }
  2256. reg := descriptor.NewRegistry()
  2257. if err := AddErrorDefs(reg); err != nil {
  2258. t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  2259. return
  2260. }
  2261. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  2262. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  2263. })
  2264. if err != nil {
  2265. t.Fatalf("failed to load code generator request: %v", err)
  2266. }
  2267. result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  2268. if err != nil {
  2269. t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2270. return
  2271. }
  2272. // Only ExampleMessage must be present, not NestedMessage
  2273. if want, got, name := 3, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
  2274. t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  2275. }
  2276. if _, ok := result.Paths["/v1/echo"].Post.Responses["200"]; !ok {
  2277. t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.Paths["/v1/echo"].Post.Responses["200"]`)
  2278. } else {
  2279. if want, got, name := "A successful response.(streaming responses)", result.Paths["/v1/echo"].Post.Responses["200"].Description, `result.Paths["/v1/echo"].Post.Responses["200"].Description`; !reflect.DeepEqual(got, want) {
  2280. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2281. }
  2282. streamExampleExampleMessage := result.Paths["/v1/echo"].Post.Responses["200"].Schema
  2283. if want, got, name := "object", streamExampleExampleMessage.Type, `result.Paths["/v1/echo"].Post.Responses["200"].Schema.Type`; !reflect.DeepEqual(got, want) {
  2284. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2285. }
  2286. if want, got, name := "Stream result of exampleExampleMessage", streamExampleExampleMessage.Title, `result.Paths["/v1/echo"].Post.Responses["200"].Schema.Title`; !reflect.DeepEqual(got, want) {
  2287. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2288. }
  2289. streamExampleExampleMessageProperties := *(streamExampleExampleMessage.Properties)
  2290. if want, got, name := 2, len(streamExampleExampleMessageProperties), `len(StreamDefinitions["exampleExampleMessage"].Properties)`; !reflect.DeepEqual(got, want) {
  2291. t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  2292. } else {
  2293. resultProperty := streamExampleExampleMessageProperties[0]
  2294. if want, got, name := "result", resultProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
  2295. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2296. }
  2297. result := resultProperty.Value.(openapiSchemaObject)
  2298. if want, got, name := "#/definitions/exampleExampleMessage", result.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(openapiSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
  2299. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2300. }
  2301. errorProperty := streamExampleExampleMessageProperties[1]
  2302. if want, got, name := "error", errorProperty.Key, `(*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Key`; !reflect.DeepEqual(got, want) {
  2303. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2304. }
  2305. err := errorProperty.Value.(openapiSchemaObject)
  2306. if want, got, name := "#/definitions/rpcStatus", err.Ref, `((*(StreamDefinitions["exampleExampleMessage"].Properties))[0].Value.(openapiSchemaObject)).Ref`; !reflect.DeepEqual(got, want) {
  2307. t.Errorf("applyTemplate(%#v).%s = %s want to be %s", file, name, got, want)
  2308. }
  2309. }
  2310. }
  2311. // If there was a failure, print out the input and the json result for debugging.
  2312. if t.Failed() {
  2313. t.Errorf("had: %s", file)
  2314. t.Errorf("got: %s", fmt.Sprint(result))
  2315. }
  2316. }
  2317. func TestApplyTemplateRequestWithUnusedReferences(t *testing.T) {
  2318. reqdesc := &descriptorpb.DescriptorProto{
  2319. Name: proto.String("ExampleMessage"),
  2320. Field: []*descriptorpb.FieldDescriptorProto{
  2321. {
  2322. Name: proto.String("string"),
  2323. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  2324. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  2325. Number: proto.Int32(1),
  2326. },
  2327. },
  2328. }
  2329. respdesc := &descriptorpb.DescriptorProto{
  2330. Name: proto.String("EmptyMessage"),
  2331. }
  2332. meth := &descriptorpb.MethodDescriptorProto{
  2333. Name: proto.String("Example"),
  2334. InputType: proto.String("ExampleMessage"),
  2335. OutputType: proto.String("EmptyMessage"),
  2336. ClientStreaming: proto.Bool(false),
  2337. ServerStreaming: proto.Bool(false),
  2338. }
  2339. svc := &descriptorpb.ServiceDescriptorProto{
  2340. Name: proto.String("ExampleService"),
  2341. Method: []*descriptorpb.MethodDescriptorProto{meth},
  2342. }
  2343. req := &descriptor.Message{
  2344. DescriptorProto: reqdesc,
  2345. }
  2346. resp := &descriptor.Message{
  2347. DescriptorProto: respdesc,
  2348. }
  2349. stringField := &descriptor.Field{
  2350. Message: req,
  2351. FieldDescriptorProto: req.GetField()[0],
  2352. }
  2353. file := descriptor.File{
  2354. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  2355. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  2356. Name: proto.String("example.proto"),
  2357. Package: proto.String("example"),
  2358. MessageType: []*descriptorpb.DescriptorProto{reqdesc, respdesc},
  2359. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  2360. Options: &descriptorpb.FileOptions{
  2361. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  2362. },
  2363. },
  2364. GoPkg: descriptor.GoPackage{
  2365. Path: "example.com/path/to/example/example.pb",
  2366. Name: "example_pb",
  2367. },
  2368. Messages: []*descriptor.Message{req, resp},
  2369. Services: []*descriptor.Service{
  2370. {
  2371. ServiceDescriptorProto: svc,
  2372. Methods: []*descriptor.Method{
  2373. {
  2374. MethodDescriptorProto: meth,
  2375. RequestType: req,
  2376. ResponseType: resp,
  2377. Bindings: []*descriptor.Binding{
  2378. {
  2379. HTTPMethod: "GET",
  2380. PathTmpl: httprule.Template{
  2381. Version: 1,
  2382. OpCodes: []int{0, 0},
  2383. Template: "/v1/example",
  2384. },
  2385. },
  2386. {
  2387. HTTPMethod: "POST",
  2388. PathTmpl: httprule.Template{
  2389. Version: 1,
  2390. OpCodes: []int{0, 0},
  2391. Template: "/v1/example/{string}",
  2392. },
  2393. PathParams: []descriptor.Parameter{
  2394. {
  2395. FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2396. {
  2397. Name: "string",
  2398. Target: stringField,
  2399. },
  2400. }),
  2401. Target: stringField,
  2402. },
  2403. },
  2404. Body: &descriptor.Body{
  2405. FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2406. {
  2407. Name: "string",
  2408. Target: stringField,
  2409. },
  2410. }),
  2411. },
  2412. },
  2413. },
  2414. },
  2415. },
  2416. },
  2417. },
  2418. }
  2419. reg := descriptor.NewRegistry()
  2420. if err := AddErrorDefs(reg); err != nil {
  2421. t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  2422. return
  2423. }
  2424. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  2425. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  2426. })
  2427. if err != nil {
  2428. t.Fatalf("failed to load code generator request: %v", err)
  2429. }
  2430. result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  2431. if err != nil {
  2432. t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2433. return
  2434. }
  2435. // Only EmptyMessage must be present, not ExampleMessage (plus error status)
  2436. if want, got, name := 3, len(result.Definitions), "len(Definitions)"; !reflect.DeepEqual(got, want) {
  2437. t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  2438. }
  2439. // If there was a failure, print out the input and the json result for debugging.
  2440. if t.Failed() {
  2441. t.Errorf("had: %s", file)
  2442. t.Errorf("got: %s", fmt.Sprint(result))
  2443. }
  2444. }
  2445. func TestApplyTemplateRequestWithBodyQueryParameters(t *testing.T) {
  2446. bookDesc := &descriptorpb.DescriptorProto{
  2447. Name: proto.String("Book"),
  2448. Field: []*descriptorpb.FieldDescriptorProto{
  2449. {
  2450. Name: proto.String("name"),
  2451. Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2452. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  2453. Number: proto.Int32(1),
  2454. },
  2455. {
  2456. Name: proto.String("id"),
  2457. Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2458. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  2459. Number: proto.Int32(2),
  2460. },
  2461. },
  2462. }
  2463. createDesc := &descriptorpb.DescriptorProto{
  2464. Name: proto.String("CreateBookRequest"),
  2465. Field: []*descriptorpb.FieldDescriptorProto{
  2466. {
  2467. Name: proto.String("parent"),
  2468. Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2469. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  2470. Number: proto.Int32(1),
  2471. },
  2472. {
  2473. Name: proto.String("book"),
  2474. Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2475. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  2476. Number: proto.Int32(2),
  2477. },
  2478. {
  2479. Name: proto.String("book_id"),
  2480. Label: descriptorpb.FieldDescriptorProto_LABEL_REQUIRED.Enum(),
  2481. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  2482. Number: proto.Int32(3),
  2483. },
  2484. },
  2485. }
  2486. meth := &descriptorpb.MethodDescriptorProto{
  2487. Name: proto.String("CreateBook"),
  2488. InputType: proto.String("CreateBookRequest"),
  2489. OutputType: proto.String("Book"),
  2490. }
  2491. svc := &descriptorpb.ServiceDescriptorProto{
  2492. Name: proto.String("BookService"),
  2493. Method: []*descriptorpb.MethodDescriptorProto{meth},
  2494. }
  2495. bookMsg := &descriptor.Message{
  2496. DescriptorProto: bookDesc,
  2497. }
  2498. createMsg := &descriptor.Message{
  2499. DescriptorProto: createDesc,
  2500. }
  2501. parentField := &descriptor.Field{
  2502. Message: createMsg,
  2503. FieldDescriptorProto: createMsg.GetField()[0],
  2504. }
  2505. bookField := &descriptor.Field{
  2506. Message: createMsg,
  2507. FieldMessage: bookMsg,
  2508. FieldDescriptorProto: createMsg.GetField()[1],
  2509. }
  2510. bookIDField := &descriptor.Field{
  2511. Message: createMsg,
  2512. FieldDescriptorProto: createMsg.GetField()[2],
  2513. }
  2514. createMsg.Fields = []*descriptor.Field{parentField, bookField, bookIDField}
  2515. file := descriptor.File{
  2516. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  2517. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  2518. Name: proto.String("book.proto"),
  2519. MessageType: []*descriptorpb.DescriptorProto{bookDesc, createDesc},
  2520. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  2521. Options: &descriptorpb.FileOptions{
  2522. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  2523. },
  2524. },
  2525. GoPkg: descriptor.GoPackage{
  2526. Path: "example.com/path/to/book.pb",
  2527. Name: "book_pb",
  2528. },
  2529. Messages: []*descriptor.Message{bookMsg, createMsg},
  2530. Services: []*descriptor.Service{
  2531. {
  2532. ServiceDescriptorProto: svc,
  2533. Methods: []*descriptor.Method{
  2534. {
  2535. MethodDescriptorProto: meth,
  2536. RequestType: createMsg,
  2537. ResponseType: bookMsg,
  2538. Bindings: []*descriptor.Binding{
  2539. {
  2540. HTTPMethod: "POST",
  2541. PathTmpl: httprule.Template{
  2542. Version: 1,
  2543. OpCodes: []int{0, 0},
  2544. Template: "/v1/{parent=publishers/*}/books",
  2545. },
  2546. PathParams: []descriptor.Parameter{
  2547. {
  2548. FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2549. {
  2550. Name: "parent",
  2551. Target: parentField,
  2552. },
  2553. }),
  2554. Target: parentField,
  2555. },
  2556. },
  2557. Body: &descriptor.Body{
  2558. FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{
  2559. {
  2560. Name: "book",
  2561. Target: bookField,
  2562. },
  2563. }),
  2564. },
  2565. },
  2566. },
  2567. },
  2568. },
  2569. },
  2570. },
  2571. }
  2572. reg := descriptor.NewRegistry()
  2573. if err := AddErrorDefs(reg); err != nil {
  2574. t.Errorf("AddErrorDefs(%#v) failed with %v; want success", reg, err)
  2575. return
  2576. }
  2577. err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  2578. if err != nil {
  2579. t.Errorf("Registry.Load() failed with %v; want success", err)
  2580. return
  2581. }
  2582. result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  2583. if err != nil {
  2584. t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  2585. return
  2586. }
  2587. if _, ok := result.Paths["/v1/{parent=publishers/*}/books"].Post.Responses["200"]; !ok {
  2588. t.Errorf("applyTemplate(%#v).%s = expected 200 response to be defined", file, `result.Paths["/v1/{parent=publishers/*}/books"].Post.Responses["200"]`)
  2589. } else {
  2590. if want, got, name := 3, len(result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters), `len(result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters)`; !reflect.DeepEqual(got, want) {
  2591. t.Errorf("applyTemplate(%#v).%s = %d want to be %d", file, name, got, want)
  2592. }
  2593. type param struct {
  2594. Name string
  2595. In string
  2596. Required bool
  2597. }
  2598. p0 := result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[0]
  2599. if want, got, name := (param{"parent", "path", true}), (param{p0.Name, p0.In, p0.Required}), `result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[0]`; !reflect.DeepEqual(got, want) {
  2600. t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, got, want)
  2601. }
  2602. p1 := result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[1]
  2603. if want, got, name := (param{"body", "body", true}), (param{p1.Name, p1.In, p1.Required}), `result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[1]`; !reflect.DeepEqual(got, want) {
  2604. t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, got, want)
  2605. }
  2606. p2 := result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[2]
  2607. if want, got, name := (param{"book_id", "query", false}), (param{p2.Name, p2.In, p2.Required}), `result.Paths["/v1/{parent=publishers/*}/books"].Post.Parameters[1]`; !reflect.DeepEqual(got, want) {
  2608. t.Errorf("applyTemplate(%#v).%s = %v want to be %v", file, name, got, want)
  2609. }
  2610. }
  2611. // If there was a failure, print out the input and the json result for debugging.
  2612. if t.Failed() {
  2613. t.Errorf("had: %s", file)
  2614. t.Errorf("got: %s", fmt.Sprint(result))
  2615. }
  2616. }
  2617. func generateFieldsForJSONReservedName() []*descriptor.Field {
  2618. fields := make([]*descriptor.Field, 0)
  2619. fieldName := string("json_name")
  2620. fieldJSONName := string("jsonNAME")
  2621. fieldDescriptor := descriptorpb.FieldDescriptorProto{Name: &fieldName, JsonName: &fieldJSONName}
  2622. field := &descriptor.Field{FieldDescriptorProto: &fieldDescriptor}
  2623. return append(fields, field)
  2624. }
  2625. func generateMsgsForJSONReservedName() []*descriptor.Message {
  2626. result := make([]*descriptor.Message, 0)
  2627. // The first message, its field is field_abc and its type is NewType
  2628. // NewType field_abc
  2629. fieldName := "field_abc"
  2630. fieldJSONName := "fieldAbc"
  2631. messageName1 := "message1"
  2632. messageType := "pkg.a.NewType"
  2633. pfd := descriptorpb.FieldDescriptorProto{Name: &fieldName, JsonName: &fieldJSONName, TypeName: &messageType}
  2634. result = append(result,
  2635. &descriptor.Message{
  2636. DescriptorProto: &descriptorpb.DescriptorProto{
  2637. Name: &messageName1, Field: []*descriptorpb.FieldDescriptorProto{&pfd},
  2638. },
  2639. })
  2640. // The second message, its name is NewName, its type is string
  2641. // message NewType {
  2642. // string field_newName [json_name = RESERVEDJSONNAME]
  2643. // }
  2644. messageName := "NewType"
  2645. field := "field_newName"
  2646. fieldJSONName2 := "RESERVEDJSONNAME"
  2647. pfd2 := descriptorpb.FieldDescriptorProto{Name: &field, JsonName: &fieldJSONName2}
  2648. result = append(result, &descriptor.Message{
  2649. DescriptorProto: &descriptorpb.DescriptorProto{
  2650. Name: &messageName, Field: []*descriptorpb.FieldDescriptorProto{&pfd2},
  2651. },
  2652. })
  2653. return result
  2654. }
  2655. func TestTemplateWithJsonCamelCase(t *testing.T) {
  2656. var tests = []struct {
  2657. input string
  2658. expected string
  2659. }{
  2660. {"/test/{test_id}", "/test/{testId}"},
  2661. {"/test1/{test1_id}/test2/{test2_id}", "/test1/{test1Id}/test2/{test2Id}"},
  2662. {"/test1/{test1_id}/{test2_id}", "/test1/{test1Id}/{test2Id}"},
  2663. {"/test1/test2/{test1_id}/{test2_id}", "/test1/test2/{test1Id}/{test2Id}"},
  2664. {"/test1/{test1_id1_id2}", "/test1/{test1Id1Id2}"},
  2665. {"/test1/{test1_id1_id2}/test2/{test2_id3_id4}", "/test1/{test1Id1Id2}/test2/{test2Id3Id4}"},
  2666. {"/test1/test2/{test1_id1_id2}/{test2_id3_id4}", "/test1/test2/{test1Id1Id2}/{test2Id3Id4}"},
  2667. {"test/{a}", "test/{a}"},
  2668. {"test/{ab}", "test/{ab}"},
  2669. {"test/{a_a}", "test/{aA}"},
  2670. {"test/{ab_c}", "test/{abC}"},
  2671. {"test/{json_name}", "test/{jsonNAME}"},
  2672. {"test/{field_abc.field_newName}", "test/{fieldAbc.RESERVEDJSONNAME}"},
  2673. }
  2674. reg := descriptor.NewRegistry()
  2675. reg.SetUseJSONNamesForFields(true)
  2676. for _, data := range tests {
  2677. actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2678. if data.expected != actual {
  2679. t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2680. }
  2681. }
  2682. }
  2683. func TestTemplateWithoutJsonCamelCase(t *testing.T) {
  2684. var tests = []struct {
  2685. input string
  2686. expected string
  2687. }{
  2688. {"/test/{test_id}", "/test/{test_id}"},
  2689. {"/test1/{test1_id}/test2/{test2_id}", "/test1/{test1_id}/test2/{test2_id}"},
  2690. {"/test1/{test1_id}/{test2_id}", "/test1/{test1_id}/{test2_id}"},
  2691. {"/test1/test2/{test1_id}/{test2_id}", "/test1/test2/{test1_id}/{test2_id}"},
  2692. {"/test1/{test1_id1_id2}", "/test1/{test1_id1_id2}"},
  2693. {"/test1/{test1_id1_id2}/test2/{test2_id3_id4}", "/test1/{test1_id1_id2}/test2/{test2_id3_id4}"},
  2694. {"/test1/test2/{test1_id1_id2}/{test2_id3_id4}", "/test1/test2/{test1_id1_id2}/{test2_id3_id4}"},
  2695. {"test/{a}", "test/{a}"},
  2696. {"test/{ab}", "test/{ab}"},
  2697. {"test/{a_a}", "test/{a_a}"},
  2698. {"test/{json_name}", "test/{json_name}"},
  2699. {"test/{field_abc.field_newName}", "test/{field_abc.field_newName}"},
  2700. }
  2701. reg := descriptor.NewRegistry()
  2702. reg.SetUseJSONNamesForFields(false)
  2703. for _, data := range tests {
  2704. actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2705. if data.expected != actual {
  2706. t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2707. }
  2708. }
  2709. }
  2710. func TestTemplateToOpenAPIPath(t *testing.T) {
  2711. var tests = []struct {
  2712. input string
  2713. expected string
  2714. }{
  2715. {"/test", "/test"},
  2716. {"/{test}", "/{test}"},
  2717. {"/{test=prefix/*}", "/{test}"},
  2718. {"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
  2719. {"/{test1}/{test2}", "/{test1}/{test2}"},
  2720. {"/{test1}/{test2}/", "/{test1}/{test2}/"},
  2721. {"/{name=prefix/*}", "/{name=prefix/*}"},
  2722. {"/{name=prefix1/*/prefix2/*}", "/{name=prefix1/*/prefix2/*}"},
  2723. {"/{user.name=prefix/*}", "/{user.name=prefix/*}"},
  2724. {"/{user.name=prefix1/*/prefix2/*}", "/{user.name=prefix1/*/prefix2/*}"},
  2725. {"/{parent=prefix/*}/children", "/{parent=prefix/*}/children"},
  2726. {"/{name=prefix/*}:customMethod", "/{name=prefix/*}:customMethod"},
  2727. {"/{name=prefix1/*/prefix2/*}:customMethod", "/{name=prefix1/*/prefix2/*}:customMethod"},
  2728. {"/{user.name=prefix/*}:customMethod", "/{user.name=prefix/*}:customMethod"},
  2729. {"/{user.name=prefix1/*/prefix2/*}:customMethod", "/{user.name=prefix1/*/prefix2/*}:customMethod"},
  2730. {"/{parent=prefix/*}/children:customMethod", "/{parent=prefix/*}/children:customMethod"},
  2731. }
  2732. reg := descriptor.NewRegistry()
  2733. reg.SetUseJSONNamesForFields(false)
  2734. for _, data := range tests {
  2735. actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2736. if data.expected != actual {
  2737. t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2738. }
  2739. }
  2740. reg.SetUseJSONNamesForFields(true)
  2741. for _, data := range tests {
  2742. actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2743. if data.expected != actual {
  2744. t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2745. }
  2746. }
  2747. }
  2748. func BenchmarkTemplateToOpenAPIPath(b *testing.B) {
  2749. const input = "/{user.name=prefix1/*/prefix2/*}:customMethod"
  2750. b.Run("with JSON names", func(b *testing.B) {
  2751. reg := descriptor.NewRegistry()
  2752. reg.SetUseJSONNamesForFields(false)
  2753. for i := 0; i < b.N; i++ {
  2754. _ = templateToOpenAPIPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2755. }
  2756. })
  2757. b.Run("without JSON names", func(b *testing.B) {
  2758. reg := descriptor.NewRegistry()
  2759. reg.SetUseJSONNamesForFields(true)
  2760. for i := 0; i < b.N; i++ {
  2761. _ = templateToOpenAPIPath(input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2762. }
  2763. })
  2764. }
  2765. func TestResolveFullyQualifiedNameToOpenAPIName(t *testing.T) {
  2766. var tests = []struct {
  2767. input string
  2768. output string
  2769. listOfFQMNs []string
  2770. useFQNForOpenAPIName bool
  2771. }{
  2772. {
  2773. ".a.b.C",
  2774. "C",
  2775. []string{
  2776. ".a.b.C",
  2777. },
  2778. false,
  2779. },
  2780. {
  2781. ".a.b.C",
  2782. "abC",
  2783. []string{
  2784. ".a.C",
  2785. ".a.b.C",
  2786. },
  2787. false,
  2788. },
  2789. {
  2790. ".a.b.C",
  2791. "abC",
  2792. []string{
  2793. ".C",
  2794. ".a.C",
  2795. ".a.b.C",
  2796. },
  2797. false,
  2798. },
  2799. {
  2800. ".a.b.C",
  2801. "a.b.C",
  2802. []string{
  2803. ".C",
  2804. ".a.C",
  2805. ".a.b.C",
  2806. },
  2807. true,
  2808. },
  2809. }
  2810. for _, data := range tests {
  2811. names := resolveFullyQualifiedNameToOpenAPINames(data.listOfFQMNs, data.useFQNForOpenAPIName)
  2812. output := names[data.input]
  2813. if output != data.output {
  2814. t.Errorf("Expected fullyQualifiedNameToOpenAPIName(%v) to be %s but got %s",
  2815. data.input, data.output, output)
  2816. }
  2817. }
  2818. }
  2819. func TestFQMNtoOpenAPIName(t *testing.T) {
  2820. var tests = []struct {
  2821. input string
  2822. expected string
  2823. }{
  2824. {"/test", "/test"},
  2825. {"/{test}", "/{test}"},
  2826. {"/{test=prefix/*}", "/{test}"},
  2827. {"/{test=prefix/that/has/multiple/parts/to/it/*}", "/{test}"},
  2828. {"/{test1}/{test2}", "/{test1}/{test2}"},
  2829. {"/{test1}/{test2}/", "/{test1}/{test2}/"},
  2830. }
  2831. reg := descriptor.NewRegistry()
  2832. reg.SetUseJSONNamesForFields(false)
  2833. for _, data := range tests {
  2834. actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2835. if data.expected != actual {
  2836. t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2837. }
  2838. }
  2839. reg.SetUseJSONNamesForFields(true)
  2840. for _, data := range tests {
  2841. actual := templateToOpenAPIPath(data.input, reg, generateFieldsForJSONReservedName(), generateMsgsForJSONReservedName())
  2842. if data.expected != actual {
  2843. t.Errorf("Expected templateToOpenAPIPath(%v) = %v, actual: %v", data.input, data.expected, actual)
  2844. }
  2845. }
  2846. }
  2847. func TestSchemaOfField(t *testing.T) {
  2848. type test struct {
  2849. field *descriptor.Field
  2850. refs refMap
  2851. expected openapiSchemaObject
  2852. openAPIOptions *openapiconfig.OpenAPIOptions
  2853. }
  2854. jsonSchema := &openapi_options.JSONSchema{
  2855. Title: "field title",
  2856. Description: "field description",
  2857. }
  2858. var fieldOptions = new(descriptorpb.FieldOptions)
  2859. proto.SetExtension(fieldOptions, openapi_options.E_Openapiv2Field, jsonSchema)
  2860. var requiredField = []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
  2861. var requiredFieldOptions = new(descriptorpb.FieldOptions)
  2862. proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, requiredField)
  2863. var outputOnlyField = []annotations.FieldBehavior{annotations.FieldBehavior_OUTPUT_ONLY}
  2864. var outputOnlyOptions = new(descriptorpb.FieldOptions)
  2865. proto.SetExtension(outputOnlyOptions, annotations.E_FieldBehavior, outputOnlyField)
  2866. tests := []test{
  2867. {
  2868. field: &descriptor.Field{
  2869. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  2870. Name: proto.String("primitive_field"),
  2871. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  2872. },
  2873. },
  2874. refs: make(refMap),
  2875. expected: openapiSchemaObject{
  2876. schemaCore: schemaCore{
  2877. Type: "string",
  2878. },
  2879. },
  2880. },
  2881. {
  2882. field: &descriptor.Field{
  2883. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  2884. Name: proto.String("repeated_primitive_field"),
  2885. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  2886. Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  2887. },
  2888. },
  2889. refs: make(refMap),
  2890. expected: openapiSchemaObject{
  2891. schemaCore: schemaCore{
  2892. Type: "array",
  2893. Items: &openapiItemsObject{
  2894. Type: "string",
  2895. },
  2896. },
  2897. },
  2898. },
  2899. {
  2900. field: &descriptor.Field{
  2901. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  2902. Name: proto.String("wrapped_field"),
  2903. TypeName: proto.String(".google.protobuf.FieldMask"),
  2904. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2905. },
  2906. },
  2907. refs: make(refMap),
  2908. expected: openapiSchemaObject{
  2909. schemaCore: schemaCore{
  2910. Type: "string",
  2911. },
  2912. },
  2913. },
  2914. {
  2915. field: &descriptor.Field{
  2916. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  2917. Name: proto.String("wrapped_field"),
  2918. TypeName: proto.String(".google.protobuf.Timestamp"),
  2919. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2920. },
  2921. },
  2922. refs: make(refMap),
  2923. expected: openapiSchemaObject{
  2924. schemaCore: schemaCore{
  2925. Type: "string",
  2926. Format: "date-time",
  2927. },
  2928. },
  2929. },
  2930. {
  2931. field: &descriptor.Field{
  2932. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  2933. Name: proto.String("wrapped_field"),
  2934. TypeName: proto.String(".google.protobuf.Duration"),
  2935. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2936. },
  2937. },
  2938. refs: make(refMap),
  2939. expected: openapiSchemaObject{
  2940. schemaCore: schemaCore{
  2941. Type: "string",
  2942. },
  2943. },
  2944. },
  2945. {
  2946. field: &descriptor.Field{
  2947. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  2948. Name: proto.String("wrapped_field"),
  2949. TypeName: proto.String(".google.protobuf.StringValue"),
  2950. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2951. },
  2952. },
  2953. refs: make(refMap),
  2954. expected: openapiSchemaObject{
  2955. schemaCore: schemaCore{
  2956. Type: "string",
  2957. },
  2958. },
  2959. },
  2960. {
  2961. field: &descriptor.Field{
  2962. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  2963. Name: proto.String("repeated_wrapped_field"),
  2964. TypeName: proto.String(".google.protobuf.StringValue"),
  2965. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2966. Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  2967. },
  2968. },
  2969. refs: make(refMap),
  2970. expected: openapiSchemaObject{
  2971. schemaCore: schemaCore{
  2972. Type: "array",
  2973. Items: &openapiItemsObject{
  2974. Type: "string",
  2975. },
  2976. },
  2977. },
  2978. },
  2979. {
  2980. field: &descriptor.Field{
  2981. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  2982. Name: proto.String("wrapped_field"),
  2983. TypeName: proto.String(".google.protobuf.BytesValue"),
  2984. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  2985. },
  2986. },
  2987. refs: make(refMap),
  2988. expected: openapiSchemaObject{
  2989. schemaCore: schemaCore{
  2990. Type: "string",
  2991. Format: "byte",
  2992. },
  2993. },
  2994. },
  2995. {
  2996. field: &descriptor.Field{
  2997. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  2998. Name: proto.String("wrapped_field"),
  2999. TypeName: proto.String(".google.protobuf.Int32Value"),
  3000. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3001. },
  3002. },
  3003. refs: make(refMap),
  3004. expected: openapiSchemaObject{
  3005. schemaCore: schemaCore{
  3006. Type: "integer",
  3007. Format: "int32",
  3008. },
  3009. },
  3010. },
  3011. {
  3012. field: &descriptor.Field{
  3013. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3014. Name: proto.String("wrapped_field"),
  3015. TypeName: proto.String(".google.protobuf.UInt32Value"),
  3016. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3017. },
  3018. },
  3019. refs: make(refMap),
  3020. expected: openapiSchemaObject{
  3021. schemaCore: schemaCore{
  3022. Type: "integer",
  3023. Format: "int64",
  3024. },
  3025. },
  3026. },
  3027. {
  3028. field: &descriptor.Field{
  3029. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3030. Name: proto.String("wrapped_field"),
  3031. TypeName: proto.String(".google.protobuf.Int64Value"),
  3032. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3033. },
  3034. },
  3035. refs: make(refMap),
  3036. expected: openapiSchemaObject{
  3037. schemaCore: schemaCore{
  3038. Type: "string",
  3039. Format: "int64",
  3040. },
  3041. },
  3042. },
  3043. {
  3044. field: &descriptor.Field{
  3045. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3046. Name: proto.String("wrapped_field"),
  3047. TypeName: proto.String(".google.protobuf.UInt64Value"),
  3048. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3049. },
  3050. },
  3051. refs: make(refMap),
  3052. expected: openapiSchemaObject{
  3053. schemaCore: schemaCore{
  3054. Type: "string",
  3055. Format: "uint64",
  3056. },
  3057. },
  3058. },
  3059. {
  3060. field: &descriptor.Field{
  3061. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3062. Name: proto.String("wrapped_field"),
  3063. TypeName: proto.String(".google.protobuf.FloatValue"),
  3064. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3065. },
  3066. },
  3067. refs: make(refMap),
  3068. expected: openapiSchemaObject{
  3069. schemaCore: schemaCore{
  3070. Type: "number",
  3071. Format: "float",
  3072. },
  3073. },
  3074. },
  3075. {
  3076. field: &descriptor.Field{
  3077. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3078. Name: proto.String("wrapped_field"),
  3079. TypeName: proto.String(".google.protobuf.DoubleValue"),
  3080. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3081. },
  3082. },
  3083. refs: make(refMap),
  3084. expected: openapiSchemaObject{
  3085. schemaCore: schemaCore{
  3086. Type: "number",
  3087. Format: "double",
  3088. },
  3089. },
  3090. },
  3091. {
  3092. field: &descriptor.Field{
  3093. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3094. Name: proto.String("wrapped_field"),
  3095. TypeName: proto.String(".google.protobuf.BoolValue"),
  3096. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3097. },
  3098. },
  3099. refs: make(refMap),
  3100. expected: openapiSchemaObject{
  3101. schemaCore: schemaCore{
  3102. Type: "boolean",
  3103. },
  3104. },
  3105. },
  3106. {
  3107. field: &descriptor.Field{
  3108. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3109. Name: proto.String("wrapped_field"),
  3110. TypeName: proto.String(".google.protobuf.Struct"),
  3111. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3112. },
  3113. },
  3114. refs: make(refMap),
  3115. expected: openapiSchemaObject{
  3116. schemaCore: schemaCore{
  3117. Type: "object",
  3118. },
  3119. },
  3120. },
  3121. {
  3122. field: &descriptor.Field{
  3123. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3124. Name: proto.String("wrapped_field"),
  3125. TypeName: proto.String(".google.protobuf.Value"),
  3126. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3127. },
  3128. },
  3129. refs: make(refMap),
  3130. expected: openapiSchemaObject{
  3131. schemaCore: schemaCore{
  3132. Type: "object",
  3133. },
  3134. },
  3135. },
  3136. {
  3137. field: &descriptor.Field{
  3138. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3139. Name: proto.String("wrapped_field"),
  3140. TypeName: proto.String(".google.protobuf.ListValue"),
  3141. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3142. },
  3143. },
  3144. refs: make(refMap),
  3145. expected: openapiSchemaObject{
  3146. schemaCore: schemaCore{
  3147. Type: "array",
  3148. Items: (*openapiItemsObject)(&schemaCore{
  3149. Type: "object",
  3150. }),
  3151. },
  3152. },
  3153. },
  3154. {
  3155. field: &descriptor.Field{
  3156. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3157. Name: proto.String("wrapped_field"),
  3158. TypeName: proto.String(".google.protobuf.NullValue"),
  3159. Type: descriptorpb.FieldDescriptorProto_TYPE_ENUM.Enum(),
  3160. },
  3161. },
  3162. refs: make(refMap),
  3163. expected: openapiSchemaObject{
  3164. schemaCore: schemaCore{
  3165. Type: "string",
  3166. },
  3167. },
  3168. },
  3169. {
  3170. field: &descriptor.Field{
  3171. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3172. Name: proto.String("message_field"),
  3173. TypeName: proto.String(".example.Message"),
  3174. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3175. },
  3176. },
  3177. refs: refMap{".example.Message": struct{}{}},
  3178. expected: openapiSchemaObject{
  3179. schemaCore: schemaCore{
  3180. Ref: "#/definitions/exampleMessage",
  3181. },
  3182. },
  3183. },
  3184. {
  3185. field: &descriptor.Field{
  3186. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3187. Name: proto.String("map_field"),
  3188. Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  3189. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3190. TypeName: proto.String(".example.Message.MapFieldEntry"),
  3191. Options: fieldOptions,
  3192. },
  3193. },
  3194. refs: make(refMap),
  3195. expected: openapiSchemaObject{
  3196. schemaCore: schemaCore{
  3197. Type: "object",
  3198. },
  3199. AdditionalProperties: &openapiSchemaObject{
  3200. schemaCore: schemaCore{Type: "string"},
  3201. },
  3202. Title: "field title",
  3203. Description: "field description",
  3204. },
  3205. },
  3206. {
  3207. field: &descriptor.Field{
  3208. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3209. Name: proto.String("array_field"),
  3210. Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  3211. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3212. Options: fieldOptions,
  3213. },
  3214. },
  3215. refs: make(refMap),
  3216. expected: openapiSchemaObject{
  3217. schemaCore: schemaCore{
  3218. Type: "array",
  3219. Items: (*openapiItemsObject)(&schemaCore{Type: "string"}),
  3220. },
  3221. Title: "field title",
  3222. Description: "field description",
  3223. },
  3224. },
  3225. {
  3226. field: &descriptor.Field{
  3227. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3228. Name: proto.String("primitive_field"),
  3229. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3230. Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  3231. Options: fieldOptions,
  3232. },
  3233. },
  3234. refs: make(refMap),
  3235. expected: openapiSchemaObject{
  3236. schemaCore: schemaCore{
  3237. Type: "integer",
  3238. Format: "int32",
  3239. },
  3240. Title: "field title",
  3241. Description: "field description",
  3242. },
  3243. },
  3244. {
  3245. field: &descriptor.Field{
  3246. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3247. Name: proto.String("message_field"),
  3248. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3249. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3250. TypeName: proto.String(".example.Empty"),
  3251. Options: fieldOptions,
  3252. },
  3253. },
  3254. refs: refMap{".example.Empty": struct{}{}},
  3255. expected: openapiSchemaObject{
  3256. schemaCore: schemaCore{
  3257. Ref: "#/definitions/exampleEmpty",
  3258. },
  3259. Title: "field title",
  3260. Description: "field description",
  3261. },
  3262. },
  3263. {
  3264. field: &descriptor.Field{
  3265. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3266. Name: proto.String("map_field"), // should be called map_field_option but it's not valid map field name
  3267. Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  3268. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3269. TypeName: proto.String(".example.Message.MapFieldEntry"),
  3270. },
  3271. },
  3272. openAPIOptions: &openapiconfig.OpenAPIOptions{
  3273. Field: []*openapiconfig.OpenAPIFieldOption{
  3274. {
  3275. Field: "example.Message.map_field",
  3276. Option: jsonSchema,
  3277. },
  3278. },
  3279. },
  3280. refs: make(refMap),
  3281. expected: openapiSchemaObject{
  3282. schemaCore: schemaCore{
  3283. Type: "object",
  3284. },
  3285. AdditionalProperties: &openapiSchemaObject{
  3286. schemaCore: schemaCore{Type: "string"},
  3287. },
  3288. Title: "field title",
  3289. Description: "field description",
  3290. },
  3291. },
  3292. {
  3293. field: &descriptor.Field{
  3294. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3295. Name: proto.String("array_field_option"),
  3296. Label: descriptorpb.FieldDescriptorProto_LABEL_REPEATED.Enum(),
  3297. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3298. },
  3299. },
  3300. openAPIOptions: &openapiconfig.OpenAPIOptions{
  3301. Field: []*openapiconfig.OpenAPIFieldOption{
  3302. {
  3303. Field: "example.Message.array_field_option",
  3304. Option: jsonSchema,
  3305. },
  3306. },
  3307. },
  3308. refs: make(refMap),
  3309. expected: openapiSchemaObject{
  3310. schemaCore: schemaCore{
  3311. Type: "array",
  3312. Items: (*openapiItemsObject)(&schemaCore{Type: "string"}),
  3313. },
  3314. Title: "field title",
  3315. Description: "field description",
  3316. },
  3317. },
  3318. {
  3319. field: &descriptor.Field{
  3320. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3321. Name: proto.String("primitive_field_option"),
  3322. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3323. Type: descriptorpb.FieldDescriptorProto_TYPE_INT32.Enum(),
  3324. },
  3325. },
  3326. openAPIOptions: &openapiconfig.OpenAPIOptions{
  3327. Field: []*openapiconfig.OpenAPIFieldOption{
  3328. {
  3329. Field: "example.Message.primitive_field_option",
  3330. Option: jsonSchema,
  3331. },
  3332. },
  3333. },
  3334. refs: make(refMap),
  3335. expected: openapiSchemaObject{
  3336. schemaCore: schemaCore{
  3337. Type: "integer",
  3338. Format: "int32",
  3339. },
  3340. Title: "field title",
  3341. Description: "field description",
  3342. },
  3343. },
  3344. {
  3345. field: &descriptor.Field{
  3346. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3347. Name: proto.String("message_field_option"),
  3348. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3349. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3350. TypeName: proto.String(".example.Empty"),
  3351. },
  3352. },
  3353. openAPIOptions: &openapiconfig.OpenAPIOptions{
  3354. Field: []*openapiconfig.OpenAPIFieldOption{
  3355. {
  3356. Field: "example.Message.message_field_option",
  3357. Option: jsonSchema,
  3358. },
  3359. },
  3360. },
  3361. refs: refMap{".example.Empty": struct{}{}},
  3362. expected: openapiSchemaObject{
  3363. schemaCore: schemaCore{
  3364. Ref: "#/definitions/exampleEmpty",
  3365. },
  3366. Title: "field title",
  3367. Description: "field description",
  3368. },
  3369. },
  3370. {
  3371. field: &descriptor.Field{
  3372. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3373. Name: proto.String("required_via_field_behavior_field"),
  3374. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3375. Options: requiredFieldOptions,
  3376. },
  3377. },
  3378. refs: make(refMap),
  3379. expected: openapiSchemaObject{
  3380. schemaCore: schemaCore{
  3381. Type: "string",
  3382. },
  3383. Required: []string{"required_via_field_behavior_field"},
  3384. },
  3385. },
  3386. {
  3387. field: &descriptor.Field{
  3388. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3389. Name: proto.String("readonly_via_field_behavior_field"),
  3390. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3391. Options: outputOnlyOptions,
  3392. },
  3393. },
  3394. refs: make(refMap),
  3395. expected: openapiSchemaObject{
  3396. schemaCore: schemaCore{
  3397. Type: "string",
  3398. },
  3399. ReadOnly: true,
  3400. },
  3401. },
  3402. {
  3403. field: &descriptor.Field{
  3404. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3405. Name: proto.String("message_field"),
  3406. TypeName: proto.String(".example.Message"),
  3407. Type: descriptorpb.FieldDescriptorProto_TYPE_MESSAGE.Enum(),
  3408. Options: requiredFieldOptions,
  3409. },
  3410. },
  3411. refs: refMap{".example.Message": struct{}{}},
  3412. expected: openapiSchemaObject{
  3413. schemaCore: schemaCore{
  3414. Ref: "#/definitions/exampleMessage",
  3415. },
  3416. },
  3417. },
  3418. }
  3419. for _, test := range tests {
  3420. reg := descriptor.NewRegistry()
  3421. req := &pluginpb.CodeGeneratorRequest{
  3422. ProtoFile: []*descriptorpb.FileDescriptorProto{
  3423. {
  3424. Name: proto.String("third_party/google.proto"),
  3425. Package: proto.String("google.protobuf"),
  3426. Options: &descriptorpb.FileOptions{
  3427. GoPackage: proto.String("third_party/google"),
  3428. },
  3429. MessageType: []*descriptorpb.DescriptorProto{
  3430. protodesc.ToDescriptorProto((&structpb.Struct{}).ProtoReflect().Descriptor()),
  3431. protodesc.ToDescriptorProto((&structpb.Value{}).ProtoReflect().Descriptor()),
  3432. protodesc.ToDescriptorProto((&structpb.ListValue{}).ProtoReflect().Descriptor()),
  3433. protodesc.ToDescriptorProto((&field_mask.FieldMask{}).ProtoReflect().Descriptor()),
  3434. protodesc.ToDescriptorProto((&timestamppb.Timestamp{}).ProtoReflect().Descriptor()),
  3435. protodesc.ToDescriptorProto((&durationpb.Duration{}).ProtoReflect().Descriptor()),
  3436. protodesc.ToDescriptorProto((&wrapperspb.StringValue{}).ProtoReflect().Descriptor()),
  3437. protodesc.ToDescriptorProto((&wrapperspb.BytesValue{}).ProtoReflect().Descriptor()),
  3438. protodesc.ToDescriptorProto((&wrapperspb.Int32Value{}).ProtoReflect().Descriptor()),
  3439. protodesc.ToDescriptorProto((&wrapperspb.UInt32Value{}).ProtoReflect().Descriptor()),
  3440. protodesc.ToDescriptorProto((&wrapperspb.Int64Value{}).ProtoReflect().Descriptor()),
  3441. protodesc.ToDescriptorProto((&wrapperspb.UInt64Value{}).ProtoReflect().Descriptor()),
  3442. protodesc.ToDescriptorProto((&wrapperspb.FloatValue{}).ProtoReflect().Descriptor()),
  3443. protodesc.ToDescriptorProto((&wrapperspb.DoubleValue{}).ProtoReflect().Descriptor()),
  3444. protodesc.ToDescriptorProto((&wrapperspb.BoolValue{}).ProtoReflect().Descriptor()),
  3445. },
  3446. EnumType: []*descriptorpb.EnumDescriptorProto{
  3447. protodesc.ToEnumDescriptorProto(structpb.NullValue(0).Descriptor()),
  3448. },
  3449. },
  3450. {
  3451. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  3452. Name: proto.String("example.proto"),
  3453. Package: proto.String("example"),
  3454. Dependency: []string{"third_party/google.proto"},
  3455. Options: &descriptorpb.FileOptions{
  3456. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  3457. },
  3458. MessageType: []*descriptorpb.DescriptorProto{
  3459. {
  3460. Name: proto.String("Message"),
  3461. Field: []*descriptorpb.FieldDescriptorProto{
  3462. {
  3463. Name: proto.String("value"),
  3464. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3465. Number: proto.Int32(1),
  3466. },
  3467. func() *descriptorpb.FieldDescriptorProto {
  3468. fd := test.field.FieldDescriptorProto
  3469. fd.Number = proto.Int32(2)
  3470. return fd
  3471. }(),
  3472. },
  3473. NestedType: []*descriptorpb.DescriptorProto{
  3474. {
  3475. Name: proto.String("MapFieldEntry"),
  3476. Options: &descriptorpb.MessageOptions{MapEntry: proto.Bool(true)},
  3477. Field: []*descriptorpb.FieldDescriptorProto{
  3478. {
  3479. Name: proto.String("key"),
  3480. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3481. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3482. Number: proto.Int32(1),
  3483. },
  3484. {
  3485. Name: proto.String("value"),
  3486. Label: descriptorpb.FieldDescriptorProto_LABEL_OPTIONAL.Enum(),
  3487. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3488. Number: proto.Int32(2),
  3489. },
  3490. },
  3491. },
  3492. },
  3493. },
  3494. {
  3495. Name: proto.String("Empty"),
  3496. },
  3497. },
  3498. EnumType: []*descriptorpb.EnumDescriptorProto{
  3499. {
  3500. Name: proto.String("MessageType"),
  3501. Value: []*descriptorpb.EnumValueDescriptorProto{
  3502. {
  3503. Name: proto.String("MESSAGE_TYPE_1"),
  3504. Number: proto.Int32(0),
  3505. },
  3506. },
  3507. },
  3508. },
  3509. Service: []*descriptorpb.ServiceDescriptorProto{},
  3510. },
  3511. },
  3512. }
  3513. err := reg.Load(req)
  3514. if err != nil {
  3515. t.Errorf("failed to reg.Load(req): %v", err)
  3516. }
  3517. // set field's parent message pointer to message so field can resolve its FQFN
  3518. test.field.Message = &descriptor.Message{
  3519. DescriptorProto: req.ProtoFile[1].MessageType[0],
  3520. File: &descriptor.File{
  3521. FileDescriptorProto: req.ProtoFile[1],
  3522. },
  3523. }
  3524. if test.openAPIOptions != nil {
  3525. if err := reg.RegisterOpenAPIOptions(test.openAPIOptions); err != nil {
  3526. t.Fatalf("failed to register OpenAPI options: %s", err)
  3527. }
  3528. }
  3529. refs := make(refMap)
  3530. actual := schemaOfField(test.field, reg, refs)
  3531. expectedSchemaObject := test.expected
  3532. if e, a := expectedSchemaObject, actual; !reflect.DeepEqual(a, e) {
  3533. t.Errorf("Expected schemaOfField(%v) = \n%#+v, actual: \n%#+v", test.field, e, a)
  3534. }
  3535. if !reflect.DeepEqual(refs, test.refs) {
  3536. t.Errorf("Expected schemaOfField(%v) to add refs %v, not %v", test.field, test.refs, refs)
  3537. }
  3538. }
  3539. }
  3540. func TestRenderMessagesAsDefinition(t *testing.T) {
  3541. jsonSchema := &openapi_options.JSONSchema{
  3542. Title: "field title",
  3543. Description: "field description",
  3544. Required: []string{"aRequiredField"},
  3545. }
  3546. var requiredField = new(descriptorpb.FieldOptions)
  3547. proto.SetExtension(requiredField, openapi_options.E_Openapiv2Field, jsonSchema)
  3548. var fieldBehaviorRequired = []annotations.FieldBehavior{annotations.FieldBehavior_REQUIRED}
  3549. var requiredFieldOptions = new(descriptorpb.FieldOptions)
  3550. proto.SetExtension(requiredFieldOptions, annotations.E_FieldBehavior, fieldBehaviorRequired)
  3551. var fieldBehaviorOutputOnlyField = []annotations.FieldBehavior{annotations.FieldBehavior_OUTPUT_ONLY}
  3552. var fieldBehaviorOutputOnlyOptions = new(descriptorpb.FieldOptions)
  3553. proto.SetExtension(fieldBehaviorOutputOnlyOptions, annotations.E_FieldBehavior, fieldBehaviorOutputOnlyField)
  3554. tests := []struct {
  3555. descr string
  3556. msgDescs []*descriptorpb.DescriptorProto
  3557. schema map[string]openapi_options.Schema // per-message schema to add
  3558. defs openapiDefinitionsObject
  3559. openAPIOptions *openapiconfig.OpenAPIOptions
  3560. excludedFields []*descriptor.Field
  3561. }{
  3562. {
  3563. descr: "no OpenAPI options",
  3564. msgDescs: []*descriptorpb.DescriptorProto{
  3565. {Name: proto.String("Message")},
  3566. },
  3567. schema: map[string]openapi_options.Schema{},
  3568. defs: map[string]openapiSchemaObject{
  3569. "Message": {schemaCore: schemaCore{Type: "object"}},
  3570. },
  3571. },
  3572. {
  3573. descr: "example option",
  3574. msgDescs: []*descriptorpb.DescriptorProto{
  3575. {Name: proto.String("Message")},
  3576. },
  3577. schema: map[string]openapi_options.Schema{
  3578. "Message": {
  3579. Example: `{"foo":"bar"}`,
  3580. },
  3581. },
  3582. defs: map[string]openapiSchemaObject{
  3583. "Message": {schemaCore: schemaCore{
  3584. Type: "object",
  3585. Example: json.RawMessage(`{"foo":"bar"}`),
  3586. }},
  3587. },
  3588. },
  3589. {
  3590. descr: "example option with something non-json",
  3591. msgDescs: []*descriptorpb.DescriptorProto{
  3592. {Name: proto.String("Message")},
  3593. },
  3594. schema: map[string]openapi_options.Schema{
  3595. "Message": {
  3596. Example: `XXXX anything goes XXXX`,
  3597. },
  3598. },
  3599. defs: map[string]openapiSchemaObject{
  3600. "Message": {schemaCore: schemaCore{
  3601. Type: "object",
  3602. Example: json.RawMessage(`XXXX anything goes XXXX`),
  3603. }},
  3604. },
  3605. },
  3606. {
  3607. descr: "external docs option",
  3608. msgDescs: []*descriptorpb.DescriptorProto{
  3609. {Name: proto.String("Message")},
  3610. },
  3611. schema: map[string]openapi_options.Schema{
  3612. "Message": {
  3613. ExternalDocs: &openapi_options.ExternalDocumentation{
  3614. Description: "glorious docs",
  3615. Url: "https://nada",
  3616. },
  3617. },
  3618. },
  3619. defs: map[string]openapiSchemaObject{
  3620. "Message": {
  3621. schemaCore: schemaCore{
  3622. Type: "object",
  3623. },
  3624. ExternalDocs: &openapiExternalDocumentationObject{
  3625. Description: "glorious docs",
  3626. URL: "https://nada",
  3627. },
  3628. },
  3629. },
  3630. },
  3631. {
  3632. descr: "JSONSchema options",
  3633. msgDescs: []*descriptorpb.DescriptorProto{
  3634. {Name: proto.String("Message")},
  3635. },
  3636. schema: map[string]openapi_options.Schema{
  3637. "Message": {
  3638. JsonSchema: &openapi_options.JSONSchema{
  3639. Title: "title",
  3640. Description: "desc",
  3641. MultipleOf: 100,
  3642. Maximum: 101,
  3643. ExclusiveMaximum: true,
  3644. Minimum: 1,
  3645. ExclusiveMinimum: true,
  3646. MaxLength: 10,
  3647. MinLength: 3,
  3648. Pattern: "[a-z]+",
  3649. MaxItems: 20,
  3650. MinItems: 2,
  3651. UniqueItems: true,
  3652. MaxProperties: 33,
  3653. MinProperties: 22,
  3654. Required: []string{"req"},
  3655. ReadOnly: true,
  3656. },
  3657. },
  3658. },
  3659. defs: map[string]openapiSchemaObject{
  3660. "Message": {
  3661. schemaCore: schemaCore{
  3662. Type: "object",
  3663. },
  3664. Title: "title",
  3665. Description: "desc",
  3666. MultipleOf: 100,
  3667. Maximum: 101,
  3668. ExclusiveMaximum: true,
  3669. Minimum: 1,
  3670. ExclusiveMinimum: true,
  3671. MaxLength: 10,
  3672. MinLength: 3,
  3673. Pattern: "[a-z]+",
  3674. MaxItems: 20,
  3675. MinItems: 2,
  3676. UniqueItems: true,
  3677. MaxProperties: 33,
  3678. MinProperties: 22,
  3679. Required: []string{"req"},
  3680. ReadOnly: true,
  3681. },
  3682. },
  3683. },
  3684. {
  3685. descr: "JSONSchema options from registry",
  3686. msgDescs: []*descriptorpb.DescriptorProto{
  3687. {Name: proto.String("Message")},
  3688. },
  3689. openAPIOptions: &openapiconfig.OpenAPIOptions{
  3690. Message: []*openapiconfig.OpenAPIMessageOption{
  3691. {
  3692. Message: "example.Message",
  3693. Option: &openapi_options.Schema{
  3694. JsonSchema: &openapi_options.JSONSchema{
  3695. Title: "title",
  3696. Description: "desc",
  3697. MultipleOf: 100,
  3698. Maximum: 101,
  3699. ExclusiveMaximum: true,
  3700. Minimum: 1,
  3701. ExclusiveMinimum: true,
  3702. MaxLength: 10,
  3703. MinLength: 3,
  3704. Pattern: "[a-z]+",
  3705. MaxItems: 20,
  3706. MinItems: 2,
  3707. UniqueItems: true,
  3708. MaxProperties: 33,
  3709. MinProperties: 22,
  3710. Required: []string{"req"},
  3711. ReadOnly: true,
  3712. },
  3713. },
  3714. },
  3715. },
  3716. },
  3717. defs: map[string]openapiSchemaObject{
  3718. "Message": {
  3719. schemaCore: schemaCore{
  3720. Type: "object",
  3721. },
  3722. Title: "title",
  3723. Description: "desc",
  3724. MultipleOf: 100,
  3725. Maximum: 101,
  3726. ExclusiveMaximum: true,
  3727. Minimum: 1,
  3728. ExclusiveMinimum: true,
  3729. MaxLength: 10,
  3730. MinLength: 3,
  3731. Pattern: "[a-z]+",
  3732. MaxItems: 20,
  3733. MinItems: 2,
  3734. UniqueItems: true,
  3735. MaxProperties: 33,
  3736. MinProperties: 22,
  3737. Required: []string{"req"},
  3738. ReadOnly: true,
  3739. },
  3740. },
  3741. },
  3742. {
  3743. descr: "JSONSchema with required properties",
  3744. msgDescs: []*descriptorpb.DescriptorProto{
  3745. {
  3746. Name: proto.String("Message"),
  3747. Field: []*descriptorpb.FieldDescriptorProto{
  3748. {
  3749. Name: proto.String("aRequiredField"),
  3750. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3751. Number: proto.Int32(1),
  3752. Options: requiredField,
  3753. },
  3754. },
  3755. },
  3756. },
  3757. schema: map[string]openapi_options.Schema{
  3758. "Message": {
  3759. JsonSchema: &openapi_options.JSONSchema{
  3760. Title: "title",
  3761. Description: "desc",
  3762. Required: []string{"req"},
  3763. },
  3764. },
  3765. },
  3766. defs: map[string]openapiSchemaObject{
  3767. "Message": {
  3768. schemaCore: schemaCore{
  3769. Type: "object",
  3770. },
  3771. Title: "title",
  3772. Description: "desc",
  3773. Required: []string{"req", "aRequiredField"},
  3774. Properties: &openapiSchemaObjectProperties{
  3775. {
  3776. Key: "aRequiredField",
  3777. Value: openapiSchemaObject{
  3778. schemaCore: schemaCore{
  3779. Type: "string",
  3780. },
  3781. Description: "field description",
  3782. Title: "field title",
  3783. Required: []string{"aRequiredField"},
  3784. },
  3785. },
  3786. },
  3787. },
  3788. },
  3789. },
  3790. {
  3791. descr: "JSONSchema with excluded fields",
  3792. msgDescs: []*descriptorpb.DescriptorProto{
  3793. {
  3794. Name: proto.String("Message"),
  3795. Field: []*descriptorpb.FieldDescriptorProto{
  3796. {
  3797. Name: proto.String("aRequiredField"),
  3798. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3799. Number: proto.Int32(1),
  3800. Options: requiredField,
  3801. },
  3802. {
  3803. Name: proto.String("anExcludedField"),
  3804. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3805. Number: proto.Int32(2),
  3806. },
  3807. },
  3808. },
  3809. },
  3810. schema: map[string]openapi_options.Schema{
  3811. "Message": {
  3812. JsonSchema: &openapi_options.JSONSchema{
  3813. Title: "title",
  3814. Description: "desc",
  3815. Required: []string{"req"},
  3816. },
  3817. },
  3818. },
  3819. defs: map[string]openapiSchemaObject{
  3820. "Message": {
  3821. schemaCore: schemaCore{
  3822. Type: "object",
  3823. },
  3824. Title: "title",
  3825. Description: "desc",
  3826. Required: []string{"req", "aRequiredField"},
  3827. Properties: &openapiSchemaObjectProperties{
  3828. {
  3829. Key: "aRequiredField",
  3830. Value: openapiSchemaObject{
  3831. schemaCore: schemaCore{
  3832. Type: "string",
  3833. },
  3834. Description: "field description",
  3835. Title: "field title",
  3836. Required: []string{"aRequiredField"},
  3837. },
  3838. },
  3839. },
  3840. },
  3841. },
  3842. excludedFields: []*descriptor.Field{
  3843. {
  3844. FieldDescriptorProto: &descriptorpb.FieldDescriptorProto{
  3845. Name: strPtr("anExcludedField"),
  3846. },
  3847. },
  3848. },
  3849. },
  3850. {
  3851. descr: "JSONSchema with required properties via field_behavior",
  3852. msgDescs: []*descriptorpb.DescriptorProto{
  3853. {
  3854. Name: proto.String("Message"),
  3855. Field: []*descriptorpb.FieldDescriptorProto{
  3856. {
  3857. Name: proto.String("aRequiredField"),
  3858. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3859. Number: proto.Int32(1),
  3860. Options: requiredFieldOptions,
  3861. },
  3862. {
  3863. Name: proto.String("aOutputOnlyField"),
  3864. Type: descriptorpb.FieldDescriptorProto_TYPE_STRING.Enum(),
  3865. Number: proto.Int32(2),
  3866. Options: fieldBehaviorOutputOnlyOptions,
  3867. },
  3868. },
  3869. },
  3870. },
  3871. schema: map[string]openapi_options.Schema{
  3872. "Message": {
  3873. JsonSchema: &openapi_options.JSONSchema{
  3874. Title: "title",
  3875. Description: "desc",
  3876. Required: []string{"req"},
  3877. },
  3878. },
  3879. },
  3880. defs: map[string]openapiSchemaObject{
  3881. "Message": {
  3882. schemaCore: schemaCore{
  3883. Type: "object",
  3884. },
  3885. Title: "title",
  3886. Description: "desc",
  3887. Required: []string{"req", "aRequiredField"},
  3888. Properties: &openapiSchemaObjectProperties{
  3889. {
  3890. Key: "aRequiredField",
  3891. Value: openapiSchemaObject{
  3892. schemaCore: schemaCore{
  3893. Type: "string",
  3894. },
  3895. Required: []string{"aRequiredField"},
  3896. },
  3897. },
  3898. {
  3899. Key: "aOutputOnlyField",
  3900. Value: openapiSchemaObject{
  3901. schemaCore: schemaCore{
  3902. Type: "string",
  3903. },
  3904. ReadOnly: true,
  3905. },
  3906. },
  3907. },
  3908. },
  3909. },
  3910. },
  3911. }
  3912. for _, test := range tests {
  3913. t.Run(test.descr, func(t *testing.T) {
  3914. msgs := []*descriptor.Message{}
  3915. for _, msgdesc := range test.msgDescs {
  3916. msgdesc.Options = &descriptorpb.MessageOptions{}
  3917. msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  3918. }
  3919. reg := descriptor.NewRegistry()
  3920. file := descriptor.File{
  3921. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  3922. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  3923. Name: proto.String("example.proto"),
  3924. Package: proto.String("example"),
  3925. Dependency: []string{},
  3926. MessageType: test.msgDescs,
  3927. EnumType: []*descriptorpb.EnumDescriptorProto{},
  3928. Service: []*descriptorpb.ServiceDescriptorProto{},
  3929. Options: &descriptorpb.FileOptions{
  3930. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  3931. },
  3932. },
  3933. Messages: msgs,
  3934. }
  3935. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  3936. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  3937. })
  3938. if err != nil {
  3939. t.Fatalf("failed to load code generator request: %v", err)
  3940. }
  3941. msgMap := map[string]*descriptor.Message{}
  3942. for _, d := range test.msgDescs {
  3943. name := d.GetName()
  3944. msg, err := reg.LookupMsg("example", name)
  3945. if err != nil {
  3946. t.Fatalf("lookup message %v: %v", name, err)
  3947. }
  3948. msgMap[msg.FQMN()] = msg
  3949. if schema, ok := test.schema[name]; ok {
  3950. proto.SetExtension(d.Options, openapi_options.E_Openapiv2Schema, &schema)
  3951. }
  3952. }
  3953. if test.openAPIOptions != nil {
  3954. if err := reg.RegisterOpenAPIOptions(test.openAPIOptions); err != nil {
  3955. t.Fatalf("failed to register OpenAPI options: %s", err)
  3956. }
  3957. }
  3958. refs := make(refMap)
  3959. actual := make(openapiDefinitionsObject)
  3960. renderMessagesAsDefinition(msgMap, actual, reg, refs, test.excludedFields)
  3961. if !reflect.DeepEqual(actual, test.defs) {
  3962. t.Errorf("Expected renderMessagesAsDefinition() to add defs %+v, not %+v", test.defs, actual)
  3963. }
  3964. })
  3965. }
  3966. }
  3967. func strPtr(s string) *string {
  3968. return &s
  3969. }
  3970. func TestUpdateOpenAPIDataFromComments(t *testing.T) {
  3971. tests := []struct {
  3972. descr string
  3973. openapiSwaggerObject interface{}
  3974. comments string
  3975. expectedError error
  3976. expectedOpenAPIObject interface{}
  3977. useGoTemplate bool
  3978. }{
  3979. {
  3980. descr: "empty comments",
  3981. openapiSwaggerObject: nil,
  3982. expectedOpenAPIObject: nil,
  3983. comments: "",
  3984. expectedError: nil,
  3985. },
  3986. {
  3987. descr: "set field to read only",
  3988. openapiSwaggerObject: &openapiSchemaObject{},
  3989. expectedOpenAPIObject: &openapiSchemaObject{
  3990. ReadOnly: true,
  3991. Description: "... Output only. ...",
  3992. },
  3993. comments: "... Output only. ...",
  3994. expectedError: nil,
  3995. },
  3996. {
  3997. descr: "set title",
  3998. openapiSwaggerObject: &openapiSchemaObject{},
  3999. expectedOpenAPIObject: &openapiSchemaObject{
  4000. Title: "Comment with no trailing dot",
  4001. },
  4002. comments: "Comment with no trailing dot",
  4003. expectedError: nil,
  4004. },
  4005. {
  4006. descr: "set description",
  4007. openapiSwaggerObject: &openapiSchemaObject{},
  4008. expectedOpenAPIObject: &openapiSchemaObject{
  4009. Description: "Comment with trailing dot.",
  4010. },
  4011. comments: "Comment with trailing dot.",
  4012. expectedError: nil,
  4013. },
  4014. {
  4015. descr: "use info object",
  4016. openapiSwaggerObject: &openapiSwaggerObject{
  4017. Info: openapiInfoObject{},
  4018. },
  4019. expectedOpenAPIObject: &openapiSwaggerObject{
  4020. Info: openapiInfoObject{
  4021. Description: "Comment with trailing dot.",
  4022. },
  4023. },
  4024. comments: "Comment with trailing dot.",
  4025. expectedError: nil,
  4026. },
  4027. {
  4028. descr: "multi line comment with title",
  4029. openapiSwaggerObject: &openapiSchemaObject{},
  4030. expectedOpenAPIObject: &openapiSchemaObject{
  4031. Title: "First line",
  4032. Description: "Second line",
  4033. },
  4034. comments: "First line\n\nSecond line",
  4035. expectedError: nil,
  4036. },
  4037. {
  4038. descr: "multi line comment no title",
  4039. openapiSwaggerObject: &openapiSchemaObject{},
  4040. expectedOpenAPIObject: &openapiSchemaObject{
  4041. Description: "First line.\n\nSecond line",
  4042. },
  4043. comments: "First line.\n\nSecond line",
  4044. expectedError: nil,
  4045. },
  4046. {
  4047. descr: "multi line comment with summary with dot",
  4048. openapiSwaggerObject: &openapiOperationObject{},
  4049. expectedOpenAPIObject: &openapiOperationObject{
  4050. Summary: "First line.",
  4051. Description: "Second line",
  4052. },
  4053. comments: "First line.\n\nSecond line",
  4054. expectedError: nil,
  4055. },
  4056. {
  4057. descr: "multi line comment with summary no dot",
  4058. openapiSwaggerObject: &openapiOperationObject{},
  4059. expectedOpenAPIObject: &openapiOperationObject{
  4060. Summary: "First line",
  4061. Description: "Second line",
  4062. },
  4063. comments: "First line\n\nSecond line",
  4064. expectedError: nil,
  4065. },
  4066. {
  4067. descr: "multi line comment with summary no dot",
  4068. openapiSwaggerObject: &schemaCore{},
  4069. expectedOpenAPIObject: &schemaCore{},
  4070. comments: "Any comment",
  4071. expectedError: errors.New("no description nor summary property"),
  4072. },
  4073. {
  4074. descr: "without use_go_template",
  4075. openapiSwaggerObject: &openapiSchemaObject{},
  4076. expectedOpenAPIObject: &openapiSchemaObject{
  4077. Title: "First line",
  4078. Description: "{{import \"documentation.md\"}}",
  4079. },
  4080. comments: "First line\n\n{{import \"documentation.md\"}}",
  4081. expectedError: nil,
  4082. },
  4083. {
  4084. descr: "error with use_go_template",
  4085. openapiSwaggerObject: &openapiSchemaObject{},
  4086. expectedOpenAPIObject: &openapiSchemaObject{
  4087. Title: "First line",
  4088. Description: "open noneexistingfile.txt: no such file or directory",
  4089. },
  4090. comments: "First line\n\n{{import \"noneexistingfile.txt\"}}",
  4091. expectedError: nil,
  4092. useGoTemplate: true,
  4093. },
  4094. {
  4095. descr: "template with use_go_template",
  4096. openapiSwaggerObject: &openapiSchemaObject{},
  4097. expectedOpenAPIObject: &openapiSchemaObject{
  4098. Title: "Template",
  4099. Description: `Description "which means nothing"`,
  4100. },
  4101. comments: "Template\n\nDescription {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  4102. expectedError: nil,
  4103. useGoTemplate: true,
  4104. },
  4105. }
  4106. for _, test := range tests {
  4107. t.Run(test.descr, func(t *testing.T) {
  4108. reg := descriptor.NewRegistry()
  4109. if test.useGoTemplate {
  4110. reg.SetUseGoTemplate(true)
  4111. }
  4112. err := updateOpenAPIDataFromComments(reg, test.openapiSwaggerObject, nil, test.comments, false)
  4113. if test.expectedError == nil {
  4114. if err != nil {
  4115. t.Errorf("unexpected error '%v'", err)
  4116. }
  4117. if !reflect.DeepEqual(test.openapiSwaggerObject, test.expectedOpenAPIObject) {
  4118. t.Errorf("openapiSwaggerObject was not updated correctly, expected '%+v', got '%+v'", test.expectedOpenAPIObject, test.openapiSwaggerObject)
  4119. }
  4120. } else {
  4121. if err == nil {
  4122. t.Error("expected update error not returned")
  4123. }
  4124. if !reflect.DeepEqual(test.openapiSwaggerObject, test.expectedOpenAPIObject) {
  4125. t.Errorf("openapiSwaggerObject was not updated correctly, expected '%+v', got '%+v'", test.expectedOpenAPIObject, test.openapiSwaggerObject)
  4126. }
  4127. if err.Error() != test.expectedError.Error() {
  4128. t.Errorf("expected error malformed, expected %q, got %q", test.expectedError.Error(), err.Error())
  4129. }
  4130. }
  4131. })
  4132. }
  4133. }
  4134. func TestMessageOptionsWithGoTemplate(t *testing.T) {
  4135. tests := []struct {
  4136. descr string
  4137. msgDescs []*descriptorpb.DescriptorProto
  4138. schema map[string]openapi_options.Schema // per-message schema to add
  4139. defs openapiDefinitionsObject
  4140. openAPIOptions *openapiconfig.OpenAPIOptions
  4141. useGoTemplate bool
  4142. }{
  4143. {
  4144. descr: "external docs option",
  4145. msgDescs: []*descriptorpb.DescriptorProto{
  4146. {Name: proto.String("Message")},
  4147. },
  4148. schema: map[string]openapi_options.Schema{
  4149. "Message": {
  4150. JsonSchema: &openapi_options.JSONSchema{
  4151. Title: "{{.Name}}",
  4152. Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  4153. },
  4154. ExternalDocs: &openapi_options.ExternalDocumentation{
  4155. Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  4156. },
  4157. },
  4158. },
  4159. defs: map[string]openapiSchemaObject{
  4160. "Message": {
  4161. schemaCore: schemaCore{
  4162. Type: "object",
  4163. },
  4164. Title: "Message",
  4165. Description: `Description "which means nothing"`,
  4166. ExternalDocs: &openapiExternalDocumentationObject{
  4167. Description: `Description "which means nothing"`,
  4168. },
  4169. },
  4170. },
  4171. useGoTemplate: true,
  4172. },
  4173. {
  4174. descr: "external docs option",
  4175. msgDescs: []*descriptorpb.DescriptorProto{
  4176. {Name: proto.String("Message")},
  4177. },
  4178. schema: map[string]openapi_options.Schema{
  4179. "Message": {
  4180. JsonSchema: &openapi_options.JSONSchema{
  4181. Title: "{{.Name}}",
  4182. Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  4183. },
  4184. ExternalDocs: &openapi_options.ExternalDocumentation{
  4185. Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  4186. },
  4187. },
  4188. },
  4189. defs: map[string]openapiSchemaObject{
  4190. "Message": {
  4191. schemaCore: schemaCore{
  4192. Type: "object",
  4193. },
  4194. Title: "{{.Name}}",
  4195. Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  4196. ExternalDocs: &openapiExternalDocumentationObject{
  4197. Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  4198. },
  4199. },
  4200. },
  4201. useGoTemplate: false,
  4202. },
  4203. {
  4204. descr: "registered OpenAPIOption",
  4205. msgDescs: []*descriptorpb.DescriptorProto{
  4206. {Name: proto.String("Message")},
  4207. },
  4208. openAPIOptions: &openapiconfig.OpenAPIOptions{
  4209. Message: []*openapiconfig.OpenAPIMessageOption{
  4210. {
  4211. Message: "example.Message",
  4212. Option: &openapi_options.Schema{
  4213. JsonSchema: &openapi_options.JSONSchema{
  4214. Title: "{{.Name}}",
  4215. Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  4216. },
  4217. ExternalDocs: &openapi_options.ExternalDocumentation{
  4218. Description: "Description {{with \"which means nothing\"}}{{printf \"%q\" .}}{{end}}",
  4219. },
  4220. },
  4221. },
  4222. },
  4223. },
  4224. defs: map[string]openapiSchemaObject{
  4225. "Message": {
  4226. schemaCore: schemaCore{
  4227. Type: "object",
  4228. },
  4229. Title: "Message",
  4230. Description: `Description "which means nothing"`,
  4231. ExternalDocs: &openapiExternalDocumentationObject{
  4232. Description: `Description "which means nothing"`,
  4233. },
  4234. },
  4235. },
  4236. useGoTemplate: true,
  4237. },
  4238. }
  4239. for _, test := range tests {
  4240. t.Run(test.descr, func(t *testing.T) {
  4241. msgs := []*descriptor.Message{}
  4242. for _, msgdesc := range test.msgDescs {
  4243. msgdesc.Options = &descriptorpb.MessageOptions{}
  4244. msgs = append(msgs, &descriptor.Message{DescriptorProto: msgdesc})
  4245. }
  4246. reg := descriptor.NewRegistry()
  4247. reg.SetUseGoTemplate(test.useGoTemplate)
  4248. file := descriptor.File{
  4249. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  4250. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  4251. Name: proto.String("example.proto"),
  4252. Package: proto.String("example"),
  4253. Dependency: []string{},
  4254. MessageType: test.msgDescs,
  4255. EnumType: []*descriptorpb.EnumDescriptorProto{},
  4256. Service: []*descriptorpb.ServiceDescriptorProto{},
  4257. Options: &descriptorpb.FileOptions{
  4258. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  4259. },
  4260. },
  4261. Messages: msgs,
  4262. }
  4263. err := reg.Load(&pluginpb.CodeGeneratorRequest{
  4264. ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto},
  4265. })
  4266. if err != nil {
  4267. t.Fatalf("failed to load code generator request: %v", err)
  4268. }
  4269. msgMap := map[string]*descriptor.Message{}
  4270. for _, d := range test.msgDescs {
  4271. name := d.GetName()
  4272. msg, err := reg.LookupMsg("example", name)
  4273. if err != nil {
  4274. t.Fatalf("lookup message %v: %v", name, err)
  4275. }
  4276. msgMap[msg.FQMN()] = msg
  4277. if schema, ok := test.schema[name]; ok {
  4278. proto.SetExtension(d.Options, openapi_options.E_Openapiv2Schema, &schema)
  4279. }
  4280. }
  4281. if test.openAPIOptions != nil {
  4282. if err := reg.RegisterOpenAPIOptions(test.openAPIOptions); err != nil {
  4283. t.Fatalf("failed to register OpenAPI options: %s", err)
  4284. }
  4285. }
  4286. refs := make(refMap)
  4287. actual := make(openapiDefinitionsObject)
  4288. renderMessagesAsDefinition(msgMap, actual, reg, refs, nil)
  4289. if !reflect.DeepEqual(actual, test.defs) {
  4290. t.Errorf("Expected renderMessagesAsDefinition() to add defs %+v, not %+v", test.defs, actual)
  4291. }
  4292. })
  4293. }
  4294. }
  4295. func TestTemplateWithoutErrorDefinition(t *testing.T) {
  4296. msgdesc := &descriptorpb.DescriptorProto{
  4297. Name: proto.String("ExampleMessage"),
  4298. Field: []*descriptorpb.FieldDescriptorProto{},
  4299. }
  4300. meth := &descriptorpb.MethodDescriptorProto{
  4301. Name: proto.String("Echo"),
  4302. InputType: proto.String("ExampleMessage"),
  4303. OutputType: proto.String("ExampleMessage"),
  4304. }
  4305. svc := &descriptorpb.ServiceDescriptorProto{
  4306. Name: proto.String("ExampleService"),
  4307. Method: []*descriptorpb.MethodDescriptorProto{meth},
  4308. }
  4309. msg := &descriptor.Message{
  4310. DescriptorProto: msgdesc,
  4311. }
  4312. file := descriptor.File{
  4313. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  4314. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  4315. Name: proto.String("example.proto"),
  4316. Package: proto.String("example"),
  4317. MessageType: []*descriptorpb.DescriptorProto{msgdesc},
  4318. Service: []*descriptorpb.ServiceDescriptorProto{svc},
  4319. Options: &descriptorpb.FileOptions{
  4320. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  4321. },
  4322. },
  4323. GoPkg: descriptor.GoPackage{
  4324. Path: "example.com/path/to/example/example.pb",
  4325. Name: "example_pb",
  4326. },
  4327. Messages: []*descriptor.Message{msg},
  4328. Services: []*descriptor.Service{
  4329. {
  4330. ServiceDescriptorProto: svc,
  4331. Methods: []*descriptor.Method{
  4332. {
  4333. MethodDescriptorProto: meth,
  4334. RequestType: msg,
  4335. ResponseType: msg,
  4336. Bindings: []*descriptor.Binding{
  4337. {
  4338. HTTPMethod: "POST",
  4339. PathTmpl: httprule.Template{
  4340. Version: 1,
  4341. OpCodes: []int{0, 0},
  4342. Template: "/v1/echo",
  4343. },
  4344. Body: &descriptor.Body{
  4345. FieldPath: descriptor.FieldPath([]descriptor.FieldPathComponent{}),
  4346. },
  4347. },
  4348. },
  4349. },
  4350. },
  4351. },
  4352. },
  4353. }
  4354. reg := descriptor.NewRegistry()
  4355. err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  4356. if err != nil {
  4357. t.Errorf("failed to reg.Load(): %v", err)
  4358. return
  4359. }
  4360. result, err := applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  4361. if err != nil {
  4362. t.Errorf("applyTemplate(%#v) failed with %v; want success", file, err)
  4363. return
  4364. }
  4365. defRsp, ok := result.Paths["/v1/echo"].Post.Responses["default"]
  4366. if !ok {
  4367. return
  4368. }
  4369. ref := defRsp.Schema.schemaCore.Ref
  4370. refName := strings.TrimPrefix(ref, "#/definitions/")
  4371. if refName == "" {
  4372. t.Fatal("created default Error response with empty reflink")
  4373. }
  4374. if _, ok := result.Definitions[refName]; !ok {
  4375. t.Errorf("default Error response with reflink '%v', but its definition was not found", refName)
  4376. }
  4377. }
  4378. func Test_getReservedJsonName(t *testing.T) {
  4379. type args struct {
  4380. fieldName string
  4381. messageNameToFieldsToJSONName map[string]map[string]string
  4382. fieldNameToType map[string]string
  4383. }
  4384. tests := []struct {
  4385. name string
  4386. args args
  4387. want string
  4388. }{
  4389. {
  4390. "test case 1: single dot use case",
  4391. args{
  4392. fieldName: "abc.a_1",
  4393. messageNameToFieldsToJSONName: map[string]map[string]string{
  4394. "Msg": {
  4395. "a_1": "a1JSONNAME",
  4396. "b_1": "b1JSONNAME",
  4397. },
  4398. },
  4399. fieldNameToType: map[string]string{
  4400. "abc": "pkg1.test.Msg",
  4401. "bcd": "pkg1.test.Msg",
  4402. },
  4403. },
  4404. "a1JSONNAME",
  4405. },
  4406. {
  4407. "test case 2: single dot use case with no existing field",
  4408. args{
  4409. fieldName: "abc.d_1",
  4410. messageNameToFieldsToJSONName: map[string]map[string]string{
  4411. "Msg": {
  4412. "a_1": "a1JSONNAME",
  4413. "b_1": "b1JSONNAME",
  4414. },
  4415. },
  4416. fieldNameToType: map[string]string{
  4417. "abc": "pkg1.test.Msg",
  4418. "bcd": "pkg1.test.Msg",
  4419. },
  4420. },
  4421. "",
  4422. },
  4423. {
  4424. "test case 3: double dot use case",
  4425. args{
  4426. fieldName: "pkg.abc.a_1",
  4427. messageNameToFieldsToJSONName: map[string]map[string]string{
  4428. "Msg": {
  4429. "a_1": "a1JSONNAME",
  4430. "b_1": "b1JSONNAME",
  4431. },
  4432. },
  4433. fieldNameToType: map[string]string{
  4434. "abc": "pkg1.test.Msg",
  4435. "bcd": "pkg1.test.Msg",
  4436. },
  4437. },
  4438. "a1JSONNAME",
  4439. },
  4440. {
  4441. "test case 4: double dot use case with a not existed field",
  4442. args{
  4443. fieldName: "pkg.abc.c_1",
  4444. messageNameToFieldsToJSONName: map[string]map[string]string{
  4445. "Msg": {
  4446. "a_1": "a1JSONNAME",
  4447. "b_1": "b1JSONNAME",
  4448. },
  4449. },
  4450. fieldNameToType: map[string]string{
  4451. "abc": "pkg1.test.Msg",
  4452. "bcd": "pkg1.test.Msg",
  4453. },
  4454. },
  4455. "",
  4456. },
  4457. }
  4458. for _, tt := range tests {
  4459. t.Run(tt.name, func(t *testing.T) {
  4460. if got := getReservedJSONName(tt.args.fieldName, tt.args.messageNameToFieldsToJSONName, tt.args.fieldNameToType); got != tt.want {
  4461. t.Errorf("getReservedJSONName() = %v, want %v", got, tt.want)
  4462. }
  4463. })
  4464. }
  4465. }
  4466. func TestParseIncompleteSecurityRequirement(t *testing.T) {
  4467. swagger := openapi_options.Swagger{
  4468. Security: []*openapi_options.SecurityRequirement{
  4469. {
  4470. SecurityRequirement: map[string]*openapi_options.SecurityRequirement_SecurityRequirementValue{
  4471. "key": nil,
  4472. },
  4473. },
  4474. },
  4475. }
  4476. file := descriptor.File{
  4477. FileDescriptorProto: &descriptorpb.FileDescriptorProto{
  4478. SourceCodeInfo: &descriptorpb.SourceCodeInfo{},
  4479. Name: proto.String("example.proto"),
  4480. Package: proto.String("example"),
  4481. Options: &descriptorpb.FileOptions{
  4482. GoPackage: proto.String("github.com/grpc-ecosystem/grpc-gateway/runtime/internal/examplepb;example"),
  4483. },
  4484. },
  4485. }
  4486. proto.SetExtension(proto.Message(file.FileDescriptorProto.Options), openapi_options.E_Openapiv2Swagger, &swagger)
  4487. reg := descriptor.NewRegistry()
  4488. err := reg.Load(&pluginpb.CodeGeneratorRequest{ProtoFile: []*descriptorpb.FileDescriptorProto{file.FileDescriptorProto}})
  4489. if err != nil {
  4490. t.Errorf("failed to reg.Load(): %v", err)
  4491. return
  4492. }
  4493. _, err = applyTemplate(param{File: crossLinkFixture(&file), reg: reg})
  4494. if err == nil {
  4495. t.Errorf("applyTemplate(%#v) did not error as expected", file)
  4496. return
  4497. }
  4498. }