registry.go 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702
  1. package descriptor
  2. import (
  3. "fmt"
  4. "strings"
  5. "git.ikuban.com/server/swagger-api/protoc-gen-openapiv2/internal/codegenerator"
  6. "git.ikuban.com/server/swagger-api/protoc-gen-openapiv2/internal/descriptor/openapiconfig"
  7. "github.com/golang/glog"
  8. "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options"
  9. "google.golang.org/genproto/googleapis/api/annotations"
  10. "google.golang.org/protobuf/compiler/protogen"
  11. "google.golang.org/protobuf/types/descriptorpb"
  12. "google.golang.org/protobuf/types/pluginpb"
  13. )
  14. // Registry is a registry of information extracted from pluginpb.CodeGeneratorRequest.
  15. type Registry struct {
  16. // msgs is a mapping from fully-qualified message name to descriptor
  17. msgs map[string]*Message
  18. // enums is a mapping from fully-qualified enum name to descriptor
  19. enums map[string]*Enum
  20. // files is a mapping from file path to descriptor
  21. files map[string]*File
  22. // prefix is a prefix to be inserted to golang package paths generated from proto package names.
  23. prefix string
  24. // pkgMap is a user-specified mapping from file path to proto package.
  25. pkgMap map[string]string
  26. // pkgAliases is a mapping from package aliases to package paths in go which are already taken.
  27. pkgAliases map[string]string
  28. // allowDeleteBody permits http delete methods to have a body
  29. allowDeleteBody bool
  30. // externalHttpRules is a mapping from fully qualified service method names to additional HttpRules applicable besides the ones found in annotations.
  31. externalHTTPRules map[string][]*annotations.HttpRule
  32. // allowMerge generation one OpenAPI file out of multiple protos
  33. allowMerge bool
  34. // mergeFileName target OpenAPI file name after merge
  35. mergeFileName string
  36. // allowRepeatedFieldsInBody permits repeated field in body field path of `google.api.http` annotation option
  37. allowRepeatedFieldsInBody bool
  38. // includePackageInTags controls whether the package name defined in the `package` directive
  39. // in the proto file can be prepended to the gRPC service name in the `Tags` field of every operation.
  40. includePackageInTags bool
  41. // repeatedPathParamSeparator specifies how path parameter repeated fields are separated
  42. repeatedPathParamSeparator repeatedFieldSeparator
  43. // useJSONNamesForFields if true json tag name is used for generating fields in OpenAPI definitions,
  44. // otherwise the original proto name is used. It's helpful for synchronizing the OpenAPI definition
  45. // with gRPC-Gateway response, if it uses json tags for marshaling.
  46. useJSONNamesForFields bool
  47. // useFQNForOpenAPIName if true OpenAPI names will use the full qualified name (FQN) from proto definition,
  48. // and generate a dot-separated OpenAPI name concatenating all elements from the proto FQN.
  49. // If false, the default behavior is to concat the last 2 elements of the FQN if they are unique, otherwise concat
  50. // all the elements of the FQN without any separator
  51. useFQNForOpenAPIName bool
  52. // useGoTemplate determines whether you want to use GO templates
  53. // in your protofile comments
  54. useGoTemplate bool
  55. // enumsAsInts render enum as integer, as opposed to string
  56. enumsAsInts bool
  57. // disableDefaultErrors disables the generation of the default error types.
  58. // This is useful for users who have defined custom error handling.
  59. disableDefaultErrors bool
  60. // simpleOperationIDs removes the service prefix from the generated
  61. // operationIDs. This risks generating duplicate operationIDs.
  62. simpleOperationIDs bool
  63. standalone bool
  64. // warnOnUnboundMethods causes the registry to emit warning logs if an RPC method
  65. // has no HttpRule annotation.
  66. warnOnUnboundMethods bool
  67. // proto3OptionalNullable specifies whether Proto3 Optional fields should be marked as x-nullable.
  68. proto3OptionalNullable bool
  69. // fileOptions is a mapping of file name to additional OpenAPI file options
  70. fileOptions map[string]*options.Swagger
  71. // methodOptions is a mapping of fully-qualified method name to additional OpenAPI method options
  72. methodOptions map[string]*options.Operation
  73. // messageOptions is a mapping of fully-qualified message name to additional OpenAPI message options
  74. messageOptions map[string]*options.Schema
  75. //serviceOptions is a mapping of fully-qualified service name to additional OpenAPI service options
  76. serviceOptions map[string]*options.Tag
  77. // fieldOptions is a mapping of the fully-qualified name of the parent message concat
  78. // field name and a period to additional OpenAPI field options
  79. fieldOptions map[string]*options.JSONSchema
  80. // generateUnboundMethods causes the registry to generate proxy methods even for
  81. // RPC methods that have no HttpRule annotation.
  82. generateUnboundMethods bool
  83. generateRPCMethods bool
  84. // omitPackageDoc, if false, causes a package comment to be included in the generated code.
  85. omitPackageDoc bool
  86. // recursiveDepth sets the maximum depth of a field parameter
  87. recursiveDepth int
  88. }
  89. type repeatedFieldSeparator struct {
  90. name string
  91. sep rune
  92. }
  93. // NewRegistry returns a new Registry.
  94. func NewRegistry() *Registry {
  95. return &Registry{
  96. msgs: make(map[string]*Message),
  97. enums: make(map[string]*Enum),
  98. files: make(map[string]*File),
  99. pkgMap: make(map[string]string),
  100. pkgAliases: make(map[string]string),
  101. externalHTTPRules: make(map[string][]*annotations.HttpRule),
  102. repeatedPathParamSeparator: repeatedFieldSeparator{
  103. name: "csv",
  104. sep: ',',
  105. },
  106. fileOptions: make(map[string]*options.Swagger),
  107. methodOptions: make(map[string]*options.Operation),
  108. messageOptions: make(map[string]*options.Schema),
  109. serviceOptions: make(map[string]*options.Tag),
  110. fieldOptions: make(map[string]*options.JSONSchema),
  111. recursiveDepth: 1000,
  112. }
  113. }
  114. // Load loads definitions of services, methods, messages, enumerations and fields from "req".
  115. func (r *Registry) Load(req *pluginpb.CodeGeneratorRequest) error {
  116. var profofiles []*descriptorpb.FileDescriptorProto
  117. registry := map[string]struct{}{}
  118. for _, f := range req.ProtoFile {
  119. if _, ok := registry[f.GetName()]; !ok {
  120. registry[f.GetName()] = struct{}{}
  121. profofiles = append(profofiles, f)
  122. }
  123. }
  124. req.ProtoFile = profofiles
  125. gen, err := protogen.Options{}.New(req)
  126. if err != nil {
  127. fmt.Println("New err:", err)
  128. return err
  129. }
  130. // Note: keep in mind that this might be not enough because
  131. // protogen.Plugin is used only to load files here.
  132. // The support for features must be set on the pluginpb.CodeGeneratorResponse.
  133. codegenerator.SetSupportedFeaturesOnPluginGen(gen)
  134. return r.load(gen)
  135. }
  136. func (r *Registry) LoadFromPlugin(gen *protogen.Plugin) error {
  137. return r.load(gen)
  138. }
  139. func (r *Registry) load(gen *protogen.Plugin) error {
  140. for filePath, f := range gen.FilesByPath {
  141. r.loadFile(filePath, f)
  142. }
  143. for filePath, f := range gen.FilesByPath {
  144. if !f.Generate {
  145. continue
  146. }
  147. file := r.files[filePath]
  148. if err := r.loadServices(file); err != nil {
  149. return err
  150. }
  151. }
  152. return nil
  153. }
  154. // loadFile loads messages, enumerations and fields from "file".
  155. // It does not loads services and methods in "file". You need to call
  156. // loadServices after loadFiles is called for all files to load services and methods.
  157. func (r *Registry) loadFile(filePath string, file *protogen.File) {
  158. pkg := GoPackage{
  159. Path: string(file.GoImportPath),
  160. Name: string(file.GoPackageName),
  161. }
  162. if r.standalone {
  163. pkg.Alias = "ext" + strings.Title(pkg.Name)
  164. }
  165. if err := r.ReserveGoPackageAlias(pkg.Name, pkg.Path); err != nil {
  166. for i := 0; ; i++ {
  167. alias := fmt.Sprintf("%s_%d", pkg.Name, i)
  168. if err := r.ReserveGoPackageAlias(alias, pkg.Path); err == nil {
  169. pkg.Alias = alias
  170. break
  171. }
  172. }
  173. }
  174. f := &File{
  175. FileDescriptorProto: file.Proto,
  176. GoPkg: pkg,
  177. GeneratedFilenamePrefix: file.GeneratedFilenamePrefix,
  178. }
  179. r.files[filePath] = f
  180. r.registerMsg(f, nil, file.Proto.MessageType)
  181. r.registerEnum(f, nil, file.Proto.EnumType)
  182. }
  183. func (r *Registry) registerMsg(file *File, outerPath []string, msgs []*descriptorpb.DescriptorProto) {
  184. for i, md := range msgs {
  185. m := &Message{
  186. File: file,
  187. Outers: outerPath,
  188. DescriptorProto: md,
  189. Index: i,
  190. ForcePrefixedName: r.standalone,
  191. }
  192. for _, fd := range md.GetField() {
  193. m.Fields = append(m.Fields, &Field{
  194. Message: m,
  195. FieldDescriptorProto: fd,
  196. ForcePrefixedName: r.standalone,
  197. })
  198. }
  199. file.Messages = append(file.Messages, m)
  200. r.msgs[m.FQMN()] = m
  201. glog.V(1).Infof("register name: %s", m.FQMN())
  202. var outers []string
  203. outers = append(outers, outerPath...)
  204. outers = append(outers, m.GetName())
  205. r.registerMsg(file, outers, m.GetNestedType())
  206. r.registerEnum(file, outers, m.GetEnumType())
  207. }
  208. }
  209. func (r *Registry) registerEnum(file *File, outerPath []string, enums []*descriptorpb.EnumDescriptorProto) {
  210. for i, ed := range enums {
  211. e := &Enum{
  212. File: file,
  213. Outers: outerPath,
  214. EnumDescriptorProto: ed,
  215. Index: i,
  216. ForcePrefixedName: r.standalone,
  217. }
  218. file.Enums = append(file.Enums, e)
  219. r.enums[e.FQEN()] = e
  220. glog.V(1).Infof("register enum name: %s", e.FQEN())
  221. }
  222. }
  223. // LookupMsg looks up a message type by "name".
  224. // It tries to resolve "name" from "location" if "name" is a relative message name.
  225. func (r *Registry) LookupMsg(location, name string) (*Message, error) {
  226. glog.V(1).Infof("lookup %s from %s", name, location)
  227. if strings.HasPrefix(name, ".") {
  228. m, ok := r.msgs[name]
  229. if !ok {
  230. return nil, fmt.Errorf("no message found: %s", name)
  231. }
  232. return m, nil
  233. }
  234. if !strings.HasPrefix(location, ".") {
  235. location = fmt.Sprintf(".%s", location)
  236. }
  237. components := strings.Split(location, ".")
  238. for len(components) > 0 {
  239. fqmn := strings.Join(append(components, name), ".")
  240. if m, ok := r.msgs[fqmn]; ok {
  241. return m, nil
  242. }
  243. components = components[:len(components)-1]
  244. }
  245. return nil, fmt.Errorf("no message found: %s", name)
  246. }
  247. // LookupEnum looks up a enum type by "name".
  248. // It tries to resolve "name" from "location" if "name" is a relative enum name.
  249. func (r *Registry) LookupEnum(location, name string) (*Enum, error) {
  250. glog.V(1).Infof("lookup enum %s from %s", name, location)
  251. if strings.HasPrefix(name, ".") {
  252. e, ok := r.enums[name]
  253. if !ok {
  254. return nil, fmt.Errorf("no enum found: %s", name)
  255. }
  256. return e, nil
  257. }
  258. if !strings.HasPrefix(location, ".") {
  259. location = fmt.Sprintf(".%s", location)
  260. }
  261. components := strings.Split(location, ".")
  262. for len(components) > 0 {
  263. fqen := strings.Join(append(components, name), ".")
  264. if e, ok := r.enums[fqen]; ok {
  265. return e, nil
  266. }
  267. components = components[:len(components)-1]
  268. }
  269. return nil, fmt.Errorf("no enum found: %s", name)
  270. }
  271. // LookupFile looks up a file by name.
  272. func (r *Registry) LookupFile(name string) (*File, error) {
  273. f, ok := r.files[name]
  274. if !ok {
  275. return nil, fmt.Errorf("no such file given: %s", name)
  276. }
  277. return f, nil
  278. }
  279. // LookupExternalHTTPRules looks up external http rules by fully qualified service method name
  280. func (r *Registry) LookupExternalHTTPRules(qualifiedMethodName string) []*annotations.HttpRule {
  281. return r.externalHTTPRules[qualifiedMethodName]
  282. }
  283. // AddExternalHTTPRule adds an external http rule for the given fully qualified service method name
  284. func (r *Registry) AddExternalHTTPRule(qualifiedMethodName string, rule *annotations.HttpRule) {
  285. r.externalHTTPRules[qualifiedMethodName] = append(r.externalHTTPRules[qualifiedMethodName], rule)
  286. }
  287. // UnboundExternalHTTPRules returns the list of External HTTPRules
  288. // which does not have a matching method in the registry
  289. func (r *Registry) UnboundExternalHTTPRules() []string {
  290. allServiceMethods := make(map[string]struct{})
  291. for _, f := range r.files {
  292. for _, s := range f.GetService() {
  293. svc := &Service{File: f, ServiceDescriptorProto: s}
  294. for _, m := range s.GetMethod() {
  295. method := &Method{Service: svc, MethodDescriptorProto: m}
  296. allServiceMethods[method.FQMN()] = struct{}{}
  297. }
  298. }
  299. }
  300. var missingMethods []string
  301. for httpRuleMethod := range r.externalHTTPRules {
  302. if _, ok := allServiceMethods[httpRuleMethod]; !ok {
  303. missingMethods = append(missingMethods, httpRuleMethod)
  304. }
  305. }
  306. return missingMethods
  307. }
  308. // AddPkgMap adds a mapping from a .proto file to proto package name.
  309. func (r *Registry) AddPkgMap(file, protoPkg string) {
  310. r.pkgMap[file] = protoPkg
  311. }
  312. // SetPrefix registers the prefix to be added to go package paths generated from proto package names.
  313. func (r *Registry) SetPrefix(prefix string) {
  314. r.prefix = prefix
  315. }
  316. // SetStandalone registers standalone flag to control package prefix
  317. func (r *Registry) SetStandalone(standalone bool) {
  318. r.standalone = standalone
  319. }
  320. // SetRecursiveDepth records the max recursion count
  321. func (r *Registry) SetRecursiveDepth(count int) {
  322. r.recursiveDepth = count
  323. }
  324. // GetRecursiveDepth returns the max recursion count
  325. func (r *Registry) GetRecursiveDepth() int {
  326. return r.recursiveDepth
  327. }
  328. // ReserveGoPackageAlias reserves the unique alias of go package.
  329. // If succeeded, the alias will be never used for other packages in generated go files.
  330. // If failed, the alias is already taken by another package, so you need to use another
  331. // alias for the package in your go files.
  332. func (r *Registry) ReserveGoPackageAlias(alias, pkgpath string) error {
  333. if taken, ok := r.pkgAliases[alias]; ok {
  334. if taken == pkgpath {
  335. return nil
  336. }
  337. return fmt.Errorf("package name %s is already taken. Use another alias", alias)
  338. }
  339. r.pkgAliases[alias] = pkgpath
  340. return nil
  341. }
  342. // GetAllFQMNs returns a list of all FQMNs
  343. func (r *Registry) GetAllFQMNs() []string {
  344. var keys []string
  345. for k := range r.msgs {
  346. keys = append(keys, k)
  347. }
  348. return keys
  349. }
  350. // GetAllFQENs returns a list of all FQENs
  351. func (r *Registry) GetAllFQENs() []string {
  352. var keys []string
  353. for k := range r.enums {
  354. keys = append(keys, k)
  355. }
  356. return keys
  357. }
  358. // SetAllowDeleteBody controls whether http delete methods may have a
  359. // body or fail loading if encountered.
  360. func (r *Registry) SetAllowDeleteBody(allow bool) {
  361. r.allowDeleteBody = allow
  362. }
  363. // SetAllowMerge controls whether generation one OpenAPI file out of multiple protos
  364. func (r *Registry) SetAllowMerge(allow bool) {
  365. r.allowMerge = allow
  366. }
  367. // IsAllowMerge whether generation one OpenAPI file out of multiple protos
  368. func (r *Registry) IsAllowMerge() bool {
  369. return r.allowMerge
  370. }
  371. // SetMergeFileName controls the target OpenAPI file name out of multiple protos
  372. func (r *Registry) SetMergeFileName(mergeFileName string) {
  373. r.mergeFileName = mergeFileName
  374. }
  375. // SetAllowRepeatedFieldsInBody controls whether repeated field can be used
  376. // in `body` and `response_body` (`google.api.http` annotation option) field path or not
  377. func (r *Registry) SetAllowRepeatedFieldsInBody(allow bool) {
  378. r.allowRepeatedFieldsInBody = allow
  379. }
  380. // IsAllowRepeatedFieldsInBody checks if repeated field can be used
  381. // in `body` and `response_body` (`google.api.http` annotation option) field path or not
  382. func (r *Registry) IsAllowRepeatedFieldsInBody() bool {
  383. return r.allowRepeatedFieldsInBody
  384. }
  385. // SetIncludePackageInTags controls whether the package name defined in the `package` directive
  386. // in the proto file can be prepended to the gRPC service name in the `Tags` field of every operation.
  387. func (r *Registry) SetIncludePackageInTags(allow bool) {
  388. r.includePackageInTags = allow
  389. }
  390. // IsIncludePackageInTags checks whether the package name defined in the `package` directive
  391. // in the proto file can be prepended to the gRPC service name in the `Tags` field of every operation.
  392. func (r *Registry) IsIncludePackageInTags() bool {
  393. return r.includePackageInTags
  394. }
  395. // GetRepeatedPathParamSeparator returns a rune spcifying how
  396. // path parameter repeated fields are separated.
  397. func (r *Registry) GetRepeatedPathParamSeparator() rune {
  398. return r.repeatedPathParamSeparator.sep
  399. }
  400. // GetRepeatedPathParamSeparatorName returns the name path parameter repeated
  401. // fields repeatedFieldSeparator. I.e. 'csv', 'pipe', 'ssv' or 'tsv'
  402. func (r *Registry) GetRepeatedPathParamSeparatorName() string {
  403. return r.repeatedPathParamSeparator.name
  404. }
  405. // SetRepeatedPathParamSeparator sets how path parameter repeated fields are
  406. // separated. Allowed names are 'csv', 'pipe', 'ssv' and 'tsv'.
  407. func (r *Registry) SetRepeatedPathParamSeparator(name string) error {
  408. var sep rune
  409. switch name {
  410. case "csv":
  411. sep = ','
  412. case "pipes":
  413. sep = '|'
  414. case "ssv":
  415. sep = ' '
  416. case "tsv":
  417. sep = '\t'
  418. default:
  419. return fmt.Errorf("unknown repeated path parameter separator: %s", name)
  420. }
  421. r.repeatedPathParamSeparator = repeatedFieldSeparator{
  422. name: name,
  423. sep: sep,
  424. }
  425. return nil
  426. }
  427. // SetUseJSONNamesForFields sets useJSONNamesForFields
  428. func (r *Registry) SetUseJSONNamesForFields(use bool) {
  429. r.useJSONNamesForFields = use
  430. }
  431. // GetUseJSONNamesForFields returns useJSONNamesForFields
  432. func (r *Registry) GetUseJSONNamesForFields() bool {
  433. return r.useJSONNamesForFields
  434. }
  435. // SetUseFQNForOpenAPIName sets useFQNForOpenAPIName
  436. func (r *Registry) SetUseFQNForOpenAPIName(use bool) {
  437. r.useFQNForOpenAPIName = use
  438. }
  439. // GetUseFQNForOpenAPIName returns useFQNForOpenAPIName
  440. func (r *Registry) GetUseFQNForOpenAPIName() bool {
  441. return r.useFQNForOpenAPIName
  442. }
  443. // GetMergeFileName return the target merge OpenAPI file name
  444. func (r *Registry) GetMergeFileName() string {
  445. return r.mergeFileName
  446. }
  447. // SetUseGoTemplate sets useGoTemplate
  448. func (r *Registry) SetUseGoTemplate(use bool) {
  449. r.useGoTemplate = use
  450. }
  451. // GetUseGoTemplate returns useGoTemplate
  452. func (r *Registry) GetUseGoTemplate() bool {
  453. return r.useGoTemplate
  454. }
  455. // SetEnumsAsInts set enumsAsInts
  456. func (r *Registry) SetEnumsAsInts(enumsAsInts bool) {
  457. r.enumsAsInts = enumsAsInts
  458. }
  459. // GetEnumsAsInts returns enumsAsInts
  460. func (r *Registry) GetEnumsAsInts() bool {
  461. return r.enumsAsInts
  462. }
  463. // SetDisableDefaultErrors sets disableDefaultErrors
  464. func (r *Registry) SetDisableDefaultErrors(use bool) {
  465. r.disableDefaultErrors = use
  466. }
  467. // GetDisableDefaultErrors returns disableDefaultErrors
  468. func (r *Registry) GetDisableDefaultErrors() bool {
  469. return r.disableDefaultErrors
  470. }
  471. // SetSimpleOperationIDs sets simpleOperationIDs
  472. func (r *Registry) SetSimpleOperationIDs(use bool) {
  473. r.simpleOperationIDs = use
  474. }
  475. // GetSimpleOperationIDs returns simpleOperationIDs
  476. func (r *Registry) GetSimpleOperationIDs() bool {
  477. return r.simpleOperationIDs
  478. }
  479. // SetWarnOnUnboundMethods sets warnOnUnboundMethods
  480. func (r *Registry) SetWarnOnUnboundMethods(warn bool) {
  481. r.warnOnUnboundMethods = warn
  482. }
  483. // SetGenerateUnboundMethods sets generateUnboundMethods
  484. func (r *Registry) SetGenerateUnboundMethods(generate bool) {
  485. r.generateUnboundMethods = generate
  486. }
  487. // SetGenerateRPCMethods sets generateRPCMethods
  488. func (r *Registry) SetGenerateRPCMethods(rpc bool) {
  489. r.generateRPCMethods = rpc
  490. }
  491. // SetOmitPackageDoc controls whether the generated code contains a package comment (if set to false, it will contain one)
  492. func (r *Registry) SetOmitPackageDoc(omit bool) {
  493. r.omitPackageDoc = omit
  494. }
  495. // GetOmitPackageDoc returns whether a package comment will be omitted from the generated code
  496. func (r *Registry) GetOmitPackageDoc() bool {
  497. return r.omitPackageDoc
  498. }
  499. // SetProto3OptionalNullable set proto3OtionalNullable
  500. func (r *Registry) SetProto3OptionalNullable(proto3OtionalNullable bool) {
  501. r.proto3OptionalNullable = proto3OtionalNullable
  502. }
  503. // GetProto3OptionalNullable returns proto3OtionalNullable
  504. func (r *Registry) GetProto3OptionalNullable() bool {
  505. return r.proto3OptionalNullable
  506. }
  507. // RegisterOpenAPIOptions registers OpenAPI options
  508. func (r *Registry) RegisterOpenAPIOptions(opts *openapiconfig.OpenAPIOptions) error {
  509. if opts == nil {
  510. return nil
  511. }
  512. for _, opt := range opts.File {
  513. if _, ok := r.files[opt.File]; !ok {
  514. return fmt.Errorf("no file %s found", opt.File)
  515. }
  516. r.fileOptions[opt.File] = opt.Option
  517. }
  518. // build map of all registered methods
  519. methods := make(map[string]struct{})
  520. services := make(map[string]struct{})
  521. for _, f := range r.files {
  522. for _, s := range f.Services {
  523. services[s.FQSN()] = struct{}{}
  524. for _, m := range s.Methods {
  525. methods[m.FQMN()] = struct{}{}
  526. }
  527. }
  528. }
  529. for _, opt := range opts.Method {
  530. qualifiedMethod := "." + opt.Method
  531. if _, ok := methods[qualifiedMethod]; !ok {
  532. return fmt.Errorf("no method %s found", opt.Method)
  533. }
  534. r.methodOptions[qualifiedMethod] = opt.Option
  535. }
  536. for _, opt := range opts.Message {
  537. qualifiedMessage := "." + opt.Message
  538. if _, ok := r.msgs[qualifiedMessage]; !ok {
  539. return fmt.Errorf("no message %s found", opt.Message)
  540. }
  541. r.messageOptions[qualifiedMessage] = opt.Option
  542. }
  543. for _, opt := range opts.Service {
  544. qualifiedService := "." + opt.Service
  545. if _, ok := services[qualifiedService]; !ok {
  546. return fmt.Errorf("no service %s found", opt.Service)
  547. }
  548. r.serviceOptions[qualifiedService] = opt.Option
  549. }
  550. // build map of all registered fields
  551. fields := make(map[string]struct{})
  552. for _, m := range r.msgs {
  553. for _, f := range m.Fields {
  554. fields[f.FQFN()] = struct{}{}
  555. }
  556. }
  557. for _, opt := range opts.Field {
  558. qualifiedField := "." + opt.Field
  559. if _, ok := fields[qualifiedField]; !ok {
  560. return fmt.Errorf("no field %s found", opt.Field)
  561. }
  562. r.fieldOptions[qualifiedField] = opt.Option
  563. }
  564. return nil
  565. }
  566. // GetOpenAPIFileOption returns a registered OpenAPI option for a file
  567. func (r *Registry) GetOpenAPIFileOption(file string) (*options.Swagger, bool) {
  568. opt, ok := r.fileOptions[file]
  569. return opt, ok
  570. }
  571. // GetOpenAPIMethodOption returns a registered OpenAPI option for a method
  572. func (r *Registry) GetOpenAPIMethodOption(qualifiedMethod string) (*options.Operation, bool) {
  573. opt, ok := r.methodOptions[qualifiedMethod]
  574. return opt, ok
  575. }
  576. // GetOpenAPIMessageOption returns a registered OpenAPI option for a message
  577. func (r *Registry) GetOpenAPIMessageOption(qualifiedMessage string) (*options.Schema, bool) {
  578. opt, ok := r.messageOptions[qualifiedMessage]
  579. return opt, ok
  580. }
  581. // GetOpenAPIServiceOption returns a registered OpenAPI option for a service
  582. func (r *Registry) GetOpenAPIServiceOption(qualifiedService string) (*options.Tag, bool) {
  583. opt, ok := r.serviceOptions[qualifiedService]
  584. return opt, ok
  585. }
  586. // GetOpenAPIFieldOption returns a registered OpenAPI option for a field
  587. func (r *Registry) GetOpenAPIFieldOption(qualifiedField string) (*options.JSONSchema, bool) {
  588. opt, ok := r.fieldOptions[qualifiedField]
  589. return opt, ok
  590. }
  591. func (r *Registry) FieldName(f *Field) string {
  592. if r.useJSONNamesForFields {
  593. return f.GetJsonName()
  594. }
  595. return f.GetName()
  596. }