Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions cmd/oapi-codegen/oapi-codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ var (
flagExcludeSchemas string
flagResponseTypeSuffix string
flagAliasTypes bool
flagInitalismOverrides bool
)

type configuration struct {
Expand Down Expand Up @@ -98,6 +99,7 @@ func main() {
flag.StringVar(&flagExcludeSchemas, "exclude-schemas", "", "A comma separated list of schemas which must be excluded from generation")
flag.StringVar(&flagResponseTypeSuffix, "response-type-suffix", "", "the suffix used for responses types")
flag.BoolVar(&flagAliasTypes, "alias-types", false, "Alias type declarations of possible")
flag.BoolVar(&flagInitalismOverrides, "initialism-overrides", false, "Use initialism overrides")

flag.Parse()

Expand Down Expand Up @@ -396,6 +398,8 @@ func updateConfigFromFlags(cfg *configuration) error {
cfg.OutputFile = flagOutputFile
}

cfg.OutputOptions.InitialismOverrides = flagInitalismOverrides

return nil
}

Expand Down
2 changes: 1 addition & 1 deletion pkg/codegen/codegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
}
}

ops, err := OperationDefinitions(spec)
ops, err := OperationDefinitions(spec, opts.OutputOptions.InitialismOverrides)
if err != nil {
return "", fmt.Errorf("error creating operation definitions: %w", err)
}
Expand Down
7 changes: 4 additions & 3 deletions pkg/codegen/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,10 @@ type OutputOptions struct {
ExcludeTags []string `yaml:"exclude-tags,omitempty"` // Exclude operations that have one of these tags. Ignored when empty.
UserTemplates map[string]string `yaml:"user-templates,omitempty"` // Override built-in templates from user-provided files

ExcludeSchemas []string `yaml:"exclude-schemas,omitempty"` // Exclude from generation schemas with given names. Ignored when empty.
ResponseTypeSuffix string `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types
ClientTypeName string `yaml:"client-type-name,omitempty"` // Override the default generated client type with the value
ExcludeSchemas []string `yaml:"exclude-schemas,omitempty"` // Exclude from generation schemas with given names. Ignored when empty.
ResponseTypeSuffix string `yaml:"response-type-suffix,omitempty"` // The suffix used for responses types
ClientTypeName string `yaml:"client-type-name,omitempty"` // Override the default generated client type with the value
InitialismOverrides bool `yaml:"initialism-overrides,omitempty"` // Whether to use the initialism overrides
}

// UpdateDefaults sets reasonable default values for unset fields in Configuration
Expand Down
19 changes: 13 additions & 6 deletions pkg/codegen/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -483,9 +483,16 @@ func FilterParameterDefinitionByType(params []ParameterDefinition, in string) []
}

// OperationDefinitions returns all operations for a swagger definition.
func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
func OperationDefinitions(swagger *openapi3.T, initialismOverrides bool) ([]OperationDefinition, error) {
var operations []OperationDefinition

var toCamelCaseFunc func(string) string
if initialismOverrides {
toCamelCaseFunc = ToCamelCaseWithInitialism
} else {
toCamelCaseFunc = ToCamelCase
}

for _, requestPath := range SortedPathsKeys(swagger.Paths) {
pathItem := swagger.Paths[requestPath]
// These are parameters defined for all methods on a given path. They
Expand All @@ -505,13 +512,13 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
}
// We rely on OperationID to generate function names, it's required
if op.OperationID == "" {
op.OperationID, err = generateDefaultOperationID(opName, requestPath)
op.OperationID, err = generateDefaultOperationID(opName, requestPath, toCamelCaseFunc)
if err != nil {
return nil, fmt.Errorf("error generating default OperationID for %s/%s: %s",
opName, requestPath, err)
}
} else {
op.OperationID = ToCamelCase(op.OperationID)
op.OperationID = toCamelCaseFunc(op.OperationID)
}
op.OperationID = typeNamePrefix(op.OperationID) + op.OperationID

Expand Down Expand Up @@ -550,7 +557,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
HeaderParams: FilterParameterDefinitionByType(allParams, "header"),
QueryParams: FilterParameterDefinitionByType(allParams, "query"),
CookieParams: FilterParameterDefinitionByType(allParams, "cookie"),
OperationId: ToCamelCase(op.OperationID),
OperationId: toCamelCaseFunc(op.OperationID),
// Replace newlines in summary.
Summary: op.Summary,
Method: opName,
Expand Down Expand Up @@ -588,7 +595,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
return operations, nil
}

func generateDefaultOperationID(opName string, requestPath string) (string, error) {
func generateDefaultOperationID(opName string, requestPath string, toCamelCaseFunc func(string) string) (string, error) {
var operationId = strings.ToLower(opName)

if opName == "" {
Expand All @@ -605,7 +612,7 @@ func generateDefaultOperationID(opName string, requestPath string) (string, erro
}
}

return ToCamelCase(operationId), nil
return toCamelCaseFunc(operationId), nil
}

// GenerateBodyDefinitions turns the Swagger body definitions into a list of our body
Expand Down
2 changes: 1 addition & 1 deletion pkg/codegen/operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ func TestGenerateDefaultOperationID(t *testing.T) {
}

for _, test := range suite {
got, err := generateDefaultOperationID(test.op, test.path)
got, err := generateDefaultOperationID(test.op, test.path, ToCamelCase)
if err != nil {
if !test.wantErr {
t.Fatalf("did not expected error but got %v", err)
Expand Down
18 changes: 18 additions & 0 deletions pkg/codegen/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,24 @@ func ToCamelCase(str string) string {
return n
}

func ToCamelCaseWithInitialism(str string) string {
return replaceInitialism(ToCamelCase(str))
}

func replaceInitialism(s string) string {
// These strings do not apply CamelCase
// Do not do CamelCase when these characters match when the preceding character is lowercase
// ["Acl", "Api", "Ascii", "Cpu", "Css", "Dns", "Eof", "Guid", "Html", "Http", "Https", "Id", "Ip", "Json", "Qps", "Ram", "Rpc", "Sla", "Smtp", "Sql", "Ssh", "Tcp", "Tls", "Ttl", "Udp", "Ui", "Gid", "Uid", "Uuid", "Uri", "Url", "Utf8", "Vm", "Xml", "Xmpp", "Xsrf", "Xss", "Sip", "Rtp", "Amqp", "Db", "Ts"]
targetWordRegex := regexp.MustCompile(`(?i)(Acl|Api|Ascii|Cpu|Css|Dns|Eof|Guid|Html|Http|Https|Id|Ip|Json|Qps|Ram|Rpc|Sla|Smtp|Sql|Ssh|Tcp|Tls|Ttl|Udp|Ui|Gid|Uid|Uuid|Uri|Url|Utf8|Vm|Xml|Xmpp|Xsrf|Xss|Sip|Rtp|Amqp|Db|Ts)`)
return targetWordRegex.ReplaceAllStringFunc(s, func(s string) string {
// If the preceding character is lowercase, do not do CamelCase
if unicode.IsLower(rune(s[0])) {
return s
}
return strings.ToUpper(s)
})
}

// SortedSchemaKeys returns the keys of the given SchemaRef dictionary in sorted
// order, since Golang scrambles dictionary keys
func SortedSchemaKeys(dict map[string]*openapi3.SchemaRef) []string {
Expand Down
42 changes: 42 additions & 0 deletions pkg/codegen/utils_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -446,3 +446,45 @@ func TestRefPathToObjName(t *testing.T) {
assert.Equal(t, want, RefPathToObjName(in))
}
}

func Test_replaceInitialisms(t *testing.T) {
type args struct {
s string
}
tests := []struct {
name string
args args
want string
}{
{
name: "empty string",
args: args{s: ""},
want: "",
},
{
name: "no initialism",
args: args{s: "foo"},
want: "foo",
},
{
name: "one initialism",
args: args{s: "fooId"},
want: "fooID",
},
{
name: "two initialism",
args: args{s: "fooIdBarApi"},
want: "fooIDBarAPI",
},
{
name: "already initialism",
args: args{s: "fooIDBarAPI"},
want: "fooIDBarAPI",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, replaceInitialism(tt.args.s), "replaceInitialism(%v)", tt.args.s)
})
}
}