...

Source file src/github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor/registry.go

Documentation: github.com/grpc-ecosystem/grpc-gateway/v2/internal/descriptor

     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  }
   878  

View as plain text