Skip to content

Commit 93c25a7

Browse files
committed
Add initialism-overrides flag (oapi-codegen#1007)
* Add initialism-overrides flag * Add testcase * FIx option
1 parent 1a069b1 commit 93c25a7

7 files changed

Lines changed: 83 additions & 11 deletions

File tree

cmd/oapi-codegen/oapi-codegen.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ var (
5353
flagExcludeSchemas string
5454
flagResponseTypeSuffix string
5555
flagAliasTypes bool
56+
flagInitalismOverrides bool
5657
)
5758

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

102104
flag.Parse()
103105

@@ -396,6 +398,8 @@ func updateConfigFromFlags(cfg *configuration) error {
396398
cfg.OutputFile = flagOutputFile
397399
}
398400

401+
cfg.OutputOptions.InitialismOverrides = flagInitalismOverrides
402+
399403
return nil
400404
}
401405

pkg/codegen/codegen.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ func Generate(spec *openapi3.T, opts Configuration) (string, error) {
138138
}
139139
}
140140

141-
ops, err := OperationDefinitions(spec)
141+
ops, err := OperationDefinitions(spec, opts.OutputOptions.InitialismOverrides)
142142
if err != nil {
143143
return "", fmt.Errorf("error creating operation definitions: %w", err)
144144
}

pkg/codegen/configuration.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,10 @@ type OutputOptions struct {
8484
ExcludeTags []string `yaml:"exclude-tags,omitempty"` // Exclude operations that have one of these tags. Ignored when empty.
8585
UserTemplates map[string]string `yaml:"user-templates,omitempty"` // Override built-in templates from user-provided files
8686

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

9293
// UpdateDefaults sets reasonable default values for unset fields in Configuration

pkg/codegen/operations.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -483,9 +483,16 @@ func FilterParameterDefinitionByType(params []ParameterDefinition, in string) []
483483
}
484484

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

489+
var toCamelCaseFunc func(string) string
490+
if initialismOverrides {
491+
toCamelCaseFunc = ToCamelCaseWithInitialism
492+
} else {
493+
toCamelCaseFunc = ToCamelCase
494+
}
495+
489496
for _, requestPath := range SortedPathsKeys(swagger.Paths) {
490497
pathItem := swagger.Paths[requestPath]
491498
// These are parameters defined for all methods on a given path. They
@@ -505,13 +512,13 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
505512
}
506513
// We rely on OperationID to generate function names, it's required
507514
if op.OperationID == "" {
508-
op.OperationID, err = generateDefaultOperationID(opName, requestPath)
515+
op.OperationID, err = generateDefaultOperationID(opName, requestPath, toCamelCaseFunc)
509516
if err != nil {
510517
return nil, fmt.Errorf("error generating default OperationID for %s/%s: %s",
511518
opName, requestPath, err)
512519
}
513520
} else {
514-
op.OperationID = ToCamelCase(op.OperationID)
521+
op.OperationID = toCamelCaseFunc(op.OperationID)
515522
}
516523
op.OperationID = typeNamePrefix(op.OperationID) + op.OperationID
517524

@@ -550,7 +557,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
550557
HeaderParams: FilterParameterDefinitionByType(allParams, "header"),
551558
QueryParams: FilterParameterDefinitionByType(allParams, "query"),
552559
CookieParams: FilterParameterDefinitionByType(allParams, "cookie"),
553-
OperationId: ToCamelCase(op.OperationID),
560+
OperationId: toCamelCaseFunc(op.OperationID),
554561
// Replace newlines in summary.
555562
Summary: op.Summary,
556563
Method: opName,
@@ -588,7 +595,7 @@ func OperationDefinitions(swagger *openapi3.T) ([]OperationDefinition, error) {
588595
return operations, nil
589596
}
590597

591-
func generateDefaultOperationID(opName string, requestPath string) (string, error) {
598+
func generateDefaultOperationID(opName string, requestPath string, toCamelCaseFunc func(string) string) (string, error) {
592599
var operationId = strings.ToLower(opName)
593600

594601
if opName == "" {
@@ -605,7 +612,7 @@ func generateDefaultOperationID(opName string, requestPath string) (string, erro
605612
}
606613
}
607614

608-
return ToCamelCase(operationId), nil
615+
return toCamelCaseFunc(operationId), nil
609616
}
610617

611618
// GenerateBodyDefinitions turns the Swagger body definitions into a list of our body

pkg/codegen/operations_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ func TestGenerateDefaultOperationID(t *testing.T) {
131131
}
132132

133133
for _, test := range suite {
134-
got, err := generateDefaultOperationID(test.op, test.path)
134+
got, err := generateDefaultOperationID(test.op, test.path, ToCamelCase)
135135
if err != nil {
136136
if !test.wantErr {
137137
t.Fatalf("did not expected error but got %v", err)

pkg/codegen/utils.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,24 @@ func ToCamelCase(str string) string {
160160
return n
161161
}
162162

163+
func ToCamelCaseWithInitialism(str string) string {
164+
return replaceInitialism(ToCamelCase(str))
165+
}
166+
167+
func replaceInitialism(s string) string {
168+
// These strings do not apply CamelCase
169+
// Do not do CamelCase when these characters match when the preceding character is lowercase
170+
// ["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"]
171+
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)`)
172+
return targetWordRegex.ReplaceAllStringFunc(s, func(s string) string {
173+
// If the preceding character is lowercase, do not do CamelCase
174+
if unicode.IsLower(rune(s[0])) {
175+
return s
176+
}
177+
return strings.ToUpper(s)
178+
})
179+
}
180+
163181
// SortedSchemaKeys returns the keys of the given SchemaRef dictionary in sorted
164182
// order, since Golang scrambles dictionary keys
165183
func SortedSchemaKeys(dict map[string]*openapi3.SchemaRef) []string {

pkg/codegen/utils_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -446,3 +446,45 @@ func TestRefPathToObjName(t *testing.T) {
446446
assert.Equal(t, want, RefPathToObjName(in))
447447
}
448448
}
449+
450+
func Test_replaceInitialisms(t *testing.T) {
451+
type args struct {
452+
s string
453+
}
454+
tests := []struct {
455+
name string
456+
args args
457+
want string
458+
}{
459+
{
460+
name: "empty string",
461+
args: args{s: ""},
462+
want: "",
463+
},
464+
{
465+
name: "no initialism",
466+
args: args{s: "foo"},
467+
want: "foo",
468+
},
469+
{
470+
name: "one initialism",
471+
args: args{s: "fooId"},
472+
want: "fooID",
473+
},
474+
{
475+
name: "two initialism",
476+
args: args{s: "fooIdBarApi"},
477+
want: "fooIDBarAPI",
478+
},
479+
{
480+
name: "already initialism",
481+
args: args{s: "fooIDBarAPI"},
482+
want: "fooIDBarAPI",
483+
},
484+
}
485+
for _, tt := range tests {
486+
t.Run(tt.name, func(t *testing.T) {
487+
assert.Equalf(t, tt.want, replaceInitialism(tt.args.s), "replaceInitialism(%v)", tt.args.s)
488+
})
489+
}
490+
}

0 commit comments

Comments
 (0)