github.com/grpc-ecosystem/grpc-gateway/v2@v2.19.1/internal/descriptor/registry.go (about) 1 package descriptor 2 3 import ( 4 "fmt" 5 "sort" 6 "strings" 7 8 "github.com/grpc-ecosystem/grpc-gateway/v2/internal/codegenerator" 9 "github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/openapiconfig" 10 "github.com/grpc-ecosystem/grpc-gateway/v2/protoc-gen-openapiv2/options" 11 "golang.org/x/text/cases" 12 "golang.org/x/text/language" 13 "google.golang.org/genproto/googleapis/api/annotations" 14 "google.golang.org/grpc/grpclog" 15 "google.golang.org/protobuf/compiler/protogen" 16 "google.golang.org/protobuf/types/descriptorpb" 17 "google.golang.org/protobuf/types/pluginpb" 18 ) 19 20 // Registry is a registry of information extracted from pluginpb.CodeGeneratorRequest. 21 type Registry struct { 22 // msgs is a mapping from fully-qualified message name to descriptor 23 msgs map[string]*Message 24 25 // enums is a mapping from fully-qualified enum name to descriptor 26 enums map[string]*Enum 27 28 // files is a mapping from file path to descriptor 29 files map[string]*File 30 31 // meths is a mapping from fully-qualified method name to descriptor 32 meths map[string]*Method 33 34 // prefix is a prefix to be inserted to golang package paths generated from proto package names. 35 prefix string 36 37 // pkgMap is a user-specified mapping from file path to proto package. 38 pkgMap map[string]string 39 40 // pkgAliases is a mapping from package aliases to package paths in go which are already taken. 41 pkgAliases map[string]string 42 43 // allowDeleteBody permits http delete methods to have a body 44 allowDeleteBody bool 45 46 // externalHttpRules is a mapping from fully qualified service method names to additional HttpRules applicable besides the ones found in annotations. 47 externalHTTPRules map[string][]*annotations.HttpRule 48 49 // allowMerge generation one OpenAPI file out of multiple protos 50 allowMerge bool 51 52 // mergeFileName target OpenAPI file name after merge 53 mergeFileName string 54 55 // includePackageInTags controls whether the package name defined in the `package` directive 56 // in the proto file can be prepended to the gRPC service name in the `Tags` field of every operation. 57 includePackageInTags bool 58 59 // repeatedPathParamSeparator specifies how path parameter repeated fields are separated 60 repeatedPathParamSeparator repeatedFieldSeparator 61 62 // useJSONNamesForFields if true json tag name is used for generating fields in OpenAPI definitions, 63 // otherwise the original proto name is used. It's helpful for synchronizing the OpenAPI definition 64 // with gRPC-Gateway response, if it uses json tags for marshaling. 65 useJSONNamesForFields bool 66 67 // openAPINamingStrategy is the naming strategy to use for assigning OpenAPI field and parameter names. This can be one of the following: 68 // - `legacy`: use the legacy naming strategy from protoc-gen-swagger, that generates unique but not necessarily 69 // maximally concise names. Components are concatenated directly, e.g., `MyOuterMessageMyNestedMessage`. 70 // - `simple`: use a simple heuristic for generating unique and concise names. Components are concatenated using 71 // dots as a separator, e.g., `MyOuterMesage.MyNestedMessage` (if `MyNestedMessage` alone is unique, 72 // `MyNestedMessage` will be used as the OpenAPI name). 73 // - `fqn`: always use the fully-qualified name of the proto message (leading dot removed) as the OpenAPI 74 // name. 75 openAPINamingStrategy string 76 77 // visibilityRestrictionSelectors is a map of selectors for `google.api.VisibilityRule`s that will be included in the OpenAPI output. 78 visibilityRestrictionSelectors map[string]bool 79 80 // useGoTemplate determines whether you want to use GO templates 81 // in your protofile comments 82 useGoTemplate bool 83 84 // goTemplateArgs specifies a list of key value pair inputs to be displayed in Go templates 85 goTemplateArgs map[string]string 86 87 // ignoreComments determines whether all protofile comments should be excluded from output 88 ignoreComments bool 89 90 // removeInternalComments determines whether to remove substrings in comments that begin with 91 // `(--` and end with `--)` as specified in https://google.aip.dev/192#internal-comments. 92 removeInternalComments bool 93 94 // enumsAsInts render enum as integer, as opposed to string 95 enumsAsInts bool 96 97 // omitEnumDefaultValue omits default value of enum 98 omitEnumDefaultValue bool 99 100 // disableDefaultErrors disables the generation of the default error types. 101 // This is useful for users who have defined custom error handling. 102 disableDefaultErrors bool 103 104 // simpleOperationIDs removes the service prefix from the generated 105 // operationIDs. This risks generating duplicate operationIDs. 106 simpleOperationIDs bool 107 108 standalone bool 109 // warnOnUnboundMethods causes the registry to emit warning logs if an RPC method 110 // has no HttpRule annotation. 111 warnOnUnboundMethods bool 112 113 // proto3OptionalNullable specifies whether Proto3 Optional fields should be marked as x-nullable. 114 proto3OptionalNullable bool 115 116 // fileOptions is a mapping of file name to additional OpenAPI file options 117 fileOptions map[string]*options.Swagger 118 119 // methodOptions is a mapping of fully-qualified method name to additional OpenAPI method options 120 methodOptions map[string]*options.Operation 121 122 // messageOptions is a mapping of fully-qualified message name to additional OpenAPI message options 123 messageOptions map[string]*options.Schema 124 125 //serviceOptions is a mapping of fully-qualified service name to additional OpenAPI service options 126 serviceOptions map[string]*options.Tag 127 128 // fieldOptions is a mapping of the fully-qualified name of the parent message concat 129 // field name and a period to additional OpenAPI field options 130 fieldOptions map[string]*options.JSONSchema 131 132 // generateUnboundMethods causes the registry to generate proxy methods even for 133 // RPC methods that have no HttpRule annotation. 134 generateUnboundMethods bool 135 136 // omitPackageDoc, if false, causes a package comment to be included in the generated code. 137 omitPackageDoc bool 138 139 // recursiveDepth sets the maximum depth of a field parameter 140 recursiveDepth int 141 142 // annotationMap is used to check for duplicate HTTP annotations 143 annotationMap map[annotationIdentifier]struct{} 144 145 // disableServiceTags disables the generation of service tags. 146 // This is useful if you do not want to expose the names of your backend grpc services. 147 disableServiceTags bool 148 149 // disableDefaultResponses disables the generation of default responses. 150 // Useful if you have to support custom response codes that are not 200. 151 disableDefaultResponses bool 152 153 // useAllOfForRefs, if set, will use allOf as container for $ref to preserve same-level 154 // properties 155 useAllOfForRefs bool 156 157 // allowPatchFeature determines whether to use PATCH feature involving update masks (using google.protobuf.FieldMask). 158 allowPatchFeature bool 159 160 // preserveRPCOrder, if true, will ensure the order of paths emitted in openapi swagger files mirror 161 // the order of RPC methods found in proto files. If false, emitted paths will be ordered alphabetically. 162 preserveRPCOrder bool 163 } 164 165 type repeatedFieldSeparator struct { 166 name string 167 sep rune 168 } 169 170 type annotationIdentifier struct { 171 method string 172 pathTemplate string 173 service *Service 174 } 175 176 // NewRegistry returns a new Registry. 177 func NewRegistry() *Registry { 178 return &Registry{ 179 msgs: make(map[string]*Message), 180 enums: make(map[string]*Enum), 181 meths: make(map[string]*Method), 182 files: make(map[string]*File), 183 pkgMap: make(map[string]string), 184 pkgAliases: make(map[string]string), 185 externalHTTPRules: make(map[string][]*annotations.HttpRule), 186 openAPINamingStrategy: "legacy", 187 visibilityRestrictionSelectors: make(map[string]bool), 188 repeatedPathParamSeparator: repeatedFieldSeparator{ 189 name: "csv", 190 sep: ',', 191 }, 192 fileOptions: make(map[string]*options.Swagger), 193 methodOptions: make(map[string]*options.Operation), 194 messageOptions: make(map[string]*options.Schema), 195 serviceOptions: make(map[string]*options.Tag), 196 fieldOptions: make(map[string]*options.JSONSchema), 197 annotationMap: make(map[annotationIdentifier]struct{}), 198 recursiveDepth: 1000, 199 } 200 } 201 202 // Load loads definitions of services, methods, messages, enumerations and fields from "req". 203 func (r *Registry) Load(req *pluginpb.CodeGeneratorRequest) error { 204 gen, err := protogen.Options{}.New(req) 205 if err != nil { 206 return err 207 } 208 // Note: keep in mind that this might be not enough because 209 // protogen.Plugin is used only to load files here. 210 // The support for features must be set on the pluginpb.CodeGeneratorResponse. 211 codegenerator.SetSupportedFeaturesOnPluginGen(gen) 212 return r.load(gen) 213 } 214 215 func (r *Registry) LoadFromPlugin(gen *protogen.Plugin) error { 216 return r.load(gen) 217 } 218 219 func (r *Registry) load(gen *protogen.Plugin) error { 220 filePaths := make([]string, 0, len(gen.FilesByPath)) 221 for filePath := range gen.FilesByPath { 222 filePaths = append(filePaths, filePath) 223 } 224 sort.Strings(filePaths) 225 226 for _, filePath := range filePaths { 227 r.loadFile(filePath, gen.FilesByPath[filePath]) 228 } 229 230 for _, filePath := range filePaths { 231 if !gen.FilesByPath[filePath].Generate { 232 continue 233 } 234 file := r.files[filePath] 235 if err := r.loadServices(file); err != nil { 236 return err 237 } 238 } 239 240 return nil 241 } 242 243 // loadFile loads messages, enumerations and fields from "file". 244 // It does not loads services and methods in "file". You need to call 245 // loadServices after loadFiles is called for all files to load services and methods. 246 func (r *Registry) loadFile(filePath string, file *protogen.File) { 247 pkg := GoPackage{ 248 Path: string(file.GoImportPath), 249 Name: string(file.GoPackageName), 250 } 251 if r.standalone { 252 pkg.Alias = "ext" + cases.Title(language.AmericanEnglish).String(pkg.Name) 253 } 254 255 if err := r.ReserveGoPackageAlias(pkg.Name, pkg.Path); err != nil { 256 for i := 0; ; i++ { 257 alias := fmt.Sprintf("%s_%d", pkg.Name, i) 258 if err := r.ReserveGoPackageAlias(alias, pkg.Path); err == nil { 259 pkg.Alias = alias 260 break 261 } 262 } 263 } 264 f := &File{ 265 FileDescriptorProto: file.Proto, 266 GoPkg: pkg, 267 GeneratedFilenamePrefix: file.GeneratedFilenamePrefix, 268 } 269 270 r.files[filePath] = f 271 r.registerMsg(f, nil, file.Proto.MessageType) 272 r.registerEnum(f, nil, file.Proto.EnumType) 273 } 274 275 func (r *Registry) registerMsg(file *File, outerPath []string, msgs []*descriptorpb.DescriptorProto) { 276 for i, md := range msgs { 277 m := &Message{ 278 File: file, 279 Outers: outerPath, 280 DescriptorProto: md, 281 Index: i, 282 ForcePrefixedName: r.standalone, 283 } 284 for _, fd := range md.GetField() { 285 m.Fields = append(m.Fields, &Field{ 286 Message: m, 287 FieldDescriptorProto: fd, 288 ForcePrefixedName: r.standalone, 289 }) 290 } 291 file.Messages = append(file.Messages, m) 292 r.msgs[m.FQMN()] = m 293 if grpclog.V(1) { 294 grpclog.Infof("Register name: %s", m.FQMN()) 295 } 296 297 var outers []string 298 outers = append(outers, outerPath...) 299 outers = append(outers, m.GetName()) 300 r.registerMsg(file, outers, m.GetNestedType()) 301 r.registerEnum(file, outers, m.GetEnumType()) 302 } 303 } 304 305 func (r *Registry) registerEnum(file *File, outerPath []string, enums []*descriptorpb.EnumDescriptorProto) { 306 for i, ed := range enums { 307 e := &Enum{ 308 File: file, 309 Outers: outerPath, 310 EnumDescriptorProto: ed, 311 Index: i, 312 ForcePrefixedName: r.standalone, 313 } 314 file.Enums = append(file.Enums, e) 315 r.enums[e.FQEN()] = e 316 if grpclog.V(1) { 317 grpclog.Infof("Register enum name: %s", e.FQEN()) 318 } 319 } 320 } 321 322 // LookupMsg looks up a message type by "name". 323 // It tries to resolve "name" from "location" if "name" is a relative message name. 324 func (r *Registry) LookupMsg(location, name string) (*Message, error) { 325 if grpclog.V(1) { 326 grpclog.Infof("Lookup %s from %s", name, location) 327 } 328 if strings.HasPrefix(name, ".") { 329 m, ok := r.msgs[name] 330 if !ok { 331 return nil, fmt.Errorf("no message found: %s", name) 332 } 333 return m, nil 334 } 335 336 if !strings.HasPrefix(location, ".") { 337 location = fmt.Sprintf(".%s", location) 338 } 339 components := strings.Split(location, ".") 340 for len(components) > 0 { 341 fqmn := strings.Join(append(components, name), ".") 342 if m, ok := r.msgs[fqmn]; ok { 343 return m, nil 344 } 345 components = components[:len(components)-1] 346 } 347 return nil, fmt.Errorf("no message found: %s", name) 348 } 349 350 // LookupEnum looks up a enum type by "name". 351 // It tries to resolve "name" from "location" if "name" is a relative enum name. 352 func (r *Registry) LookupEnum(location, name string) (*Enum, error) { 353 if grpclog.V(1) { 354 grpclog.Infof("Lookup enum %s from %s", name, location) 355 } 356 if strings.HasPrefix(name, ".") { 357 e, ok := r.enums[name] 358 if !ok { 359 return nil, fmt.Errorf("no enum found: %s", name) 360 } 361 return e, nil 362 } 363 364 if !strings.HasPrefix(location, ".") { 365 location = fmt.Sprintf(".%s", location) 366 } 367 components := strings.Split(location, ".") 368 for len(components) > 0 { 369 fqen := strings.Join(append(components, name), ".") 370 if e, ok := r.enums[fqen]; ok { 371 return e, nil 372 } 373 components = components[:len(components)-1] 374 } 375 return nil, fmt.Errorf("no enum found: %s", name) 376 } 377 378 // LookupFile looks up a file by name. 379 func (r *Registry) LookupFile(name string) (*File, error) { 380 f, ok := r.files[name] 381 if !ok { 382 return nil, fmt.Errorf("no such file given: %s", name) 383 } 384 return f, nil 385 } 386 387 // LookupExternalHTTPRules looks up external http rules by fully qualified service method name 388 func (r *Registry) LookupExternalHTTPRules(qualifiedMethodName string) []*annotations.HttpRule { 389 return r.externalHTTPRules[qualifiedMethodName] 390 } 391 392 // AddExternalHTTPRule adds an external http rule for the given fully qualified service method name 393 func (r *Registry) AddExternalHTTPRule(qualifiedMethodName string, rule *annotations.HttpRule) { 394 r.externalHTTPRules[qualifiedMethodName] = append(r.externalHTTPRules[qualifiedMethodName], rule) 395 } 396 397 // UnboundExternalHTTPRules returns the list of External HTTPRules 398 // which does not have a matching method in the registry 399 func (r *Registry) UnboundExternalHTTPRules() []string { 400 allServiceMethods := make(map[string]struct{}) 401 for _, f := range r.files { 402 for _, s := range f.GetService() { 403 svc := &Service{File: f, ServiceDescriptorProto: s} 404 for _, m := range s.GetMethod() { 405 method := &Method{Service: svc, MethodDescriptorProto: m} 406 allServiceMethods[method.FQMN()] = struct{}{} 407 } 408 } 409 } 410 411 var missingMethods []string 412 for httpRuleMethod := range r.externalHTTPRules { 413 if _, ok := allServiceMethods[httpRuleMethod]; !ok { 414 missingMethods = append(missingMethods, httpRuleMethod) 415 } 416 } 417 return missingMethods 418 } 419 420 // AddPkgMap adds a mapping from a .proto file to proto package name. 421 func (r *Registry) AddPkgMap(file, protoPkg string) { 422 r.pkgMap[file] = protoPkg 423 } 424 425 // SetPrefix registers the prefix to be added to go package paths generated from proto package names. 426 func (r *Registry) SetPrefix(prefix string) { 427 r.prefix = prefix 428 } 429 430 // SetStandalone registers standalone flag to control package prefix 431 func (r *Registry) SetStandalone(standalone bool) { 432 r.standalone = standalone 433 } 434 435 // SetRecursiveDepth records the max recursion count 436 func (r *Registry) SetRecursiveDepth(count int) { 437 r.recursiveDepth = count 438 } 439 440 // GetRecursiveDepth returns the max recursion count 441 func (r *Registry) GetRecursiveDepth() int { 442 return r.recursiveDepth 443 } 444 445 // ReserveGoPackageAlias reserves the unique alias of go package. 446 // If succeeded, the alias will be never used for other packages in generated go files. 447 // If failed, the alias is already taken by another package, so you need to use another 448 // alias for the package in your go files. 449 func (r *Registry) ReserveGoPackageAlias(alias, pkgpath string) error { 450 if taken, ok := r.pkgAliases[alias]; ok { 451 if taken == pkgpath { 452 return nil 453 } 454 return fmt.Errorf("package name %s is already taken. Use another alias", alias) 455 } 456 r.pkgAliases[alias] = pkgpath 457 return nil 458 } 459 460 // GetAllFQMNs returns a list of all FQMNs 461 func (r *Registry) GetAllFQMNs() []string { 462 keys := make([]string, 0, len(r.msgs)) 463 for k := range r.msgs { 464 keys = append(keys, k) 465 } 466 return keys 467 } 468 469 // GetAllFQENs returns a list of all FQENs 470 func (r *Registry) GetAllFQENs() []string { 471 keys := make([]string, 0, len(r.enums)) 472 for k := range r.enums { 473 keys = append(keys, k) 474 } 475 return keys 476 } 477 478 func (r *Registry) GetAllFQMethNs() []string { 479 keys := make([]string, 0, len(r.meths)) 480 for k := range r.meths { 481 keys = append(keys, k) 482 } 483 return keys 484 } 485 486 // SetAllowDeleteBody controls whether http delete methods may have a 487 // body or fail loading if encountered. 488 func (r *Registry) SetAllowDeleteBody(allow bool) { 489 r.allowDeleteBody = allow 490 } 491 492 // SetAllowMerge controls whether generation one OpenAPI file out of multiple protos 493 func (r *Registry) SetAllowMerge(allow bool) { 494 r.allowMerge = allow 495 } 496 497 // IsAllowMerge whether generation one OpenAPI file out of multiple protos 498 func (r *Registry) IsAllowMerge() bool { 499 return r.allowMerge 500 } 501 502 // SetMergeFileName controls the target OpenAPI file name out of multiple protos 503 func (r *Registry) SetMergeFileName(mergeFileName string) { 504 r.mergeFileName = mergeFileName 505 } 506 507 // SetIncludePackageInTags controls whether the package name defined in the `package` directive 508 // in the proto file can be prepended to the gRPC service name in the `Tags` field of every operation. 509 func (r *Registry) SetIncludePackageInTags(allow bool) { 510 r.includePackageInTags = allow 511 } 512 513 // IsIncludePackageInTags checks whether the package name defined in the `package` directive 514 // in the proto file can be prepended to the gRPC service name in the `Tags` field of every operation. 515 func (r *Registry) IsIncludePackageInTags() bool { 516 return r.includePackageInTags 517 } 518 519 // GetRepeatedPathParamSeparator returns a rune spcifying how 520 // path parameter repeated fields are separated. 521 func (r *Registry) GetRepeatedPathParamSeparator() rune { 522 return r.repeatedPathParamSeparator.sep 523 } 524 525 // GetRepeatedPathParamSeparatorName returns the name path parameter repeated 526 // fields repeatedFieldSeparator. I.e. 'csv', 'pipe', 'ssv' or 'tsv' 527 func (r *Registry) GetRepeatedPathParamSeparatorName() string { 528 return r.repeatedPathParamSeparator.name 529 } 530 531 // SetRepeatedPathParamSeparator sets how path parameter repeated fields are 532 // separated. Allowed names are 'csv', 'pipe', 'ssv' and 'tsv'. 533 func (r *Registry) SetRepeatedPathParamSeparator(name string) error { 534 var sep rune 535 switch name { 536 case "csv": 537 sep = ',' 538 case "pipes": 539 sep = '|' 540 case "ssv": 541 sep = ' ' 542 case "tsv": 543 sep = '\t' 544 default: 545 return fmt.Errorf("unknown repeated path parameter separator: %s", name) 546 } 547 r.repeatedPathParamSeparator = repeatedFieldSeparator{ 548 name: name, 549 sep: sep, 550 } 551 return nil 552 } 553 554 // SetUseJSONNamesForFields sets useJSONNamesForFields 555 func (r *Registry) SetUseJSONNamesForFields(use bool) { 556 r.useJSONNamesForFields = use 557 } 558 559 // GetUseJSONNamesForFields returns useJSONNamesForFields 560 func (r *Registry) GetUseJSONNamesForFields() bool { 561 return r.useJSONNamesForFields 562 } 563 564 // SetUseFQNForOpenAPIName sets useFQNForOpenAPIName 565 // Deprecated: use SetOpenAPINamingStrategy instead. 566 func (r *Registry) SetUseFQNForOpenAPIName(use bool) { 567 r.openAPINamingStrategy = "fqn" 568 } 569 570 // GetUseFQNForOpenAPIName returns useFQNForOpenAPIName 571 // Deprecated: Use GetOpenAPINamingStrategy(). 572 func (r *Registry) GetUseFQNForOpenAPIName() bool { 573 return r.openAPINamingStrategy == "fqn" 574 } 575 576 // GetMergeFileName return the target merge OpenAPI file name 577 func (r *Registry) GetMergeFileName() string { 578 return r.mergeFileName 579 } 580 581 // SetOpenAPINamingStrategy sets the naming strategy to be used. 582 func (r *Registry) SetOpenAPINamingStrategy(strategy string) { 583 r.openAPINamingStrategy = strategy 584 } 585 586 // GetOpenAPINamingStrategy retrieves the naming strategy that is in use. 587 func (r *Registry) GetOpenAPINamingStrategy() string { 588 return r.openAPINamingStrategy 589 } 590 591 // SetUseGoTemplate sets useGoTemplate 592 func (r *Registry) SetUseGoTemplate(use bool) { 593 r.useGoTemplate = use 594 } 595 596 // GetUseGoTemplate returns useGoTemplate 597 func (r *Registry) GetUseGoTemplate() bool { 598 return r.useGoTemplate 599 } 600 601 func (r *Registry) SetGoTemplateArgs(kvs []string) { 602 r.goTemplateArgs = make(map[string]string) 603 for _, kv := range kvs { 604 if key, value, found := strings.Cut(kv, "="); found { 605 r.goTemplateArgs[key] = value 606 } 607 } 608 } 609 610 func (r *Registry) GetGoTemplateArgs() map[string]string { 611 return r.goTemplateArgs 612 } 613 614 // SetIgnoreComments sets ignoreComments 615 func (r *Registry) SetIgnoreComments(ignore bool) { 616 r.ignoreComments = ignore 617 } 618 619 // GetIgnoreComments returns ignoreComments 620 func (r *Registry) GetIgnoreComments() bool { 621 return r.ignoreComments 622 } 623 624 // SetRemoveInternalComments sets removeInternalComments 625 func (r *Registry) SetRemoveInternalComments(remove bool) { 626 r.removeInternalComments = remove 627 } 628 629 // GetRemoveInternalComments returns removeInternalComments 630 func (r *Registry) GetRemoveInternalComments() bool { 631 return r.removeInternalComments 632 } 633 634 // SetEnumsAsInts set enumsAsInts 635 func (r *Registry) SetEnumsAsInts(enumsAsInts bool) { 636 r.enumsAsInts = enumsAsInts 637 } 638 639 // GetEnumsAsInts returns enumsAsInts 640 func (r *Registry) GetEnumsAsInts() bool { 641 return r.enumsAsInts 642 } 643 644 // SetOmitEnumDefaultValue sets omitEnumDefaultValue 645 func (r *Registry) SetOmitEnumDefaultValue(omit bool) { 646 r.omitEnumDefaultValue = omit 647 } 648 649 // GetOmitEnumDefaultValue returns omitEnumDefaultValue 650 func (r *Registry) GetOmitEnumDefaultValue() bool { 651 return r.omitEnumDefaultValue 652 } 653 654 // SetVisibilityRestrictionSelectors sets the visibility restriction selectors. 655 func (r *Registry) SetVisibilityRestrictionSelectors(selectors []string) { 656 r.visibilityRestrictionSelectors = make(map[string]bool) 657 for _, selector := range selectors { 658 r.visibilityRestrictionSelectors[strings.TrimSpace(selector)] = true 659 } 660 } 661 662 // GetVisibilityRestrictionSelectors retrieves he visibility restriction selectors. 663 func (r *Registry) GetVisibilityRestrictionSelectors() map[string]bool { 664 return r.visibilityRestrictionSelectors 665 } 666 667 // SetDisableDefaultErrors sets disableDefaultErrors 668 func (r *Registry) SetDisableDefaultErrors(use bool) { 669 r.disableDefaultErrors = use 670 } 671 672 // GetDisableDefaultErrors returns disableDefaultErrors 673 func (r *Registry) GetDisableDefaultErrors() bool { 674 return r.disableDefaultErrors 675 } 676 677 // SetSimpleOperationIDs sets simpleOperationIDs 678 func (r *Registry) SetSimpleOperationIDs(use bool) { 679 r.simpleOperationIDs = use 680 } 681 682 // GetSimpleOperationIDs returns simpleOperationIDs 683 func (r *Registry) GetSimpleOperationIDs() bool { 684 return r.simpleOperationIDs 685 } 686 687 // SetWarnOnUnboundMethods sets warnOnUnboundMethods 688 func (r *Registry) SetWarnOnUnboundMethods(warn bool) { 689 r.warnOnUnboundMethods = warn 690 } 691 692 // SetGenerateUnboundMethods sets generateUnboundMethods 693 func (r *Registry) SetGenerateUnboundMethods(generate bool) { 694 r.generateUnboundMethods = generate 695 } 696 697 // SetOmitPackageDoc controls whether the generated code contains a package comment (if set to false, it will contain one) 698 func (r *Registry) SetOmitPackageDoc(omit bool) { 699 r.omitPackageDoc = omit 700 } 701 702 // GetOmitPackageDoc returns whether a package comment will be omitted from the generated code 703 func (r *Registry) GetOmitPackageDoc() bool { 704 return r.omitPackageDoc 705 } 706 707 // SetProto3OptionalNullable set proto3OtionalNullable 708 func (r *Registry) SetProto3OptionalNullable(proto3OtionalNullable bool) { 709 r.proto3OptionalNullable = proto3OtionalNullable 710 } 711 712 // GetProto3OptionalNullable returns proto3OtionalNullable 713 func (r *Registry) GetProto3OptionalNullable() bool { 714 return r.proto3OptionalNullable 715 } 716 717 // RegisterOpenAPIOptions registers OpenAPI options 718 func (r *Registry) RegisterOpenAPIOptions(opts *openapiconfig.OpenAPIOptions) error { 719 if opts == nil { 720 return nil 721 } 722 723 for _, opt := range opts.File { 724 if _, ok := r.files[opt.File]; !ok { 725 return fmt.Errorf("no file %s found", opt.File) 726 } 727 r.fileOptions[opt.File] = opt.Option 728 } 729 730 // build map of all registered methods 731 methods := make(map[string]struct{}) 732 services := make(map[string]struct{}) 733 for _, f := range r.files { 734 for _, s := range f.Services { 735 services[s.FQSN()] = struct{}{} 736 for _, m := range s.Methods { 737 methods[m.FQMN()] = struct{}{} 738 } 739 } 740 } 741 742 for _, opt := range opts.Method { 743 qualifiedMethod := "." + opt.Method 744 if _, ok := methods[qualifiedMethod]; !ok { 745 return fmt.Errorf("no method %s found", opt.Method) 746 } 747 r.methodOptions[qualifiedMethod] = opt.Option 748 } 749 750 for _, opt := range opts.Message { 751 qualifiedMessage := "." + opt.Message 752 if _, ok := r.msgs[qualifiedMessage]; !ok { 753 return fmt.Errorf("no message %s found", opt.Message) 754 } 755 r.messageOptions[qualifiedMessage] = opt.Option 756 } 757 758 for _, opt := range opts.Service { 759 qualifiedService := "." + opt.Service 760 if _, ok := services[qualifiedService]; !ok { 761 return fmt.Errorf("no service %s found", opt.Service) 762 } 763 r.serviceOptions[qualifiedService] = opt.Option 764 } 765 766 // build map of all registered fields 767 fields := make(map[string]struct{}) 768 for _, m := range r.msgs { 769 for _, f := range m.Fields { 770 fields[f.FQFN()] = struct{}{} 771 } 772 } 773 for _, opt := range opts.Field { 774 qualifiedField := "." + opt.Field 775 if _, ok := fields[qualifiedField]; !ok { 776 return fmt.Errorf("no field %s found", opt.Field) 777 } 778 r.fieldOptions[qualifiedField] = opt.Option 779 } 780 return nil 781 } 782 783 // GetOpenAPIFileOption returns a registered OpenAPI option for a file 784 func (r *Registry) GetOpenAPIFileOption(file string) (*options.Swagger, bool) { 785 opt, ok := r.fileOptions[file] 786 return opt, ok 787 } 788 789 // GetOpenAPIMethodOption returns a registered OpenAPI option for a method 790 func (r *Registry) GetOpenAPIMethodOption(qualifiedMethod string) (*options.Operation, bool) { 791 opt, ok := r.methodOptions[qualifiedMethod] 792 return opt, ok 793 } 794 795 // GetOpenAPIMessageOption returns a registered OpenAPI option for a message 796 func (r *Registry) GetOpenAPIMessageOption(qualifiedMessage string) (*options.Schema, bool) { 797 opt, ok := r.messageOptions[qualifiedMessage] 798 return opt, ok 799 } 800 801 // GetOpenAPIServiceOption returns a registered OpenAPI option for a service 802 func (r *Registry) GetOpenAPIServiceOption(qualifiedService string) (*options.Tag, bool) { 803 opt, ok := r.serviceOptions[qualifiedService] 804 return opt, ok 805 } 806 807 // GetOpenAPIFieldOption returns a registered OpenAPI option for a field 808 func (r *Registry) GetOpenAPIFieldOption(qualifiedField string) (*options.JSONSchema, bool) { 809 opt, ok := r.fieldOptions[qualifiedField] 810 return opt, ok 811 } 812 813 func (r *Registry) FieldName(f *Field) string { 814 if r.useJSONNamesForFields { 815 return f.GetJsonName() 816 } 817 return f.GetName() 818 } 819 820 func (r *Registry) CheckDuplicateAnnotation(httpMethod string, httpTemplate string, svc *Service) error { 821 a := annotationIdentifier{method: httpMethod, pathTemplate: httpTemplate, service: svc} 822 if _, ok := r.annotationMap[a]; ok { 823 return fmt.Errorf("duplicate annotation: method=%s, template=%s", httpMethod, httpTemplate) 824 } 825 r.annotationMap[a] = struct{}{} 826 return nil 827 } 828 829 // SetDisableServiceTags sets disableServiceTags 830 func (r *Registry) SetDisableServiceTags(use bool) { 831 r.disableServiceTags = use 832 } 833 834 // GetDisableServiceTags returns disableServiceTags 835 func (r *Registry) GetDisableServiceTags() bool { 836 return r.disableServiceTags 837 } 838 839 // SetDisableDefaultResponses setsdisableDefaultResponses 840 func (r *Registry) SetDisableDefaultResponses(use bool) { 841 r.disableDefaultResponses = use 842 } 843 844 // GetDisableDefaultResponses returns disableDefaultResponses 845 func (r *Registry) GetDisableDefaultResponses() bool { 846 return r.disableDefaultResponses 847 } 848 849 // SetUseAllOfForRefs sets useAllOfForRefs 850 func (r *Registry) SetUseAllOfForRefs(use bool) { 851 r.useAllOfForRefs = use 852 } 853 854 // GetUseAllOfForRefs returns useAllOfForRefs 855 func (r *Registry) GetUseAllOfForRefs() bool { 856 return r.useAllOfForRefs 857 } 858 859 // SetAllowPatchFeature sets allowPatchFeature 860 func (r *Registry) SetAllowPatchFeature(allow bool) { 861 r.allowPatchFeature = allow 862 } 863 864 // GetAllowPatchFeature returns allowPatchFeature 865 func (r *Registry) GetAllowPatchFeature() bool { 866 return r.allowPatchFeature 867 } 868 869 // SetPreserveRPCOrder sets preserveRPCOrder 870 func (r *Registry) SetPreserveRPCOrder(preserve bool) { 871 r.preserveRPCOrder = preserve 872 } 873 874 // IsPreserveRPCOrder returns preserveRPCOrder 875 func (r *Registry) IsPreserveRPCOrder() bool { 876 return r.preserveRPCOrder 877 }