template.go 77 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454
  1. package genopenapi
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "fmt"
  6. "io/ioutil"
  7. "math"
  8. "net/textproto"
  9. "reflect"
  10. "regexp"
  11. "sort"
  12. "strconv"
  13. "strings"
  14. "sync"
  15. "text/template"
  16. "time"
  17. "git.ikuban.com/server/swagger-api/protoc-gen-openapiv2/internal/casing"
  18. "git.ikuban.com/server/swagger-api/protoc-gen-openapiv2/internal/descriptor"
  19. "github.com/golang/glog"
  20. openapi_options "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
  21. "google.golang.org/genproto/googleapis/api/annotations"
  22. "google.golang.org/protobuf/encoding/protojson"
  23. "google.golang.org/protobuf/proto"
  24. "google.golang.org/protobuf/types/descriptorpb"
  25. "google.golang.org/protobuf/types/known/structpb"
  26. )
  27. // wktSchemas are the schemas of well-known-types.
  28. // The schemas must match with the behavior of the JSON unmarshaler in
  29. // https://github.com/protocolbuffers/protobuf-go/blob/v1.25.0/encoding/protojson/well_known_types.go
  30. var wktSchemas = map[string]schemaCore{
  31. ".google.protobuf.FieldMask": {
  32. Type: "string",
  33. },
  34. ".google.protobuf.Timestamp": {
  35. Type: "string",
  36. Format: "date-time",
  37. },
  38. ".google.protobuf.Duration": {
  39. Type: "integer",
  40. },
  41. ".google.protobuf.StringValue": {
  42. Type: "string",
  43. },
  44. ".google.protobuf.BytesValue": {
  45. Type: "string",
  46. Format: "byte",
  47. },
  48. ".google.protobuf.Int32Value": {
  49. Type: "integer",
  50. Format: "int32",
  51. },
  52. ".google.protobuf.UInt32Value": {
  53. Type: "integer",
  54. Format: "int64",
  55. },
  56. ".google.protobuf.Int64Value": {
  57. Type: "integer",
  58. Format: "int64",
  59. },
  60. ".google.protobuf.UInt64Value": {
  61. Type: "integer",
  62. Format: "uint64",
  63. },
  64. ".google.protobuf.FloatValue": {
  65. Type: "number",
  66. Format: "float",
  67. },
  68. ".google.protobuf.DoubleValue": {
  69. Type: "number",
  70. Format: "double",
  71. },
  72. ".google.protobuf.BoolValue": {
  73. Type: "boolean",
  74. },
  75. ".google.protobuf.Empty": {},
  76. ".google.protobuf.Struct": {
  77. Type: "object",
  78. },
  79. ".google.protobuf.Value": {
  80. Type: "object",
  81. },
  82. ".google.protobuf.ListValue": {
  83. Type: "array",
  84. Items: (*openapiItemsObject)(&schemaCore{
  85. Type: "object",
  86. }),
  87. },
  88. ".google.protobuf.NullValue": {
  89. Type: "string",
  90. },
  91. }
  92. func listEnumNames(enum *descriptor.Enum) (names []string) {
  93. for _, value := range enum.GetValue() {
  94. names = append(names, value.GetName())
  95. }
  96. return names
  97. }
  98. func listEnumNumbers(enum *descriptor.Enum) (numbers []string) {
  99. for _, value := range enum.GetValue() {
  100. numbers = append(numbers, strconv.Itoa(int(value.GetNumber())))
  101. }
  102. return
  103. }
  104. func getEnumDefault(enum *descriptor.Enum) string {
  105. for _, value := range enum.GetValue() {
  106. if value.GetNumber() == 0 {
  107. return value.GetName()
  108. }
  109. }
  110. return ""
  111. }
  112. // messageToQueryParameters converts a message to a list of OpenAPI query parameters.
  113. func messageToQueryParameters(message *descriptor.Message, reg *descriptor.Registry, pathParams []descriptor.Parameter, body *descriptor.Body) (params []openapiParameterObject, err error) {
  114. for _, field := range message.Fields {
  115. p, err := queryParams(message, field, "", reg, pathParams, body, reg.GetRecursiveDepth())
  116. if err != nil {
  117. return nil, err
  118. }
  119. params = append(params, p...)
  120. }
  121. return params, nil
  122. }
  123. // queryParams converts a field to a list of OpenAPI query parameters recursively through the use of nestedQueryParams.
  124. func queryParams(message *descriptor.Message, field *descriptor.Field, prefix string, reg *descriptor.Registry, pathParams []descriptor.Parameter, body *descriptor.Body, recursiveCount int) (params []openapiParameterObject, err error) {
  125. return nestedQueryParams(message, field, prefix, reg, pathParams, body, newCycleChecker(recursiveCount))
  126. }
  127. type cycleChecker struct {
  128. m map[string]int
  129. count int
  130. }
  131. func newCycleChecker(recursive int) *cycleChecker {
  132. return &cycleChecker{
  133. m: make(map[string]int),
  134. count: recursive,
  135. }
  136. }
  137. // Check returns whether name is still within recursion
  138. // toleration
  139. func (c *cycleChecker) Check(name string) bool {
  140. count, ok := c.m[name]
  141. count = count + 1
  142. isCycle := count > c.count
  143. if isCycle {
  144. return false
  145. }
  146. // provision map entry if not available
  147. if !ok {
  148. c.m[name] = 1
  149. return true
  150. }
  151. c.m[name] = count
  152. return true
  153. }
  154. func (c *cycleChecker) Branch() *cycleChecker {
  155. copy := &cycleChecker{
  156. count: c.count,
  157. m: map[string]int{},
  158. }
  159. for k, v := range c.m {
  160. copy.m[k] = v
  161. }
  162. return copy
  163. }
  164. // nestedQueryParams converts a field to a list of OpenAPI query parameters recursively.
  165. // This function is a helper function for queryParams, that keeps track of cyclical message references
  166. //
  167. // through the use of
  168. // touched map[string]int
  169. //
  170. // If a cycle is discovered, an error is returned, as cyclical data structures are dangerous
  171. //
  172. // in query parameters.
  173. func nestedQueryParams(message *descriptor.Message, field *descriptor.Field, prefix string, reg *descriptor.Registry, pathParams []descriptor.Parameter, body *descriptor.Body, cycle *cycleChecker) (params []openapiParameterObject, err error) {
  174. // make sure the parameter is not already listed as a path parameter
  175. for _, pathParam := range pathParams {
  176. if pathParam.Target == field {
  177. return nil, nil
  178. }
  179. }
  180. // make sure the parameter is not already listed as a body parameter
  181. if body != nil {
  182. if body.FieldPath == nil {
  183. return nil, nil
  184. }
  185. for _, fieldPath := range body.FieldPath {
  186. if fieldPath.Target == field {
  187. return nil, nil
  188. }
  189. }
  190. }
  191. schema := schemaOfField(field, reg, nil)
  192. fieldType := field.GetTypeName()
  193. if message.File != nil {
  194. comments := fieldProtoComments(reg, message, field)
  195. if err := updateOpenAPIDataFromComments(reg, &schema, message, comments, false); err != nil {
  196. return nil, err
  197. }
  198. }
  199. isEnum := field.GetType() == descriptorpb.FieldDescriptorProto_TYPE_ENUM
  200. items := schema.Items
  201. if schema.Type != "" || isEnum {
  202. if schema.Type == "object" {
  203. return nil, nil // TODO: currently, mapping object in query parameter is not supported
  204. }
  205. if items != nil && (items.Type == "" || items.Type == "object") && !isEnum {
  206. return nil, nil // TODO: currently, mapping object in query parameter is not supported
  207. }
  208. desc := schema.Description
  209. if schema.Title != "" { // merge title because title of parameter object will be ignored
  210. desc = strings.TrimSpace(schema.Title + ". " + schema.Description)
  211. }
  212. // verify if the field is required
  213. required := false
  214. for _, fieldName := range schema.Required {
  215. if fieldName == field.GetName() {
  216. required = true
  217. break
  218. }
  219. }
  220. param := openapiParameterObject{
  221. Description: desc,
  222. In: "query",
  223. Default: schema.Default,
  224. Type: schema.Type,
  225. Items: schema.Items,
  226. Format: schema.Format,
  227. Required: required,
  228. }
  229. if param.Type == "array" {
  230. param.CollectionFormat = "multi"
  231. }
  232. param.Name = prefix + reg.FieldName(field)
  233. if isEnum {
  234. enum, err := reg.LookupEnum("", fieldType)
  235. if err != nil {
  236. return nil, fmt.Errorf("unknown enum type %s", fieldType)
  237. }
  238. if items != nil { // array
  239. param.Items = &openapiItemsObject{
  240. Type: "string",
  241. Enum: listEnumNames(enum),
  242. }
  243. if reg.GetEnumsAsInts() {
  244. param.Items.Type = "integer"
  245. param.Items.Enum = listEnumNumbers(enum)
  246. }
  247. } else {
  248. param.Type = "string"
  249. param.Enum = listEnumNames(enum)
  250. param.Default = getEnumDefault(enum)
  251. if reg.GetEnumsAsInts() {
  252. param.Type = "integer"
  253. param.Enum = listEnumNumbers(enum)
  254. param.Default = "0"
  255. }
  256. }
  257. valueComments := enumValueProtoComments(reg, enum)
  258. if valueComments != "" {
  259. param.Description = strings.TrimLeft(param.Description+"\n\n "+valueComments, "\n")
  260. }
  261. }
  262. return []openapiParameterObject{param}, nil
  263. }
  264. // nested type, recurse
  265. msg, err := reg.LookupMsg("", fieldType)
  266. if err != nil {
  267. return nil, fmt.Errorf("unknown message type %s", fieldType)
  268. }
  269. // Check for cyclical message reference:
  270. isOK := cycle.Check(*msg.Name)
  271. if !isOK {
  272. return nil, fmt.Errorf("exceeded recursive count (%d) for query parameter %q", cycle.count, fieldType)
  273. }
  274. // Construct a new map with the message name so a cycle further down the recursive path can be detected.
  275. // Do not keep anything in the original touched reference and do not pass that reference along. This will
  276. // prevent clobbering adjacent records while recursing.
  277. touchedOut := cycle.Branch()
  278. for _, nestedField := range msg.Fields {
  279. fieldName := reg.FieldName(field)
  280. p, err := nestedQueryParams(msg, nestedField, prefix+fieldName+".", reg, pathParams, body, touchedOut)
  281. if err != nil {
  282. return nil, err
  283. }
  284. params = append(params, p...)
  285. }
  286. return params, nil
  287. }
  288. // findServicesMessagesAndEnumerations discovers all messages and enums defined in the RPC methods of the service.
  289. func findServicesMessagesAndEnumerations(s []*descriptor.Service, reg *descriptor.Registry, m messageMap, ms messageMap, e enumMap, refs refMap) {
  290. for _, svc := range s {
  291. for _, meth := range svc.Methods {
  292. // Request may be fully included in query
  293. {
  294. swgReqName, ok := fullyQualifiedNameToOpenAPIName(meth.RequestType.FQMN(), reg)
  295. if !ok {
  296. glog.Errorf("couldn't resolve OpenAPI name for FQMN '%v'", meth.RequestType.FQMN())
  297. continue
  298. }
  299. if _, ok := refs[fmt.Sprintf("#/definitions/%s", swgReqName)]; ok {
  300. if !skipRenderingRef(meth.RequestType.FQMN()) {
  301. m[swgReqName] = meth.RequestType
  302. }
  303. }
  304. }
  305. swgRspName, ok := fullyQualifiedNameToOpenAPIName(meth.ResponseType.FQMN(), reg)
  306. if !ok && !skipRenderingRef(meth.ResponseType.FQMN()) {
  307. glog.Errorf("couldn't resolve OpenAPI name for FQMN '%v'", meth.ResponseType.FQMN())
  308. continue
  309. }
  310. findNestedMessagesAndEnumerations(meth.RequestType, reg, m, e)
  311. if !skipRenderingRef(meth.ResponseType.FQMN()) {
  312. m[swgRspName] = meth.ResponseType
  313. }
  314. findNestedMessagesAndEnumerations(meth.ResponseType, reg, m, e)
  315. }
  316. }
  317. }
  318. // findNestedMessagesAndEnumerations those can be generated by the services.
  319. func findNestedMessagesAndEnumerations(message *descriptor.Message, reg *descriptor.Registry, m messageMap, e enumMap) {
  320. // Iterate over all the fields that
  321. for _, t := range message.Fields {
  322. fieldType := t.GetTypeName()
  323. // If the type is an empty string then it is a proto primitive
  324. if fieldType != "" {
  325. if _, ok := m[fieldType]; !ok {
  326. msg, err := reg.LookupMsg("", fieldType)
  327. if err != nil {
  328. enum, err := reg.LookupEnum("", fieldType)
  329. if err != nil {
  330. panic(err)
  331. }
  332. e[fieldType] = enum
  333. continue
  334. }
  335. m[fieldType] = msg
  336. findNestedMessagesAndEnumerations(msg, reg, m, e)
  337. }
  338. }
  339. }
  340. }
  341. func skipRenderingRef(refName string) bool {
  342. _, ok := wktSchemas[refName]
  343. return ok
  344. }
  345. func renderMessageAsDefinition(msg *descriptor.Message, reg *descriptor.Registry, customRefs refMap, excludeFields []*descriptor.Field) openapiSchemaObject {
  346. schema := openapiSchemaObject{
  347. schemaCore: schemaCore{
  348. Type: "object",
  349. },
  350. }
  351. msgComments := protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index))
  352. if err := updateOpenAPIDataFromComments(reg, &schema, msg, msgComments, false); err != nil {
  353. panic(err)
  354. }
  355. opts, err := getMessageOpenAPIOption(reg, msg)
  356. if err != nil {
  357. panic(err)
  358. }
  359. if opts != nil {
  360. protoSchema := openapiSchemaFromProtoSchema(opts, reg, customRefs, msg)
  361. // Warning: Make sure not to overwrite any fields already set on the schema type.
  362. schema.ExternalDocs = protoSchema.ExternalDocs
  363. schema.ReadOnly = protoSchema.ReadOnly
  364. schema.MultipleOf = protoSchema.MultipleOf
  365. schema.Maximum = protoSchema.Maximum
  366. schema.ExclusiveMaximum = protoSchema.ExclusiveMaximum
  367. schema.Minimum = protoSchema.Minimum
  368. schema.ExclusiveMinimum = protoSchema.ExclusiveMinimum
  369. schema.MaxLength = protoSchema.MaxLength
  370. schema.MinLength = protoSchema.MinLength
  371. schema.Pattern = protoSchema.Pattern
  372. schema.Default = protoSchema.Default
  373. schema.MaxItems = protoSchema.MaxItems
  374. schema.MinItems = protoSchema.MinItems
  375. schema.UniqueItems = protoSchema.UniqueItems
  376. schema.MaxProperties = protoSchema.MaxProperties
  377. schema.MinProperties = protoSchema.MinProperties
  378. schema.Required = protoSchema.Required
  379. schema.XNullable = protoSchema.XNullable
  380. if protoSchema.schemaCore.Type != "" || protoSchema.schemaCore.Ref != "" {
  381. schema.schemaCore = protoSchema.schemaCore
  382. }
  383. if protoSchema.Title != "" {
  384. schema.Title = protoSchema.Title
  385. }
  386. if protoSchema.Description != "" {
  387. schema.Description = protoSchema.Description
  388. }
  389. if protoSchema.Example != nil {
  390. schema.Example = protoSchema.Example
  391. }
  392. }
  393. schema.Required = filterOutExcludedFields(schema.Required, excludeFields, reg)
  394. for _, f := range msg.Fields {
  395. if shouldExcludeField(reg.FieldName(f), excludeFields, reg) {
  396. continue
  397. }
  398. fieldValue := schemaOfField(f, reg, customRefs)
  399. comments := fieldProtoComments(reg, msg, f)
  400. if err := updateOpenAPIDataFromComments(reg, &fieldValue, f, comments, false); err != nil {
  401. panic(err)
  402. }
  403. if requiredIdx := find(schema.Required, *f.Name); requiredIdx != -1 && reg.GetUseJSONNamesForFields() {
  404. schema.Required[requiredIdx] = f.GetJsonName()
  405. }
  406. if fieldValue.Required != nil {
  407. for _, req := range fieldValue.Required {
  408. if reg.GetUseJSONNamesForFields() {
  409. schema.Required = append(schema.Required, f.GetJsonName())
  410. } else {
  411. schema.Required = append(schema.Required, req)
  412. }
  413. }
  414. }
  415. kv := keyVal{Value: fieldValue}
  416. kv.Key = reg.FieldName(f)
  417. if schema.Properties == nil {
  418. schema.Properties = &openapiSchemaObjectProperties{}
  419. }
  420. *schema.Properties = append(*schema.Properties, kv)
  421. }
  422. return schema
  423. }
  424. func renderMessagesAsDefinition(messages messageMap, d openapiDefinitionsObject, reg *descriptor.Registry, customRefs refMap, excludeFields []*descriptor.Field) {
  425. for name, msg := range messages {
  426. swgName, ok := fullyQualifiedNameToOpenAPIName(msg.FQMN(), reg)
  427. if !ok {
  428. panic(fmt.Sprintf("can't resolve OpenAPI name from '%v'", msg.FQMN()))
  429. }
  430. if skipRenderingRef(name) {
  431. continue
  432. }
  433. if opt := msg.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
  434. continue
  435. }
  436. d[swgName] = renderMessageAsDefinition(msg, reg, customRefs, excludeFields)
  437. }
  438. }
  439. func shouldExcludeField(name string, excluded []*descriptor.Field, reg *descriptor.Registry) bool {
  440. for _, f := range excluded {
  441. if name == reg.FieldName(f) {
  442. return true
  443. }
  444. }
  445. return false
  446. }
  447. func filterOutExcludedFields(fields []string, excluded []*descriptor.Field, reg *descriptor.Registry) []string {
  448. var filtered []string
  449. for _, f := range fields {
  450. if !shouldExcludeField(f, excluded, reg) {
  451. filtered = append(filtered, f)
  452. }
  453. }
  454. return filtered
  455. }
  456. // schemaOfField returns a OpenAPI Schema Object for a protobuf field.
  457. func schemaOfField(f *descriptor.Field, reg *descriptor.Registry, refs refMap) openapiSchemaObject {
  458. const (
  459. singular = 0
  460. array = 1
  461. object = 2
  462. )
  463. var (
  464. core schemaCore
  465. aggregate int
  466. )
  467. fd := f.FieldDescriptorProto
  468. if m, err := reg.LookupMsg("", f.GetTypeName()); err == nil {
  469. if opt := m.GetOptions(); opt != nil && opt.MapEntry != nil && *opt.MapEntry {
  470. fd = m.GetField()[1]
  471. aggregate = object
  472. }
  473. }
  474. if fd.GetLabel() == descriptorpb.FieldDescriptorProto_LABEL_REPEATED {
  475. aggregate = array
  476. }
  477. var props *openapiSchemaObjectProperties
  478. switch ft := fd.GetType(); ft {
  479. case descriptorpb.FieldDescriptorProto_TYPE_ENUM, descriptorpb.FieldDescriptorProto_TYPE_MESSAGE, descriptorpb.FieldDescriptorProto_TYPE_GROUP:
  480. if wktSchema, ok := wktSchemas[fd.GetTypeName()]; ok {
  481. core = wktSchema
  482. if fd.GetTypeName() == ".google.protobuf.Empty" {
  483. props = &openapiSchemaObjectProperties{}
  484. }
  485. } else {
  486. swgRef, ok := fullyQualifiedNameToOpenAPIName(fd.GetTypeName(), reg)
  487. if !ok {
  488. panic(fmt.Sprintf("can't resolve OpenAPI ref from typename '%v'", fd.GetTypeName()))
  489. }
  490. core = schemaCore{
  491. Ref: "#/definitions/" + swgRef,
  492. }
  493. if refs != nil {
  494. refs[fd.GetTypeName()] = struct{}{}
  495. }
  496. }
  497. default:
  498. ftype, format, ok := primitiveSchema(ft)
  499. if ok {
  500. core = schemaCore{Type: ftype, Format: format}
  501. } else {
  502. core = schemaCore{Type: ft.String(), Format: "UNKNOWN"}
  503. }
  504. }
  505. ret := openapiSchemaObject{}
  506. switch aggregate {
  507. case array:
  508. ret = openapiSchemaObject{
  509. schemaCore: schemaCore{
  510. Type: "array",
  511. Items: (*openapiItemsObject)(&core),
  512. },
  513. }
  514. case object:
  515. ret = openapiSchemaObject{
  516. schemaCore: schemaCore{
  517. Type: "object",
  518. },
  519. AdditionalProperties: &openapiSchemaObject{Properties: props, schemaCore: core},
  520. }
  521. default:
  522. ret = openapiSchemaObject{
  523. schemaCore: core,
  524. Properties: props,
  525. }
  526. }
  527. if j, err := getFieldOpenAPIOption(reg, f); err == nil {
  528. updateswaggerObjectFromJSONSchema(&ret, j, reg, f)
  529. }
  530. if j, err := getFieldBehaviorOption(reg, f); err == nil {
  531. updateSwaggerObjectFromFieldBehavior(&ret, j, f)
  532. }
  533. if reg.GetProto3OptionalNullable() && f.GetProto3Optional() {
  534. ret.XNullable = true
  535. }
  536. return ret
  537. }
  538. // primitiveSchema returns a pair of "Type" and "Format" in JSON Schema for
  539. // the given primitive field type.
  540. // The last return parameter is true iff the field type is actually primitive.
  541. func primitiveSchema(t descriptorpb.FieldDescriptorProto_Type) (ftype, format string, ok bool) {
  542. switch t {
  543. case descriptorpb.FieldDescriptorProto_TYPE_DOUBLE:
  544. return "number", "double", true
  545. case descriptorpb.FieldDescriptorProto_TYPE_FLOAT:
  546. return "number", "float", true
  547. case descriptorpb.FieldDescriptorProto_TYPE_INT64:
  548. return "integer", "int64", true
  549. case descriptorpb.FieldDescriptorProto_TYPE_UINT64:
  550. // 64bit integer types are marshaled as string in the default JSONPb marshaler.
  551. // TODO(yugui) Add an option to declare 64bit integers as int64.
  552. //
  553. // NOTE: uint64 is not a predefined format of integer type in OpenAPI spec.
  554. // So we cannot expect that uint64 is commonly supported by OpenAPI processor.
  555. return "integer", "uint64", true
  556. case descriptorpb.FieldDescriptorProto_TYPE_INT32:
  557. return "integer", "int32", true
  558. case descriptorpb.FieldDescriptorProto_TYPE_FIXED64:
  559. // Ditto.
  560. return "integer", "uint64", true
  561. case descriptorpb.FieldDescriptorProto_TYPE_FIXED32:
  562. // Ditto.
  563. return "integer", "int64", true
  564. case descriptorpb.FieldDescriptorProto_TYPE_BOOL:
  565. // NOTE: in OpenAPI specification, format should be empty on boolean type
  566. return "boolean", "", true
  567. case descriptorpb.FieldDescriptorProto_TYPE_STRING:
  568. // NOTE: in OpenAPI specification, format should be empty on string type
  569. return "string", "", true
  570. case descriptorpb.FieldDescriptorProto_TYPE_BYTES:
  571. return "string", "byte", true
  572. case descriptorpb.FieldDescriptorProto_TYPE_UINT32:
  573. // Ditto.
  574. return "integer", "int64", true
  575. case descriptorpb.FieldDescriptorProto_TYPE_SFIXED32:
  576. return "integer", "int32", true
  577. case descriptorpb.FieldDescriptorProto_TYPE_SFIXED64:
  578. return "integer", "int64", true
  579. case descriptorpb.FieldDescriptorProto_TYPE_SINT32:
  580. return "integer", "int32", true
  581. case descriptorpb.FieldDescriptorProto_TYPE_SINT64:
  582. return "integer", "int64", true
  583. default:
  584. return "", "", false
  585. }
  586. }
  587. // renderEnumerationsAsDefinition inserts enums into the definitions object.
  588. func renderEnumerationsAsDefinition(enums enumMap, d openapiDefinitionsObject, reg *descriptor.Registry) {
  589. for _, enum := range enums {
  590. swgName, ok := fullyQualifiedNameToOpenAPIName(enum.FQEN(), reg)
  591. if !ok {
  592. panic(fmt.Sprintf("can't resolve OpenAPI name from FQEN '%v'", enum.FQEN()))
  593. }
  594. enumComments := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index))
  595. // it may be necessary to sort the result of the GetValue function.
  596. enumNames := listEnumNames(enum)
  597. defaultValue := getEnumDefault(enum)
  598. valueComments := enumValueProtoComments(reg, enum)
  599. if valueComments != "" {
  600. enumComments = strings.TrimLeft(enumComments+"\n\n "+valueComments, "\n")
  601. }
  602. enumSchemaObject := openapiSchemaObject{
  603. schemaCore: schemaCore{
  604. Type: "string",
  605. Enum: enumNames,
  606. Default: defaultValue,
  607. },
  608. }
  609. if reg.GetEnumsAsInts() {
  610. enumSchemaObject.Type = "integer"
  611. enumSchemaObject.Format = "int32"
  612. enumSchemaObject.Default = "0"
  613. enumSchemaObject.Enum = listEnumNumbers(enum)
  614. }
  615. if err := updateOpenAPIDataFromComments(reg, &enumSchemaObject, enum, enumComments, false); err != nil {
  616. panic(err)
  617. }
  618. d[swgName] = enumSchemaObject
  619. }
  620. }
  621. // Take in a FQMN or FQEN and return a OpenAPI safe version of the FQMN and
  622. // a boolean indicating if FQMN was properly resolved.
  623. func fullyQualifiedNameToOpenAPIName(fqn string, reg *descriptor.Registry) (string, bool) {
  624. registriesSeenMutex.Lock()
  625. defer registriesSeenMutex.Unlock()
  626. if mapping, present := registriesSeen[reg]; present {
  627. ret, ok := mapping[fqn]
  628. return ret, ok
  629. }
  630. mapping := resolveFullyQualifiedNameToOpenAPINames(append(reg.GetAllFQMNs(), reg.GetAllFQENs()...), reg.GetUseFQNForOpenAPIName())
  631. registriesSeen[reg] = mapping
  632. ret, ok := mapping[fqn]
  633. return ret, ok
  634. }
  635. // Lookup message type by location.name and return a openapiv2-safe version
  636. // of its FQMN.
  637. func lookupMsgAndOpenAPIName(location, name string, reg *descriptor.Registry) (*descriptor.Message, string, error) {
  638. msg, err := reg.LookupMsg(location, name)
  639. if err != nil {
  640. return nil, "", err
  641. }
  642. swgName, ok := fullyQualifiedNameToOpenAPIName(msg.FQMN(), reg)
  643. if !ok {
  644. return nil, "", fmt.Errorf("can't map OpenAPI name from FQMN '%v'", msg.FQMN())
  645. }
  646. return msg, swgName, nil
  647. }
  648. // registriesSeen is used to memoise calls to resolveFullyQualifiedNameToOpenAPINames so
  649. // we don't repeat it unnecessarily, since it can take some time.
  650. var registriesSeen = map[*descriptor.Registry]map[string]string{}
  651. var registriesSeenMutex sync.Mutex
  652. // Take the names of every proto and "uniq-ify" them. The idea is to produce a
  653. // set of names that meet a couple of conditions. They must be stable, they
  654. // must be unique, and they must be shorter than the FQN.
  655. //
  656. // This likely could be made better. This will always generate the same names
  657. // but may not always produce optimal names. This is a reasonably close
  658. // approximation of what they should look like in most cases.
  659. func resolveFullyQualifiedNameToOpenAPINames(messages []string, useFQNForOpenAPIName bool) map[string]string {
  660. packagesByDepth := make(map[int][][]string)
  661. uniqueNames := make(map[string]string)
  662. hierarchy := func(pkg string) []string {
  663. return strings.Split(pkg, ".")
  664. }
  665. for _, p := range messages {
  666. h := hierarchy(p)
  667. for depth := range h {
  668. if _, ok := packagesByDepth[depth]; !ok {
  669. packagesByDepth[depth] = make([][]string, 0)
  670. }
  671. packagesByDepth[depth] = append(packagesByDepth[depth], h[len(h)-depth:])
  672. }
  673. }
  674. count := func(list [][]string, item []string) int {
  675. i := 0
  676. for _, element := range list {
  677. if reflect.DeepEqual(element, item) {
  678. i++
  679. }
  680. }
  681. return i
  682. }
  683. for _, p := range messages {
  684. if useFQNForOpenAPIName {
  685. // strip leading dot from proto fqn
  686. uniqueNames[p] = p[1:]
  687. } else {
  688. h := hierarchy(p)
  689. for depth := 0; depth < len(h); depth++ {
  690. if count(packagesByDepth[depth], h[len(h)-depth:]) == 1 {
  691. uniqueNames[p] = strings.Join(h[len(h)-depth-1:], "")
  692. break
  693. }
  694. if depth == len(h)-1 {
  695. uniqueNames[p] = strings.Join(h, "")
  696. }
  697. }
  698. }
  699. }
  700. return uniqueNames
  701. }
  702. var canRegexp = regexp.MustCompile("{([a-zA-Z][a-zA-Z0-9_.]*).*}")
  703. // OpenAPI expects paths of the form /path/{string_value} but gRPC-Gateway paths are expected to be of the form /path/{string_value=strprefix/*}. This should reformat it correctly.
  704. func templateToOpenAPIPath(path string, reg *descriptor.Registry, fields []*descriptor.Field, msgs []*descriptor.Message) string {
  705. // It seems like the right thing to do here is to just use
  706. // strings.Split(path, "/") but that breaks badly when you hit a url like
  707. // /{my_field=prefix/*}/ and end up with 2 sections representing my_field.
  708. // Instead do the right thing and write a small pushdown (counter) automata
  709. // for it.
  710. var parts []string
  711. depth := 0
  712. buffer := ""
  713. jsonBuffer := ""
  714. for _, char := range path {
  715. switch char {
  716. case '{':
  717. // Push on the stack
  718. depth++
  719. buffer += string(char)
  720. jsonBuffer = ""
  721. jsonBuffer += string(char)
  722. case '}':
  723. if depth == 0 {
  724. panic("Encountered } without matching { before it.")
  725. }
  726. // Pop from the stack
  727. depth--
  728. buffer += string(char)
  729. if reg.GetUseJSONNamesForFields() &&
  730. len(jsonBuffer) > 1 {
  731. jsonSnakeCaseName := string(jsonBuffer[1:])
  732. jsonCamelCaseName := string(lowerCamelCase(jsonSnakeCaseName, fields, msgs))
  733. prev := string(buffer[:len(buffer)-len(jsonSnakeCaseName)-2])
  734. buffer = strings.Join([]string{prev, "{", jsonCamelCaseName, "}"}, "")
  735. jsonBuffer = ""
  736. }
  737. case '/':
  738. if depth == 0 {
  739. parts = append(parts, buffer)
  740. buffer = ""
  741. // Since the stack was empty when we hit the '/' we are done with this
  742. // section.
  743. continue
  744. }
  745. buffer += string(char)
  746. jsonBuffer += string(char)
  747. default:
  748. buffer += string(char)
  749. jsonBuffer += string(char)
  750. }
  751. }
  752. // Now append the last element to parts
  753. parts = append(parts, buffer)
  754. // Parts is now an array of segments of the path. Interestingly, since the
  755. // syntax for this subsection CAN be handled by a regexp since it has no
  756. // memory.
  757. for index, part := range parts {
  758. // If part is a resource name such as "parent", "name", "user.name", the format info must be retained.
  759. prefix := canRegexp.ReplaceAllString(part, "$1")
  760. if isResourceName(prefix) {
  761. continue
  762. }
  763. parts[index] = canRegexp.ReplaceAllString(part, "{$1}")
  764. }
  765. return strings.Join(parts, "/")
  766. }
  767. func isResourceName(prefix string) bool {
  768. words := strings.Split(prefix, ".")
  769. l := len(words)
  770. field := words[l-1]
  771. words = strings.Split(field, ":")
  772. field = words[0]
  773. return field == "parent" || field == "name"
  774. }
  775. func renderServiceTags(services []*descriptor.Service) []openapiTagObject {
  776. var tags []openapiTagObject
  777. for _, svc := range services {
  778. tag := openapiTagObject{
  779. Name: *svc.Name,
  780. }
  781. if proto.HasExtension(svc.Options, openapi_options.E_Openapiv2Tag) {
  782. ext := proto.GetExtension(svc.Options, openapi_options.E_Openapiv2Tag)
  783. opts, ok := ext.(*openapi_options.Tag)
  784. if !ok {
  785. glog.Errorf("extension is %T; want an OpenAPI Tag object", ext)
  786. return nil
  787. }
  788. tag.Description = opts.Description
  789. if opts.ExternalDocs != nil {
  790. tag.ExternalDocs = &openapiExternalDocumentationObject{
  791. Description: opts.ExternalDocs.Description,
  792. URL: opts.ExternalDocs.Url,
  793. }
  794. }
  795. }
  796. tags = append(tags, tag)
  797. }
  798. return tags
  799. }
  800. func renderServices(services []*descriptor.Service, paths openapiPathsObject, reg *descriptor.Registry, requestResponseRefs, customRefs refMap, msgs []*descriptor.Message) error {
  801. // Correctness of svcIdx and methIdx depends on 'services' containing the services in the same order as the 'file.Service' array.
  802. svcBaseIdx := 0
  803. var lastFile *descriptor.File = nil
  804. for svcIdx, svc := range services {
  805. if svc.File != lastFile {
  806. lastFile = svc.File
  807. svcBaseIdx = svcIdx
  808. }
  809. for methIdx, meth := range svc.Methods {
  810. for bIdx, b := range meth.Bindings {
  811. // Iterate over all the OpenAPI parameters
  812. parameters := openapiParametersObject{}
  813. for _, parameter := range b.PathParams {
  814. var paramType, paramFormat, desc, collectionFormat, defaultValue string
  815. var enumNames []string
  816. var items *openapiItemsObject
  817. var minItems *int
  818. switch pt := parameter.Target.GetType(); pt {
  819. case descriptorpb.FieldDescriptorProto_TYPE_GROUP, descriptorpb.FieldDescriptorProto_TYPE_MESSAGE:
  820. if descriptor.IsWellKnownType(parameter.Target.GetTypeName()) {
  821. if parameter.IsRepeated() {
  822. return fmt.Errorf("only primitive and enum types are allowed in repeated path parameters")
  823. }
  824. schema := schemaOfField(parameter.Target, reg, customRefs)
  825. paramType = schema.Type
  826. paramFormat = schema.Format
  827. desc = schema.Description
  828. defaultValue = schema.Default
  829. } else {
  830. return fmt.Errorf("only primitive and well-known types are allowed in path parameters")
  831. }
  832. case descriptorpb.FieldDescriptorProto_TYPE_ENUM:
  833. enum, err := reg.LookupEnum("", parameter.Target.GetTypeName())
  834. if err != nil {
  835. return err
  836. }
  837. paramType = "string"
  838. paramFormat = ""
  839. enumNames = listEnumNames(enum)
  840. if reg.GetEnumsAsInts() {
  841. paramType = "integer"
  842. paramFormat = ""
  843. enumNames = listEnumNumbers(enum)
  844. }
  845. schema := schemaOfField(parameter.Target, reg, customRefs)
  846. desc = schema.Description
  847. defaultValue = schema.Default
  848. default:
  849. var ok bool
  850. paramType, paramFormat, ok = primitiveSchema(pt)
  851. if !ok {
  852. return fmt.Errorf("unknown field type %v", pt)
  853. }
  854. schema := schemaOfField(parameter.Target, reg, customRefs)
  855. desc = schema.Description
  856. defaultValue = schema.Default
  857. }
  858. if parameter.IsRepeated() {
  859. core := schemaCore{Type: paramType, Format: paramFormat}
  860. if parameter.IsEnum() {
  861. var s []string
  862. core.Enum = enumNames
  863. enumNames = s
  864. }
  865. items = (*openapiItemsObject)(&core)
  866. paramType = "array"
  867. paramFormat = ""
  868. collectionFormat = reg.GetRepeatedPathParamSeparatorName()
  869. minItems = new(int)
  870. *minItems = 1
  871. }
  872. if desc == "" {
  873. desc = fieldProtoComments(reg, parameter.Target.Message, parameter.Target)
  874. }
  875. parameterString := parameter.String()
  876. if reg.GetUseJSONNamesForFields() {
  877. parameterString = lowerCamelCase(parameterString, meth.RequestType.Fields, msgs)
  878. }
  879. parameters = append(parameters, openapiParameterObject{
  880. Name: parameterString,
  881. Description: desc,
  882. In: "path",
  883. Required: true,
  884. Default: defaultValue,
  885. // Parameters in gRPC-Gateway can only be strings?
  886. Type: paramType,
  887. Format: paramFormat,
  888. Enum: enumNames,
  889. Items: items,
  890. CollectionFormat: collectionFormat,
  891. MinItems: minItems,
  892. })
  893. }
  894. // Now check if there is a body parameter
  895. if b.Body != nil {
  896. var schema openapiSchemaObject
  897. desc := ""
  898. if len(b.Body.FieldPath) == 0 {
  899. schema = openapiSchemaObject{
  900. schemaCore: schemaCore{},
  901. }
  902. wknSchemaCore, isWkn := wktSchemas[meth.RequestType.FQMN()]
  903. if !isWkn {
  904. var bodyExcludedFields []*descriptor.Field
  905. if len(b.PathParams) != 0 {
  906. for _, p := range b.PathParams {
  907. // We only support excluding top-level fields captured by path parameters.
  908. if len(p.FieldPath) == 1 {
  909. bodyExcludedFields = append(bodyExcludedFields, p.FieldPath[0].Target)
  910. }
  911. }
  912. }
  913. if len(bodyExcludedFields) != 0 {
  914. schema = renderMessageAsDefinition(meth.RequestType, reg, customRefs, bodyExcludedFields)
  915. if schema.Properties == nil || len(*schema.Properties) == 0 {
  916. glog.Errorf("created a body with 0 properties in the message, this might be unintended: %s", *meth.RequestType)
  917. }
  918. } else {
  919. err := schema.setRefFromFQN(meth.RequestType.FQMN(), reg)
  920. if err != nil {
  921. return err
  922. }
  923. }
  924. } else {
  925. schema.schemaCore = wknSchemaCore
  926. // Special workaround for Empty: it's well-known type but wknSchemas only returns schema.schemaCore; but we need to set schema.Properties which is a level higher.
  927. if meth.RequestType.FQMN() == ".google.protobuf.Empty" {
  928. schema.Properties = &openapiSchemaObjectProperties{}
  929. }
  930. }
  931. } else {
  932. lastField := b.Body.FieldPath[len(b.Body.FieldPath)-1]
  933. schema = schemaOfField(lastField.Target, reg, customRefs)
  934. if schema.Description != "" {
  935. desc = schema.Description
  936. } else {
  937. desc = fieldProtoComments(reg, lastField.Target.Message, lastField.Target)
  938. }
  939. }
  940. if meth.GetClientStreaming() {
  941. desc += " (streaming inputs)"
  942. }
  943. parameters = append(parameters, openapiParameterObject{
  944. Name: "body",
  945. Description: desc,
  946. In: "body",
  947. Required: true,
  948. Schema: &schema,
  949. })
  950. // add the parameters to the query string
  951. queryParams, err := messageToQueryParameters(meth.RequestType, reg, b.PathParams, b.Body)
  952. if err != nil {
  953. return err
  954. }
  955. parameters = append(parameters, queryParams...)
  956. } else if b.HTTPMethod == "GET" || b.HTTPMethod == "DELETE" {
  957. // add the parameters to the query string
  958. queryParams, err := messageToQueryParameters(meth.RequestType, reg, b.PathParams, b.Body)
  959. if err != nil {
  960. return err
  961. }
  962. parameters = append(parameters, queryParams...)
  963. }
  964. pathItemObject, ok := paths[templateToOpenAPIPath(b.PathTmpl.Template, reg, meth.RequestType.Fields, msgs)]
  965. if !ok {
  966. pathItemObject = openapiPathItemObject{}
  967. }
  968. methProtoPath := protoPathIndex(reflect.TypeOf((*descriptorpb.ServiceDescriptorProto)(nil)), "Method")
  969. desc := "A successful response."
  970. var responseSchema openapiSchemaObject
  971. if b.ResponseBody == nil || len(b.ResponseBody.FieldPath) == 0 {
  972. responseSchema = openapiSchemaObject{
  973. schemaCore: schemaCore{},
  974. }
  975. // Don't link to a full definition for
  976. // empty; it's overly verbose.
  977. // schema.Properties{} renders it as
  978. // well, without a definition
  979. wknSchemaCore, isWkn := wktSchemas[meth.ResponseType.FQMN()]
  980. if !isWkn {
  981. err := responseSchema.setRefFromFQN(meth.ResponseType.FQMN(), reg)
  982. if err != nil {
  983. return err
  984. }
  985. } else {
  986. responseSchema.schemaCore = wknSchemaCore
  987. // Special workaround for Empty: it's well-known type but wknSchemas only returns schema.schemaCore; but we need to set schema.Properties which is a level higher.
  988. if meth.ResponseType.FQMN() == ".google.protobuf.Empty" {
  989. responseSchema.Properties = &openapiSchemaObjectProperties{}
  990. }
  991. }
  992. } else {
  993. // This is resolving the value of response_body in the google.api.HttpRule
  994. lastField := b.ResponseBody.FieldPath[len(b.ResponseBody.FieldPath)-1]
  995. responseSchema = schemaOfField(lastField.Target, reg, customRefs)
  996. if responseSchema.Description != "" {
  997. desc = responseSchema.Description
  998. } else {
  999. desc = fieldProtoComments(reg, lastField.Target.Message, lastField.Target)
  1000. }
  1001. }
  1002. if meth.GetServerStreaming() {
  1003. desc += "(streaming responses)"
  1004. responseSchema.Type = "object"
  1005. swgRef, _ := fullyQualifiedNameToOpenAPIName(meth.ResponseType.FQMN(), reg)
  1006. responseSchema.Title = "Stream result of " + swgRef
  1007. props := openapiSchemaObjectProperties{
  1008. keyVal{
  1009. Key: "result",
  1010. Value: openapiSchemaObject{
  1011. schemaCore: schemaCore{
  1012. Ref: responseSchema.Ref,
  1013. },
  1014. },
  1015. },
  1016. }
  1017. statusDef, hasStatus := fullyQualifiedNameToOpenAPIName(".google.rpc.Status", reg)
  1018. if hasStatus {
  1019. props = append(props, keyVal{
  1020. Key: "error",
  1021. Value: openapiSchemaObject{
  1022. schemaCore: schemaCore{
  1023. Ref: fmt.Sprintf("#/definitions/%s", statusDef)},
  1024. },
  1025. })
  1026. }
  1027. responseSchema.Properties = &props
  1028. responseSchema.Ref = ""
  1029. }
  1030. tag := svc.GetName()
  1031. if pkg := svc.File.GetPackage(); pkg != "" && reg.IsIncludePackageInTags() {
  1032. tag = pkg + "." + tag
  1033. }
  1034. operationObject := &openapiOperationObject{
  1035. Tags: []string{tag},
  1036. Parameters: parameters,
  1037. Responses: openapiResponsesObject{
  1038. "200": openapiResponseObject{
  1039. Description: desc,
  1040. Schema: responseSchema,
  1041. Headers: openapiHeadersObject{},
  1042. },
  1043. },
  1044. }
  1045. if !reg.GetDisableDefaultErrors() {
  1046. errDef, hasErrDef := fullyQualifiedNameToOpenAPIName(".google.rpc.Status", reg)
  1047. if hasErrDef {
  1048. // https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#responses-object
  1049. operationObject.Responses["default"] = openapiResponseObject{
  1050. Description: "An unexpected error response.",
  1051. Schema: openapiSchemaObject{
  1052. schemaCore: schemaCore{
  1053. Ref: fmt.Sprintf("#/definitions/%s", errDef),
  1054. },
  1055. },
  1056. }
  1057. }
  1058. }
  1059. operationObject.OperationID = fmt.Sprintf("%s_%s", svc.GetName(), meth.GetName())
  1060. if reg.GetSimpleOperationIDs() {
  1061. operationObject.OperationID = meth.GetName()
  1062. }
  1063. if bIdx != 0 {
  1064. // OperationID must be unique in an OpenAPI v2 definition.
  1065. operationObject.OperationID += strconv.Itoa(bIdx + 1)
  1066. }
  1067. // Fill reference map with referenced request messages
  1068. for _, param := range operationObject.Parameters {
  1069. if param.Schema != nil && param.Schema.Ref != "" {
  1070. requestResponseRefs[param.Schema.Ref] = struct{}{}
  1071. }
  1072. }
  1073. methComments := protoComments(reg, svc.File, nil, "Service", int32(svcIdx-svcBaseIdx), methProtoPath, int32(methIdx))
  1074. if err := updateOpenAPIDataFromComments(reg, operationObject, meth, methComments, false); err != nil {
  1075. panic(err)
  1076. }
  1077. opts, err := getMethodOpenAPIOption(reg, meth)
  1078. if opts != nil {
  1079. if err != nil {
  1080. panic(err)
  1081. }
  1082. operationObject.ExternalDocs = protoExternalDocumentationToOpenAPIExternalDocumentation(opts.ExternalDocs, reg, meth)
  1083. // TODO(ivucica): this would be better supported by looking whether the method is deprecated in the proto file
  1084. operationObject.Deprecated = opts.Deprecated
  1085. if opts.Summary != "" {
  1086. operationObject.Summary = opts.Summary
  1087. }
  1088. if opts.Description != "" {
  1089. operationObject.Description = opts.Description
  1090. }
  1091. if len(opts.Tags) > 0 {
  1092. operationObject.Tags = make([]string, len(opts.Tags))
  1093. copy(operationObject.Tags, opts.Tags)
  1094. }
  1095. if opts.OperationId != "" {
  1096. operationObject.OperationID = opts.OperationId
  1097. }
  1098. if opts.Security != nil {
  1099. newSecurity := []openapiSecurityRequirementObject{}
  1100. if operationObject.Security != nil {
  1101. newSecurity = *operationObject.Security
  1102. }
  1103. for _, secReq := range opts.Security {
  1104. newSecReq := openapiSecurityRequirementObject{}
  1105. for secReqKey, secReqValue := range secReq.SecurityRequirement {
  1106. if secReqValue == nil {
  1107. continue
  1108. }
  1109. newSecReqValue := make([]string, len(secReqValue.Scope))
  1110. copy(newSecReqValue, secReqValue.Scope)
  1111. newSecReq[secReqKey] = newSecReqValue
  1112. }
  1113. if len(newSecReq) > 0 {
  1114. newSecurity = append(newSecurity, newSecReq)
  1115. }
  1116. }
  1117. operationObject.Security = &newSecurity
  1118. }
  1119. if opts.Responses != nil {
  1120. for name, resp := range opts.Responses {
  1121. // Merge response data into default response if available.
  1122. respObj := operationObject.Responses[name]
  1123. if resp.Description != "" {
  1124. respObj.Description = resp.Description
  1125. }
  1126. if resp.Schema != nil {
  1127. respObj.Schema = openapiSchemaFromProtoSchema(resp.Schema, reg, customRefs, meth)
  1128. }
  1129. if resp.Examples != nil {
  1130. respObj.Examples = openapiExamplesFromProtoExamples(resp.Examples)
  1131. }
  1132. if resp.Headers != nil {
  1133. hdrs, err := processHeaders(resp.Headers)
  1134. if err != nil {
  1135. return err
  1136. }
  1137. respObj.Headers = hdrs
  1138. }
  1139. if resp.Extensions != nil {
  1140. exts, err := processExtensions(resp.Extensions)
  1141. if err != nil {
  1142. return err
  1143. }
  1144. respObj.extensions = exts
  1145. }
  1146. operationObject.Responses[name] = respObj
  1147. }
  1148. }
  1149. if opts.Extensions != nil {
  1150. exts, err := processExtensions(opts.Extensions)
  1151. if err != nil {
  1152. return err
  1153. }
  1154. operationObject.extensions = exts
  1155. }
  1156. if len(opts.Produces) > 0 {
  1157. operationObject.Produces = make([]string, len(opts.Produces))
  1158. copy(operationObject.Produces, opts.Produces)
  1159. }
  1160. // TODO(ivucica): add remaining fields of operation object
  1161. }
  1162. switch b.HTTPMethod {
  1163. case "DELETE":
  1164. pathItemObject.Delete = operationObject
  1165. case "GET":
  1166. pathItemObject.Get = operationObject
  1167. case "POST":
  1168. pathItemObject.Post = operationObject
  1169. case "PUT":
  1170. pathItemObject.Put = operationObject
  1171. case "PATCH":
  1172. pathItemObject.Patch = operationObject
  1173. }
  1174. paths[templateToOpenAPIPath(b.PathTmpl.Template, reg, meth.RequestType.Fields, msgs)] = pathItemObject
  1175. }
  1176. }
  1177. }
  1178. // Success! return nil on the error object
  1179. return nil
  1180. }
  1181. // This function is called with a param which contains the entire definition of a method.
  1182. func applyTemplate(p param) (*openapiSwaggerObject, error) {
  1183. // Create the basic template object. This is the object that everything is
  1184. // defined off of.
  1185. s := openapiSwaggerObject{
  1186. // OpenAPI 2.0 is the version of this document
  1187. Swagger: "2.0",
  1188. Consumes: []string{"application/json"},
  1189. Produces: []string{"application/json"},
  1190. Paths: make(openapiPathsObject),
  1191. Definitions: make(openapiDefinitionsObject),
  1192. Info: openapiInfoObject{
  1193. Title: *p.File.Name,
  1194. Version: "version not set",
  1195. },
  1196. Schemes: []string{"HTTP", "HTTPS", "WS", "WSS"},
  1197. }
  1198. // Loops through all the services and their exposed GET/POST/PUT/DELETE definitions
  1199. // and create entries for all of them.
  1200. // Also adds custom user specified references to second map.
  1201. requestResponseRefs, customRefs := refMap{}, refMap{}
  1202. if err := renderServices(p.Services, s.Paths, p.reg, requestResponseRefs, customRefs, p.Messages); err != nil {
  1203. panic(err)
  1204. }
  1205. s.Tags = append(s.Tags, renderServiceTags(p.Services)...)
  1206. messages := messageMap{}
  1207. streamingMessages := messageMap{}
  1208. enums := enumMap{}
  1209. if !p.reg.GetDisableDefaultErrors() {
  1210. // Add the error type to the message map
  1211. runtimeError, swgRef, err := lookupMsgAndOpenAPIName("google.rpc", "Status", p.reg)
  1212. if err == nil {
  1213. messages[swgRef] = runtimeError
  1214. } else {
  1215. // just in case there is an error looking up runtimeError
  1216. glog.Error(err)
  1217. }
  1218. }
  1219. // Find all the service's messages and enumerations that are defined (recursively)
  1220. // and write request, response and other custom (but referenced) types out as definition objects.
  1221. findServicesMessagesAndEnumerations(p.Services, p.reg, messages, streamingMessages, enums, requestResponseRefs)
  1222. renderMessagesAsDefinition(messages, s.Definitions, p.reg, customRefs, nil)
  1223. renderEnumerationsAsDefinition(enums, s.Definitions, p.reg)
  1224. // File itself might have some comments and metadata.
  1225. packageProtoPath := protoPathIndex(reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil)), "Package")
  1226. packageComments := protoComments(p.reg, p.File, nil, "Package", packageProtoPath)
  1227. if err := updateOpenAPIDataFromComments(p.reg, &s, p, packageComments, true); err != nil {
  1228. panic(err)
  1229. }
  1230. // There may be additional options in the OpenAPI option in the proto.
  1231. spb, err := getFileOpenAPIOption(p.reg, p.File)
  1232. if err != nil {
  1233. panic(err)
  1234. }
  1235. if spb != nil {
  1236. if spb.Swagger != "" {
  1237. s.Swagger = spb.Swagger
  1238. }
  1239. if spb.Info != nil {
  1240. if spb.Info.Title != "" {
  1241. s.Info.Title = spb.Info.Title
  1242. }
  1243. if spb.Info.Description != "" {
  1244. s.Info.Description = spb.Info.Description
  1245. }
  1246. if spb.Info.TermsOfService != "" {
  1247. s.Info.TermsOfService = spb.Info.TermsOfService
  1248. }
  1249. if spb.Info.Version != "" {
  1250. s.Info.Version = spb.Info.Version
  1251. }
  1252. if spb.Info.Contact != nil {
  1253. if s.Info.Contact == nil {
  1254. s.Info.Contact = &openapiContactObject{}
  1255. }
  1256. if spb.Info.Contact.Name != "" {
  1257. s.Info.Contact.Name = spb.Info.Contact.Name
  1258. }
  1259. if spb.Info.Contact.Url != "" {
  1260. s.Info.Contact.URL = spb.Info.Contact.Url
  1261. }
  1262. if spb.Info.Contact.Email != "" {
  1263. s.Info.Contact.Email = spb.Info.Contact.Email
  1264. }
  1265. }
  1266. if spb.Info.License != nil {
  1267. if s.Info.License == nil {
  1268. s.Info.License = &openapiLicenseObject{}
  1269. }
  1270. if spb.Info.License.Name != "" {
  1271. s.Info.License.Name = spb.Info.License.Name
  1272. }
  1273. if spb.Info.License.Url != "" {
  1274. s.Info.License.URL = spb.Info.License.Url
  1275. }
  1276. }
  1277. if spb.Info.Extensions != nil {
  1278. exts, err := processExtensions(spb.Info.Extensions)
  1279. if err != nil {
  1280. return nil, err
  1281. }
  1282. s.Info.extensions = exts
  1283. }
  1284. }
  1285. if spb.Host != "" {
  1286. s.Host = spb.Host
  1287. }
  1288. if spb.BasePath != "" {
  1289. s.BasePath = spb.BasePath
  1290. }
  1291. if len(spb.Schemes) > 0 {
  1292. s.Schemes = make([]string, len(spb.Schemes))
  1293. for i, scheme := range spb.Schemes {
  1294. s.Schemes[i] = strings.ToLower(scheme.String())
  1295. }
  1296. }
  1297. if len(spb.Consumes) > 0 {
  1298. s.Consumes = make([]string, len(spb.Consumes))
  1299. copy(s.Consumes, spb.Consumes)
  1300. }
  1301. if len(spb.Produces) > 0 {
  1302. s.Produces = make([]string, len(spb.Produces))
  1303. copy(s.Produces, spb.Produces)
  1304. }
  1305. if spb.SecurityDefinitions != nil && spb.SecurityDefinitions.Security != nil {
  1306. if s.SecurityDefinitions == nil {
  1307. s.SecurityDefinitions = openapiSecurityDefinitionsObject{}
  1308. }
  1309. for secDefKey, secDefValue := range spb.SecurityDefinitions.Security {
  1310. var newSecDefValue openapiSecuritySchemeObject
  1311. if oldSecDefValue, ok := s.SecurityDefinitions[secDefKey]; !ok {
  1312. newSecDefValue = openapiSecuritySchemeObject{}
  1313. } else {
  1314. newSecDefValue = oldSecDefValue
  1315. }
  1316. if secDefValue.Type != openapi_options.SecurityScheme_TYPE_INVALID {
  1317. switch secDefValue.Type {
  1318. case openapi_options.SecurityScheme_TYPE_BASIC:
  1319. newSecDefValue.Type = "basic"
  1320. case openapi_options.SecurityScheme_TYPE_API_KEY:
  1321. newSecDefValue.Type = "apiKey"
  1322. case openapi_options.SecurityScheme_TYPE_OAUTH2:
  1323. newSecDefValue.Type = "oauth2"
  1324. }
  1325. }
  1326. if secDefValue.Description != "" {
  1327. newSecDefValue.Description = secDefValue.Description
  1328. }
  1329. if secDefValue.Name != "" {
  1330. newSecDefValue.Name = secDefValue.Name
  1331. }
  1332. if secDefValue.In != openapi_options.SecurityScheme_IN_INVALID {
  1333. switch secDefValue.In {
  1334. case openapi_options.SecurityScheme_IN_QUERY:
  1335. newSecDefValue.In = "query"
  1336. case openapi_options.SecurityScheme_IN_HEADER:
  1337. newSecDefValue.In = "header"
  1338. }
  1339. }
  1340. if secDefValue.Flow != openapi_options.SecurityScheme_FLOW_INVALID {
  1341. switch secDefValue.Flow {
  1342. case openapi_options.SecurityScheme_FLOW_IMPLICIT:
  1343. newSecDefValue.Flow = "implicit"
  1344. case openapi_options.SecurityScheme_FLOW_PASSWORD:
  1345. newSecDefValue.Flow = "password"
  1346. case openapi_options.SecurityScheme_FLOW_APPLICATION:
  1347. newSecDefValue.Flow = "application"
  1348. case openapi_options.SecurityScheme_FLOW_ACCESS_CODE:
  1349. newSecDefValue.Flow = "accessCode"
  1350. }
  1351. }
  1352. if secDefValue.AuthorizationUrl != "" {
  1353. newSecDefValue.AuthorizationURL = secDefValue.AuthorizationUrl
  1354. }
  1355. if secDefValue.TokenUrl != "" {
  1356. newSecDefValue.TokenURL = secDefValue.TokenUrl
  1357. }
  1358. if secDefValue.Scopes != nil {
  1359. if newSecDefValue.Scopes == nil {
  1360. newSecDefValue.Scopes = openapiScopesObject{}
  1361. }
  1362. for scopeKey, scopeDesc := range secDefValue.Scopes.Scope {
  1363. newSecDefValue.Scopes[scopeKey] = scopeDesc
  1364. }
  1365. }
  1366. if secDefValue.Extensions != nil {
  1367. exts, err := processExtensions(secDefValue.Extensions)
  1368. if err != nil {
  1369. return nil, err
  1370. }
  1371. newSecDefValue.extensions = exts
  1372. }
  1373. s.SecurityDefinitions[secDefKey] = newSecDefValue
  1374. }
  1375. }
  1376. if spb.Security != nil {
  1377. var newSecurity []openapiSecurityRequirementObject
  1378. if s.Security != nil {
  1379. newSecurity = s.Security
  1380. }
  1381. for _, secReq := range spb.Security {
  1382. newSecReq := openapiSecurityRequirementObject{}
  1383. for secReqKey, secReqValue := range secReq.SecurityRequirement {
  1384. if secReqValue == nil {
  1385. return nil, fmt.Errorf("malformed security requirement spec for key %q; value is required", secReqKey)
  1386. }
  1387. newSecReqValue := make([]string, len(secReqValue.Scope))
  1388. copy(newSecReqValue, secReqValue.Scope)
  1389. newSecReq[secReqKey] = newSecReqValue
  1390. }
  1391. newSecurity = append(newSecurity, newSecReq)
  1392. }
  1393. s.Security = newSecurity
  1394. }
  1395. s.ExternalDocs = protoExternalDocumentationToOpenAPIExternalDocumentation(spb.ExternalDocs, p.reg, spb)
  1396. // Populate all Paths with Responses set at top level,
  1397. // preferring Responses already set over those at the top level.
  1398. if spb.Responses != nil {
  1399. for _, verbs := range s.Paths {
  1400. var maps []openapiResponsesObject
  1401. if verbs.Delete != nil {
  1402. maps = append(maps, verbs.Delete.Responses)
  1403. }
  1404. if verbs.Get != nil {
  1405. maps = append(maps, verbs.Get.Responses)
  1406. }
  1407. if verbs.Post != nil {
  1408. maps = append(maps, verbs.Post.Responses)
  1409. }
  1410. if verbs.Put != nil {
  1411. maps = append(maps, verbs.Put.Responses)
  1412. }
  1413. if verbs.Patch != nil {
  1414. maps = append(maps, verbs.Patch.Responses)
  1415. }
  1416. for k, v := range spb.Responses {
  1417. for _, respMap := range maps {
  1418. if _, ok := respMap[k]; ok {
  1419. // Don't overwrite already existing Responses
  1420. continue
  1421. }
  1422. respMap[k] = openapiResponseObject{
  1423. Description: v.Description,
  1424. Schema: openapiSchemaFromProtoSchema(v.Schema, p.reg, customRefs, nil),
  1425. Examples: openapiExamplesFromProtoExamples(v.Examples),
  1426. }
  1427. }
  1428. }
  1429. }
  1430. }
  1431. if spb.Extensions != nil {
  1432. exts, err := processExtensions(spb.Extensions)
  1433. if err != nil {
  1434. return nil, err
  1435. }
  1436. s.extensions = exts
  1437. }
  1438. // Additional fields on the OpenAPI v2 spec's "OpenAPI" object
  1439. // should be added here, once supported in the proto.
  1440. }
  1441. // Finally add any references added by users that aren't
  1442. // otherwise rendered.
  1443. addCustomRefs(s.Definitions, p.reg, customRefs)
  1444. return &s, nil
  1445. }
  1446. func processExtensions(inputExts map[string]*structpb.Value) ([]extension, error) {
  1447. exts := []extension{}
  1448. for k, v := range inputExts {
  1449. if !strings.HasPrefix(k, "x-") {
  1450. return nil, fmt.Errorf("extension keys need to start with \"x-\": %q", k)
  1451. }
  1452. ext, err := (&protojson.MarshalOptions{Indent: " "}).Marshal(v)
  1453. if err != nil {
  1454. return nil, err
  1455. }
  1456. exts = append(exts, extension{key: k, value: json.RawMessage(ext)})
  1457. }
  1458. sort.Slice(exts, func(i, j int) bool { return exts[i].key < exts[j].key })
  1459. return exts, nil
  1460. }
  1461. func validateHeaderTypeAndFormat(headerType string, format string) error {
  1462. // The type of the object. The value MUST be one of "string", "number", "integer", "boolean", or "array"
  1463. // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#headerObject
  1464. // Note: currently not implementing array as we are only implementing this in the operation response context
  1465. switch headerType {
  1466. // the format property is an open string-valued property, and can have any value to support documentation needs
  1467. // primary check for format is to ensure that the number/integer formats are extensions of the specified type
  1468. // See: https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/2.0.md#dataTypeFormat
  1469. case "string":
  1470. return nil
  1471. case "number":
  1472. switch format {
  1473. case "uint",
  1474. "uint8",
  1475. "uint16",
  1476. "uint32",
  1477. "uint64",
  1478. "int",
  1479. "int8",
  1480. "int16",
  1481. "int32",
  1482. "int64",
  1483. "float",
  1484. "float32",
  1485. "float64",
  1486. "complex64",
  1487. "complex128",
  1488. "double",
  1489. "byte",
  1490. "rune",
  1491. "uintptr",
  1492. "":
  1493. return nil
  1494. default:
  1495. return fmt.Errorf("the provided format %q is not a valid extension of the type %q", format, headerType)
  1496. }
  1497. case "integer":
  1498. switch format {
  1499. case "uint",
  1500. "uint8",
  1501. "uint16",
  1502. "uint32",
  1503. "uint64",
  1504. "int",
  1505. "int8",
  1506. "int16",
  1507. "int32",
  1508. "int64",
  1509. "":
  1510. return nil
  1511. default:
  1512. return fmt.Errorf("the provided format %q is not a valid extension of the type %q", format, headerType)
  1513. }
  1514. case "boolean":
  1515. return nil
  1516. }
  1517. return fmt.Errorf("the provided header type %q is not supported", headerType)
  1518. }
  1519. func validateDefaultValueTypeAndFormat(headerType string, defaultValue string, format string) error {
  1520. switch headerType {
  1521. case "string":
  1522. if !isQuotedString(defaultValue) {
  1523. return fmt.Errorf("the provided default value %q does not match provider type %q, or is not properly quoted with escaped quotations", defaultValue, headerType)
  1524. }
  1525. switch format {
  1526. case "date-time":
  1527. unquoteTime := strings.Trim(defaultValue, `"`)
  1528. _, err := time.Parse(time.RFC3339, unquoteTime)
  1529. if err != nil {
  1530. return fmt.Errorf("the provided default value %q is not a valid RFC3339 date-time string", defaultValue)
  1531. }
  1532. case "date":
  1533. const (
  1534. layoutRFC3339Date = "2006-01-02"
  1535. )
  1536. unquoteDate := strings.Trim(defaultValue, `"`)
  1537. _, err := time.Parse(layoutRFC3339Date, unquoteDate)
  1538. if err != nil {
  1539. return fmt.Errorf("the provided default value %q is not a valid RFC3339 date-time string", defaultValue)
  1540. }
  1541. }
  1542. case "number":
  1543. err := isJSONNumber(defaultValue, headerType)
  1544. if err != nil {
  1545. return err
  1546. }
  1547. case "integer":
  1548. switch format {
  1549. case "int32":
  1550. _, err := strconv.ParseInt(defaultValue, 0, 32)
  1551. if err != nil {
  1552. return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
  1553. }
  1554. case "uint32":
  1555. _, err := strconv.ParseUint(defaultValue, 0, 32)
  1556. if err != nil {
  1557. return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
  1558. }
  1559. case "int64":
  1560. _, err := strconv.ParseInt(defaultValue, 0, 64)
  1561. if err != nil {
  1562. return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
  1563. }
  1564. case "uint64":
  1565. _, err := strconv.ParseUint(defaultValue, 0, 64)
  1566. if err != nil {
  1567. return fmt.Errorf("the provided default value %q does not match provided format %q", defaultValue, format)
  1568. }
  1569. default:
  1570. _, err := strconv.ParseInt(defaultValue, 0, 64)
  1571. if err != nil {
  1572. return fmt.Errorf("the provided default value %q does not match provided type %q", defaultValue, headerType)
  1573. }
  1574. }
  1575. case "boolean":
  1576. if !isBool(defaultValue) {
  1577. return fmt.Errorf("the provided default value %q does not match provider type %q", defaultValue, headerType)
  1578. }
  1579. }
  1580. return nil
  1581. }
  1582. func isQuotedString(s string) bool {
  1583. return len(s) >= 2 && s[0] == '"' && s[len(s)-1] == '"'
  1584. }
  1585. func isJSONNumber(s string, t string) error {
  1586. val, err := strconv.ParseFloat(s, 64)
  1587. if err != nil {
  1588. return fmt.Errorf("the provided default value %q does not match provider type %q", s, t)
  1589. }
  1590. // Floating point values that cannot be represented as sequences of digits (such as Infinity and NaN) are not permitted.
  1591. // See: https://tools.ietf.org/html/rfc4627#section-2.4
  1592. if math.IsInf(val, 0) || math.IsNaN(val) {
  1593. return fmt.Errorf("the provided number %q is not a valid JSON number", s)
  1594. }
  1595. return nil
  1596. }
  1597. func isBool(s string) bool {
  1598. // Unable to use strconv.ParseBool because it returns truthy values https://golang.org/pkg/strconv/#example_ParseBool
  1599. // per https://swagger.io/specification/v2/#data-types
  1600. // type: boolean represents two values: true and false. Note that truthy and falsy values such as "true", "", 0 or null are not considered boolean values.
  1601. return s == "true" || s == "false"
  1602. }
  1603. func processHeaders(inputHdrs map[string]*openapi_options.Header) (openapiHeadersObject, error) {
  1604. hdrs := map[string]openapiHeaderObject{}
  1605. for k, v := range inputHdrs {
  1606. header := textproto.CanonicalMIMEHeaderKey(k)
  1607. ret := openapiHeaderObject{
  1608. Description: v.Description,
  1609. Format: v.Format,
  1610. Pattern: v.Pattern,
  1611. }
  1612. err := validateHeaderTypeAndFormat(v.Type, v.Format)
  1613. if err != nil {
  1614. return nil, err
  1615. }
  1616. ret.Type = v.Type
  1617. if v.Default != "" {
  1618. err := validateDefaultValueTypeAndFormat(v.Type, v.Default, v.Format)
  1619. if err != nil {
  1620. return nil, err
  1621. }
  1622. ret.Default = json.RawMessage(v.Default)
  1623. }
  1624. hdrs[header] = ret
  1625. }
  1626. return hdrs, nil
  1627. }
  1628. // updateOpenAPIDataFromComments updates a OpenAPI object based on a comment
  1629. // from the proto file.
  1630. //
  1631. // First paragraph of a comment is used for summary. Remaining paragraphs of
  1632. // a comment are used for description. If 'Summary' field is not present on
  1633. // the passed swaggerObject, the summary and description are joined by \n\n.
  1634. //
  1635. // If there is a field named 'Info', its 'Summary' and 'Description' fields
  1636. // will be updated instead.
  1637. //
  1638. // If there is no 'Summary', the same behavior will be attempted on 'Title',
  1639. // but only if the last character is not a period.
  1640. func updateOpenAPIDataFromComments(reg *descriptor.Registry, swaggerObject interface{}, data interface{}, comment string, isPackageObject bool) error {
  1641. if len(comment) == 0 {
  1642. return nil
  1643. }
  1644. // Checks whether the "use_go_templates" flag is set to true
  1645. if reg.GetUseGoTemplate() {
  1646. comment = goTemplateComments(comment, data, reg)
  1647. }
  1648. // Figure out what to apply changes to.
  1649. swaggerObjectValue := reflect.ValueOf(swaggerObject)
  1650. infoObjectValue := swaggerObjectValue.Elem().FieldByName("Info")
  1651. if !infoObjectValue.CanSet() {
  1652. // No such field? Apply summary and description directly to
  1653. // passed object.
  1654. infoObjectValue = swaggerObjectValue.Elem()
  1655. }
  1656. // Figure out which properties to update.
  1657. summaryValue := infoObjectValue.FieldByName("Summary")
  1658. descriptionValue := infoObjectValue.FieldByName("Description")
  1659. readOnlyValue := infoObjectValue.FieldByName("ReadOnly")
  1660. if readOnlyValue.Kind() == reflect.Bool && readOnlyValue.CanSet() && strings.Contains(comment, "Output only.") {
  1661. readOnlyValue.Set(reflect.ValueOf(true))
  1662. }
  1663. usingTitle := false
  1664. if !summaryValue.CanSet() {
  1665. summaryValue = infoObjectValue.FieldByName("Title")
  1666. usingTitle = true
  1667. }
  1668. paragraphs := strings.Split(comment, "\n\n")
  1669. // If there is a summary (or summary-equivalent) and it's empty, use the first
  1670. // paragraph as summary, and the rest as description.
  1671. if summaryValue.CanSet() {
  1672. summary := strings.TrimSpace(paragraphs[0])
  1673. description := strings.TrimSpace(strings.Join(paragraphs[1:], "\n\n"))
  1674. if !usingTitle || (len(summary) > 0 && summary[len(summary)-1] != '.') {
  1675. // overrides the schema value only if it's empty
  1676. // keep the comment precedence when updating the package definition
  1677. if summaryValue.Len() == 0 || isPackageObject {
  1678. summaryValue.Set(reflect.ValueOf(summary))
  1679. }
  1680. if len(description) > 0 {
  1681. if !descriptionValue.CanSet() {
  1682. return fmt.Errorf("encountered object type with a summary, but no description")
  1683. }
  1684. // overrides the schema value only if it's empty
  1685. // keep the comment precedence when updating the package definition
  1686. if descriptionValue.Len() == 0 || isPackageObject {
  1687. descriptionValue.Set(reflect.ValueOf(description))
  1688. }
  1689. }
  1690. return nil
  1691. }
  1692. }
  1693. // There was no summary field on the swaggerObject. Try to apply the
  1694. // whole comment into description if the OpenAPI object description is empty.
  1695. if descriptionValue.CanSet() {
  1696. if descriptionValue.Len() == 0 || isPackageObject {
  1697. descriptionValue.Set(reflect.ValueOf(strings.Join(paragraphs, "\n\n")))
  1698. }
  1699. return nil
  1700. }
  1701. return fmt.Errorf("no description nor summary property")
  1702. }
  1703. func fieldProtoComments(reg *descriptor.Registry, msg *descriptor.Message, field *descriptor.Field) string {
  1704. protoPath := protoPathIndex(reflect.TypeOf((*descriptorpb.DescriptorProto)(nil)), "Field")
  1705. for i, f := range msg.Fields {
  1706. if f == field {
  1707. return protoComments(reg, msg.File, msg.Outers, "MessageType", int32(msg.Index), protoPath, int32(i))
  1708. }
  1709. }
  1710. return ""
  1711. }
  1712. func enumValueProtoComments(reg *descriptor.Registry, enum *descriptor.Enum) string {
  1713. protoPath := protoPathIndex(reflect.TypeOf((*descriptorpb.EnumDescriptorProto)(nil)), "Value")
  1714. var comments []string
  1715. for idx, value := range enum.GetValue() {
  1716. name := value.GetName()
  1717. if reg.GetEnumsAsInts() {
  1718. name = strconv.Itoa(int(value.GetNumber()))
  1719. }
  1720. str := protoComments(reg, enum.File, enum.Outers, "EnumType", int32(enum.Index), protoPath, int32(idx))
  1721. if str != "" {
  1722. comments = append(comments, name+": "+str)
  1723. }
  1724. }
  1725. if len(comments) > 0 {
  1726. return "- " + strings.Join(comments, "\n - ")
  1727. }
  1728. return ""
  1729. }
  1730. func protoComments(reg *descriptor.Registry, file *descriptor.File, outers []string, typeName string, typeIndex int32, fieldPaths ...int32) string {
  1731. if file.SourceCodeInfo == nil {
  1732. //fmt.Fprintln(os.Stderr, "descriptor.File should not contain nil SourceCodeInfo")
  1733. return ""
  1734. }
  1735. outerPaths := make([]int32, len(outers))
  1736. for i := range outers {
  1737. location := ""
  1738. if file.Package != nil {
  1739. location = file.GetPackage()
  1740. }
  1741. msg, err := reg.LookupMsg(location, strings.Join(outers[:i+1], "."))
  1742. if err != nil {
  1743. panic(err)
  1744. }
  1745. outerPaths[i] = int32(msg.Index)
  1746. }
  1747. for _, loc := range file.SourceCodeInfo.Location {
  1748. if !isProtoPathMatches(loc.Path, outerPaths, typeName, typeIndex, fieldPaths) {
  1749. continue
  1750. }
  1751. comments := ""
  1752. if loc.LeadingComments != nil {
  1753. comments = strings.TrimRight(*loc.LeadingComments, "\n")
  1754. comments = strings.TrimSpace(comments)
  1755. // TODO(ivucica): this is a hack to fix "// " being interpreted as "//".
  1756. // perhaps we should:
  1757. // - split by \n
  1758. // - determine if every (but first and last) line begins with " "
  1759. // - trim every line only if that is the case
  1760. // - join by \n
  1761. comments = strings.Replace(comments, "\n ", "\n", -1)
  1762. }
  1763. return comments
  1764. }
  1765. return ""
  1766. }
  1767. func goTemplateComments(comment string, data interface{}, reg *descriptor.Registry) string {
  1768. var temp bytes.Buffer
  1769. tpl, err := template.New("").Funcs(template.FuncMap{
  1770. // Allows importing documentation from a file
  1771. "import": func(name string) string {
  1772. file, err := ioutil.ReadFile(name)
  1773. if err != nil {
  1774. return err.Error()
  1775. }
  1776. // Runs template over imported file
  1777. return goTemplateComments(string(file), data, reg)
  1778. },
  1779. // Grabs title and description from a field
  1780. "fieldcomments": func(msg *descriptor.Message, field *descriptor.Field) string {
  1781. return strings.Replace(fieldProtoComments(reg, msg, field), "\n", "<br>", -1)
  1782. },
  1783. }).Parse(comment)
  1784. if err != nil {
  1785. // If there is an error parsing the templating insert the error as string in the comment
  1786. // to make it easier to debug the template error
  1787. return err.Error()
  1788. }
  1789. err = tpl.Execute(&temp, data)
  1790. if err != nil {
  1791. // If there is an error executing the templating insert the error as string in the comment
  1792. // to make it easier to debug the error
  1793. return err.Error()
  1794. }
  1795. return temp.String()
  1796. }
  1797. var messageProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil)), "MessageType")
  1798. var nestedProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.DescriptorProto)(nil)), "NestedType")
  1799. var packageProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil)), "Package")
  1800. var serviceProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil)), "Service")
  1801. var methodProtoPath = protoPathIndex(reflect.TypeOf((*descriptorpb.ServiceDescriptorProto)(nil)), "Method")
  1802. func isProtoPathMatches(paths []int32, outerPaths []int32, typeName string, typeIndex int32, fieldPaths []int32) bool {
  1803. if typeName == "Package" && typeIndex == packageProtoPath {
  1804. // path for package comments is just [2], and all the other processing
  1805. // is too complex for it.
  1806. if len(paths) == 0 || typeIndex != paths[0] {
  1807. return false
  1808. }
  1809. return true
  1810. }
  1811. if len(paths) != len(outerPaths)*2+2+len(fieldPaths) {
  1812. return false
  1813. }
  1814. if typeName == "Method" {
  1815. if paths[0] != serviceProtoPath || paths[2] != methodProtoPath {
  1816. return false
  1817. }
  1818. paths = paths[2:]
  1819. } else {
  1820. typeNameDescriptor := reflect.TypeOf((*descriptorpb.FileDescriptorProto)(nil))
  1821. if len(outerPaths) > 0 {
  1822. if paths[0] != messageProtoPath || paths[1] != outerPaths[0] {
  1823. return false
  1824. }
  1825. paths = paths[2:]
  1826. outerPaths = outerPaths[1:]
  1827. for i, v := range outerPaths {
  1828. if paths[i*2] != nestedProtoPath || paths[i*2+1] != v {
  1829. return false
  1830. }
  1831. }
  1832. paths = paths[len(outerPaths)*2:]
  1833. if typeName == "MessageType" {
  1834. typeName = "NestedType"
  1835. }
  1836. typeNameDescriptor = reflect.TypeOf((*descriptorpb.DescriptorProto)(nil))
  1837. }
  1838. if paths[0] != protoPathIndex(typeNameDescriptor, typeName) || paths[1] != typeIndex {
  1839. return false
  1840. }
  1841. paths = paths[2:]
  1842. }
  1843. for i, v := range fieldPaths {
  1844. if paths[i] != v {
  1845. return false
  1846. }
  1847. }
  1848. return true
  1849. }
  1850. // protoPathIndex returns a path component for google.protobuf.descriptor.SourceCode_Location.
  1851. //
  1852. // Specifically, it returns an id as generated from descriptor proto which
  1853. // can be used to determine what type the id following it in the path is.
  1854. // For example, if we are trying to locate comments related to a field named
  1855. // `Address` in a message named `Person`, the path will be:
  1856. //
  1857. // [4, a, 2, b]
  1858. //
  1859. // While `a` gets determined by the order in which the messages appear in
  1860. // the proto file, and `b` is the field index specified in the proto
  1861. // file itself, the path actually needs to specify that `a` refers to a
  1862. // message and not, say, a service; and that `b` refers to a field and not
  1863. // an option.
  1864. //
  1865. // protoPathIndex figures out the values 4 and 2 in the above example. Because
  1866. // messages are top level objects, the value of 4 comes from field id for
  1867. // `MessageType` inside `google.protobuf.descriptor.FileDescriptor` message.
  1868. // This field has a message type `google.protobuf.descriptor.DescriptorProto`.
  1869. // And inside message `DescriptorProto`, there is a field named `Field` with id
  1870. // 2.
  1871. //
  1872. // Some code generators seem to be hardcoding these values; this method instead
  1873. // interprets them from `descriptor.proto`-derived Go source as necessary.
  1874. func protoPathIndex(descriptorType reflect.Type, what string) int32 {
  1875. field, ok := descriptorType.Elem().FieldByName(what)
  1876. if !ok {
  1877. panic(fmt.Errorf("could not find protobuf descriptor type id for %s", what))
  1878. }
  1879. pbtag := field.Tag.Get("protobuf")
  1880. if pbtag == "" {
  1881. panic(fmt.Errorf("no Go tag 'protobuf' on protobuf descriptor for %s", what))
  1882. }
  1883. path, err := strconv.Atoi(strings.Split(pbtag, ",")[1])
  1884. if err != nil {
  1885. panic(fmt.Errorf("protobuf descriptor id for %s cannot be converted to a number: %s", what, err.Error()))
  1886. }
  1887. return int32(path)
  1888. }
  1889. // extractOperationOptionFromMethodDescriptor extracts the message of type
  1890. // openapi_options.Operation from a given proto method's descriptor.
  1891. func extractOperationOptionFromMethodDescriptor(meth *descriptorpb.MethodDescriptorProto) (*openapi_options.Operation, error) {
  1892. if meth.Options == nil {
  1893. return nil, nil
  1894. }
  1895. if !proto.HasExtension(meth.Options, openapi_options.E_Openapiv2Operation) {
  1896. return nil, nil
  1897. }
  1898. ext := proto.GetExtension(meth.Options, openapi_options.E_Openapiv2Operation)
  1899. opts, ok := ext.(*openapi_options.Operation)
  1900. if !ok {
  1901. return nil, fmt.Errorf("extension is %T; want an Operation", ext)
  1902. }
  1903. return opts, nil
  1904. }
  1905. // extractSchemaOptionFromMessageDescriptor extracts the message of type
  1906. // openapi_options.Schema from a given proto message's descriptor.
  1907. func extractSchemaOptionFromMessageDescriptor(msg *descriptorpb.DescriptorProto) (*openapi_options.Schema, error) {
  1908. if msg.Options == nil {
  1909. return nil, nil
  1910. }
  1911. if !proto.HasExtension(msg.Options, openapi_options.E_Openapiv2Schema) {
  1912. return nil, nil
  1913. }
  1914. ext := proto.GetExtension(msg.Options, openapi_options.E_Openapiv2Schema)
  1915. opts, ok := ext.(*openapi_options.Schema)
  1916. if !ok {
  1917. return nil, fmt.Errorf("extension is %T; want a Schema", ext)
  1918. }
  1919. return opts, nil
  1920. }
  1921. // extractOpenAPIOptionFromFileDescriptor extracts the message of type
  1922. // openapi_options.OpenAPI from a given proto method's descriptor.
  1923. func extractOpenAPIOptionFromFileDescriptor(file *descriptorpb.FileDescriptorProto) (*openapi_options.Swagger, error) {
  1924. if file.Options == nil {
  1925. return nil, nil
  1926. }
  1927. if !proto.HasExtension(file.Options, openapi_options.E_Openapiv2Swagger) {
  1928. return nil, nil
  1929. }
  1930. ext := proto.GetExtension(file.Options, openapi_options.E_Openapiv2Swagger)
  1931. opts, ok := ext.(*openapi_options.Swagger)
  1932. if !ok {
  1933. return nil, fmt.Errorf("extension is %T; want a OpenAPI object", ext)
  1934. }
  1935. return opts, nil
  1936. }
  1937. func extractJSONSchemaFromFieldDescriptor(fd *descriptorpb.FieldDescriptorProto) (*openapi_options.JSONSchema, error) {
  1938. if fd.Options == nil {
  1939. return nil, nil
  1940. }
  1941. if !proto.HasExtension(fd.Options, openapi_options.E_Openapiv2Field) {
  1942. return nil, nil
  1943. }
  1944. ext := proto.GetExtension(fd.Options, openapi_options.E_Openapiv2Field)
  1945. opts, ok := ext.(*openapi_options.JSONSchema)
  1946. if !ok {
  1947. return nil, fmt.Errorf("extension is %T; want a JSONSchema object", ext)
  1948. }
  1949. return opts, nil
  1950. }
  1951. func extractFieldBehaviorFromFieldDescriptor(fd *descriptorpb.FieldDescriptorProto) ([]annotations.FieldBehavior, error) {
  1952. if fd.Options == nil {
  1953. return nil, nil
  1954. }
  1955. if !proto.HasExtension(fd.Options, annotations.E_FieldBehavior) {
  1956. return nil, nil
  1957. }
  1958. ext := proto.GetExtension(fd.Options, annotations.E_FieldBehavior)
  1959. opts, ok := ext.([]annotations.FieldBehavior)
  1960. if !ok {
  1961. return nil, fmt.Errorf("extension is %T; want a []FieldBehavior object", ext)
  1962. }
  1963. return opts, nil
  1964. }
  1965. func getMethodOpenAPIOption(reg *descriptor.Registry, meth *descriptor.Method) (*openapi_options.Operation, error) {
  1966. opts, err := extractOperationOptionFromMethodDescriptor(meth.MethodDescriptorProto)
  1967. if err != nil {
  1968. return nil, err
  1969. }
  1970. if opts != nil {
  1971. return opts, nil
  1972. }
  1973. opts, ok := reg.GetOpenAPIMethodOption(meth.FQMN())
  1974. if !ok {
  1975. return nil, nil
  1976. }
  1977. return opts, nil
  1978. }
  1979. func getMessageOpenAPIOption(reg *descriptor.Registry, msg *descriptor.Message) (*openapi_options.Schema, error) {
  1980. opts, err := extractSchemaOptionFromMessageDescriptor(msg.DescriptorProto)
  1981. if err != nil {
  1982. return nil, err
  1983. }
  1984. if opts != nil {
  1985. return opts, nil
  1986. }
  1987. opts, ok := reg.GetOpenAPIMessageOption(msg.FQMN())
  1988. if !ok {
  1989. return nil, nil
  1990. }
  1991. return opts, nil
  1992. }
  1993. func getFileOpenAPIOption(reg *descriptor.Registry, file *descriptor.File) (*openapi_options.Swagger, error) {
  1994. opts, err := extractOpenAPIOptionFromFileDescriptor(file.FileDescriptorProto)
  1995. if err != nil {
  1996. return nil, err
  1997. }
  1998. if opts != nil {
  1999. return opts, nil
  2000. }
  2001. opts, ok := reg.GetOpenAPIFileOption(*file.Name)
  2002. if !ok {
  2003. return nil, nil
  2004. }
  2005. return opts, nil
  2006. }
  2007. func getFieldOpenAPIOption(reg *descriptor.Registry, fd *descriptor.Field) (*openapi_options.JSONSchema, error) {
  2008. opts, err := extractJSONSchemaFromFieldDescriptor(fd.FieldDescriptorProto)
  2009. if err != nil {
  2010. return nil, err
  2011. }
  2012. if opts != nil {
  2013. return opts, nil
  2014. }
  2015. opts, ok := reg.GetOpenAPIFieldOption(fd.FQFN())
  2016. if !ok {
  2017. return nil, nil
  2018. }
  2019. return opts, nil
  2020. }
  2021. func getFieldBehaviorOption(reg *descriptor.Registry, fd *descriptor.Field) ([]annotations.FieldBehavior, error) {
  2022. opts, err := extractFieldBehaviorFromFieldDescriptor(fd.FieldDescriptorProto)
  2023. if err != nil {
  2024. return nil, err
  2025. }
  2026. if opts != nil {
  2027. return opts, nil
  2028. }
  2029. return opts, nil
  2030. }
  2031. func protoJSONSchemaToOpenAPISchemaCore(j *openapi_options.JSONSchema, reg *descriptor.Registry, refs refMap) schemaCore {
  2032. ret := schemaCore{}
  2033. if j.GetRef() != "" {
  2034. openapiName, ok := fullyQualifiedNameToOpenAPIName(j.GetRef(), reg)
  2035. if ok {
  2036. ret.Ref = "#/definitions/" + openapiName
  2037. if refs != nil {
  2038. refs[j.GetRef()] = struct{}{}
  2039. }
  2040. } else {
  2041. ret.Ref += j.GetRef()
  2042. }
  2043. } else {
  2044. f, t := protoJSONSchemaTypeToFormat(j.GetType())
  2045. ret.Format = f
  2046. ret.Type = t
  2047. }
  2048. return ret
  2049. }
  2050. func updateswaggerObjectFromJSONSchema(s *openapiSchemaObject, j *openapi_options.JSONSchema, reg *descriptor.Registry, data interface{}) {
  2051. s.Title = j.GetTitle()
  2052. s.Description = j.GetDescription()
  2053. if reg.GetUseGoTemplate() {
  2054. s.Title = goTemplateComments(s.Title, data, reg)
  2055. s.Description = goTemplateComments(s.Description, data, reg)
  2056. }
  2057. s.ReadOnly = j.GetReadOnly()
  2058. s.MultipleOf = j.GetMultipleOf()
  2059. s.Maximum = j.GetMaximum()
  2060. s.ExclusiveMaximum = j.GetExclusiveMaximum()
  2061. s.Minimum = j.GetMinimum()
  2062. s.ExclusiveMinimum = j.GetExclusiveMinimum()
  2063. s.MaxLength = j.GetMaxLength()
  2064. s.MinLength = j.GetMinLength()
  2065. s.Pattern = j.GetPattern()
  2066. s.Default = j.GetDefault()
  2067. s.MaxItems = j.GetMaxItems()
  2068. s.MinItems = j.GetMinItems()
  2069. s.UniqueItems = j.GetUniqueItems()
  2070. s.MaxProperties = j.GetMaxProperties()
  2071. s.MinProperties = j.GetMinProperties()
  2072. s.Required = j.GetRequired()
  2073. s.Enum = j.GetEnum()
  2074. if overrideType := j.GetType(); len(overrideType) > 0 {
  2075. s.Type = strings.ToLower(overrideType[0].String())
  2076. }
  2077. if j != nil && j.GetExample() != "" {
  2078. s.Example = json.RawMessage(j.GetExample())
  2079. }
  2080. if j != nil && j.GetFormat() != "" {
  2081. s.Format = j.GetFormat()
  2082. }
  2083. }
  2084. func updateSwaggerObjectFromFieldBehavior(s *openapiSchemaObject, j []annotations.FieldBehavior, field *descriptor.Field) {
  2085. // Per the JSON Reference syntax: Any members other than "$ref" in a JSON Reference object SHALL be ignored.
  2086. // https://tools.ietf.org/html/draft-pbryan-zyp-json-ref-03#section-3
  2087. if s.Ref != "" {
  2088. return
  2089. }
  2090. for _, fb := range j {
  2091. switch fb {
  2092. case annotations.FieldBehavior_REQUIRED:
  2093. s.Required = append(s.Required, *field.Name)
  2094. case annotations.FieldBehavior_OUTPUT_ONLY:
  2095. s.ReadOnly = true
  2096. case annotations.FieldBehavior_FIELD_BEHAVIOR_UNSPECIFIED:
  2097. case annotations.FieldBehavior_OPTIONAL:
  2098. case annotations.FieldBehavior_INPUT_ONLY:
  2099. // OpenAPI v3 supports a writeOnly property, but this is not supported in Open API v2
  2100. case annotations.FieldBehavior_IMMUTABLE:
  2101. }
  2102. }
  2103. }
  2104. func openapiSchemaFromProtoSchema(s *openapi_options.Schema, reg *descriptor.Registry, refs refMap, data interface{}) openapiSchemaObject {
  2105. ret := openapiSchemaObject{
  2106. ExternalDocs: protoExternalDocumentationToOpenAPIExternalDocumentation(s.GetExternalDocs(), reg, data),
  2107. }
  2108. ret.schemaCore = protoJSONSchemaToOpenAPISchemaCore(s.GetJsonSchema(), reg, refs)
  2109. updateswaggerObjectFromJSONSchema(&ret, s.GetJsonSchema(), reg, data)
  2110. if s != nil && s.Example != "" {
  2111. ret.Example = json.RawMessage(s.Example)
  2112. }
  2113. return ret
  2114. }
  2115. func openapiExamplesFromProtoExamples(in map[string]string) map[string]interface{} {
  2116. if len(in) == 0 {
  2117. return nil
  2118. }
  2119. out := make(map[string]interface{})
  2120. for mimeType, exampleStr := range in {
  2121. switch mimeType {
  2122. case "application/json":
  2123. // JSON example objects are rendered raw.
  2124. out[mimeType] = json.RawMessage(exampleStr)
  2125. default:
  2126. // All other mimetype examples are rendered as strings.
  2127. out[mimeType] = exampleStr
  2128. }
  2129. }
  2130. return out
  2131. }
  2132. func protoJSONSchemaTypeToFormat(in []openapi_options.JSONSchema_JSONSchemaSimpleTypes) (string, string) {
  2133. if len(in) == 0 {
  2134. return "", ""
  2135. }
  2136. // Can't support more than 1 type, just return the first element.
  2137. // This is due to an inconsistency in the design of the openapiv2 proto
  2138. // and that used in schemaCore. schemaCore uses the v3 definition of types,
  2139. // which only allows a single string, while the openapiv2 proto uses the OpenAPI v2
  2140. // definition, which defers to the JSON schema definition, which allows a string or an array.
  2141. // Sources:
  2142. // https://swagger.io/specification/#itemsObject
  2143. // https://tools.ietf.org/html/draft-fge-json-schema-validation-00#section-5.5.2
  2144. switch in[0] {
  2145. case openapi_options.JSONSchema_UNKNOWN, openapi_options.JSONSchema_NULL:
  2146. return "", ""
  2147. case openapi_options.JSONSchema_OBJECT:
  2148. return "object", ""
  2149. case openapi_options.JSONSchema_ARRAY:
  2150. return "array", ""
  2151. case openapi_options.JSONSchema_BOOLEAN:
  2152. // NOTE: in OpenAPI specification, format should be empty on boolean type
  2153. return "boolean", ""
  2154. case openapi_options.JSONSchema_INTEGER:
  2155. return "integer", "int32"
  2156. case openapi_options.JSONSchema_NUMBER:
  2157. return "number", "double"
  2158. case openapi_options.JSONSchema_STRING:
  2159. // NOTE: in OpenAPI specification, format should be empty on string type
  2160. return "string", ""
  2161. default:
  2162. // Maybe panic?
  2163. return "", ""
  2164. }
  2165. }
  2166. func protoExternalDocumentationToOpenAPIExternalDocumentation(in *openapi_options.ExternalDocumentation, reg *descriptor.Registry, data interface{}) *openapiExternalDocumentationObject {
  2167. if in == nil {
  2168. return nil
  2169. }
  2170. if reg.GetUseGoTemplate() {
  2171. in.Description = goTemplateComments(in.Description, data, reg)
  2172. }
  2173. return &openapiExternalDocumentationObject{
  2174. Description: in.Description,
  2175. URL: in.Url,
  2176. }
  2177. }
  2178. func addCustomRefs(d openapiDefinitionsObject, reg *descriptor.Registry, refs refMap) {
  2179. if len(refs) == 0 {
  2180. return
  2181. }
  2182. msgMap := make(messageMap)
  2183. enumMap := make(enumMap)
  2184. for ref := range refs {
  2185. swgName, swgOk := fullyQualifiedNameToOpenAPIName(ref, reg)
  2186. if !swgOk {
  2187. glog.Errorf("can't resolve OpenAPI name from CustomRef '%v'", ref)
  2188. continue
  2189. }
  2190. if _, ok := d[swgName]; ok {
  2191. // Skip already existing definitions
  2192. delete(refs, ref)
  2193. continue
  2194. }
  2195. msg, err := reg.LookupMsg("", ref)
  2196. if err == nil {
  2197. msgMap[swgName] = msg
  2198. continue
  2199. }
  2200. enum, err := reg.LookupEnum("", ref)
  2201. if err == nil {
  2202. enumMap[swgName] = enum
  2203. continue
  2204. }
  2205. // ?? Should be either enum or msg
  2206. }
  2207. renderMessagesAsDefinition(msgMap, d, reg, refs, nil)
  2208. renderEnumerationsAsDefinition(enumMap, d, reg)
  2209. // Run again in case any new refs were added
  2210. addCustomRefs(d, reg, refs)
  2211. }
  2212. func lowerCamelCase(fieldName string, fields []*descriptor.Field, msgs []*descriptor.Message) string {
  2213. for _, oneField := range fields {
  2214. if oneField.GetName() == fieldName {
  2215. return oneField.GetJsonName()
  2216. }
  2217. }
  2218. messageNameToFieldsToJSONName := make(map[string]map[string]string)
  2219. fieldNameToType := make(map[string]string)
  2220. for _, msg := range msgs {
  2221. fieldNameToJSONName := make(map[string]string)
  2222. for _, oneField := range msg.GetField() {
  2223. fieldNameToJSONName[oneField.GetName()] = oneField.GetJsonName()
  2224. fieldNameToType[oneField.GetName()] = oneField.GetTypeName()
  2225. }
  2226. messageNameToFieldsToJSONName[msg.GetName()] = fieldNameToJSONName
  2227. }
  2228. if strings.Contains(fieldName, ".") {
  2229. fieldNames := strings.Split(fieldName, ".")
  2230. fieldNamesWithCamelCase := make([]string, 0)
  2231. for i := 0; i < len(fieldNames)-1; i++ {
  2232. fieldNamesWithCamelCase = append(fieldNamesWithCamelCase, doCamelCase(string(fieldNames[i])))
  2233. }
  2234. prefix := strings.Join(fieldNamesWithCamelCase, ".")
  2235. reservedJSONName := getReservedJSONName(fieldName, messageNameToFieldsToJSONName, fieldNameToType)
  2236. if reservedJSONName != "" {
  2237. return prefix + "." + reservedJSONName
  2238. }
  2239. }
  2240. return doCamelCase(fieldName)
  2241. }
  2242. func doCamelCase(input string) string {
  2243. parameterString := casing.Camel(input)
  2244. builder := &strings.Builder{}
  2245. builder.WriteString(strings.ToLower(string(parameterString[0])))
  2246. builder.WriteString(parameterString[1:])
  2247. return builder.String()
  2248. }
  2249. func getReservedJSONName(fieldName string, messageNameToFieldsToJSONName map[string]map[string]string, fieldNameToType map[string]string) string {
  2250. if len(strings.Split(fieldName, ".")) == 2 {
  2251. fieldNames := strings.Split(fieldName, ".")
  2252. firstVariable := fieldNames[0]
  2253. firstType := fieldNameToType[firstVariable]
  2254. firstTypeShortNames := strings.Split(firstType, ".")
  2255. firstTypeShortName := firstTypeShortNames[len(firstTypeShortNames)-1]
  2256. return messageNameToFieldsToJSONName[firstTypeShortName][fieldNames[1]]
  2257. }
  2258. fieldNames := strings.Split(fieldName, ".")
  2259. return getReservedJSONName(strings.Join(fieldNames[1:], "."), messageNameToFieldsToJSONName, fieldNameToType)
  2260. }
  2261. func find(a []string, x string) int {
  2262. // This is a linear search but we are dealing with a small number of fields
  2263. for i, n := range a {
  2264. if x == n {
  2265. return i
  2266. }
  2267. }
  2268. return -1
  2269. }