template_test.go 318 KB

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