Skip to content
Closed
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
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,35 @@ which help you to use the various OpenAPI 3 Authentication mechanism.
}
```

- `x-enum-varnames`: supplies other enum names for the corresponding values. (alias: `x-enumNames`)

```yaml
components:
schemas:
Object:
properties:
category:
type: integer
enum: [0, 1, 2]
x-enum-varnames:
- notice
- warning
- urgent
```

After code generation you will get this result:

```go
// Defines values for ObjectCategory.
const (
Notice ObjectCategory = 0
Urgent ObjectCategory = 2
Warning ObjectCategory = 1
)

// ObjectCategory defines model for Object.Category.
type ObjectCategory int
```

## Using `oapi-codegen`

Expand Down
7 changes: 5 additions & 2 deletions pkg/codegen/codegen_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,6 @@ type GetTestByNameResponse struct {
XML422 *[]interface{}
JSONDefault *Error
}`)

// Check that the helper methods are generated correctly:
assert.Contains(t, code, "func (r GetTestByNameResponse) Status() string {")
assert.Contains(t, code, "func (r GetTestByNameResponse) StatusCode() int {")
Expand All @@ -185,7 +184,11 @@ type GetTestByNameResponse struct {
assert.Contains(t, code, "func (c *Client) GetTestByName(ctx context.Context, name string, params *GetTestByNameParams, reqEditors ...RequestEditorFn) (*http.Response, error) {")
assert.Contains(t, code, "func (c *ClientWithResponses) GetTestByNameWithResponse(ctx context.Context, name string, params *GetTestByNameParams, reqEditors ...RequestEditorFn) (*GetTestByNameResponse, error) {")
assert.Contains(t, code, "DeadSince *time.Time `json:\"dead_since,omitempty\" tag1:\"value1\" tag2:\"value2\"`")

assert.Contains(t, code, "type EnumTestNumerics int")
assert.Contains(t, code, "N2 EnumTestNumerics = 2")
assert.Contains(t, code, "type EnumTestEnumNames int")
assert.Contains(t, code, "Two EnumTestEnumNames = 2")
assert.Contains(t, code, "Double EnumTestEnumVarnames = 2")
// Make sure the generated code is valid:
checkLint(t, "test.gen.go", []byte(code))
}
Expand Down
14 changes: 14 additions & 0 deletions pkg/codegen/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ const (
extGoName = "x-go-name"
extPropOmitEmpty = "x-omitempty"
extPropExtraTags = "x-oapi-codegen-extra-tags"
extEnumVarNames = "x-enum-varnames"
extEnumNames = "x-enumNames"
)

func extString(extPropValue interface{}) (string, error) {
Expand Down Expand Up @@ -57,3 +59,15 @@ func extExtraTags(extPropValue interface{}) (map[string]string, error) {
}
return tags, nil
}

func extParseEnumVarNames(extPropValue interface{}) ([]string, error) {
raw, ok := extPropValue.(json.RawMessage)
if !ok {
return nil, fmt.Errorf("failed to convert type: %T", extPropValue)
}
var names []string
if err := json.Unmarshal(raw, &names); err != nil {
return nil, fmt.Errorf("failed to unmarshal json: %w", err)
}
return names, nil
}
15 changes: 14 additions & 1 deletion pkg/codegen/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,21 @@ func GenerateGoSchema(sref *openapi3.SchemaRef, path []string) (Schema, error) {
for i, enumValue := range schema.Enum {
enumValues[i] = fmt.Sprintf("%v", enumValue)
}
if 0 < len(schema.ExtensionProps.Extensions) {
//fmt.Fprintf(os.Stderr, "%#v\n\n", schema)
}
Comment on lines +404 to +406

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove old debugging?

Suggested change
if 0 < len(schema.ExtensionProps.Extensions) {
//fmt.Fprintf(os.Stderr, "%#v\n\n", schema)
}
if 0 < len(schema.ExtensionProps.Extensions) {
//fmt.Fprintf(os.Stderr, "%#v\n\n", schema)
}


enumNames := enumValues
for _, key := range []string{extEnumVarNames, extEnumNames} {
if _, ok := schema.ExtensionProps.Extensions[key]; ok {
if extEnumNames, err := extParseEnumVarNames(schema.ExtensionProps.Extensions[key]); err == nil {
enumNames = extEnumNames

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right now, your extension completely replaces the enum names that are autogenerated. What happens when the provided names array has a different length than possible enum values? I think we'll end up creating invalid enums. Can you, at a minimum, error out on cardinality mismatch?

break
}
}
}

sanitizedValues := SanitizeEnumNames(enumValues)
sanitizedValues := SanitizeEnumNames(enumNames, enumValues)
outSchema.EnumValues = make(map[string]string, len(sanitizedValues))

for k, v := range sanitizedValues {
Expand Down
41 changes: 41 additions & 0 deletions pkg/codegen/test_spec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,27 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/Error'
/enum:
get:
tags:
- enum
summary: References enum
operationId: getEnum
responses:
200:
description: Success
content:
application/json:
schema:
type: array
items:
$ref: '#/components/schemas/EnumTest'
default:
description: Error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'

components:
schemas:
Expand Down Expand Up @@ -136,3 +157,23 @@ components:
cause:
type: string
enum: [car, dog, oldage]

EnumTest:
properties:
numerics:
type: integer
enum: [0, 1, 2]
enumNames:
type: integer
enum: [0, 1, 2]
x-enum-varnames:
- zero
- one
- two
enumVarnames:
type: integer
enum: [0, 1, 2]
x-enum-varnames:
- na
- single
- double
23 changes: 14 additions & 9 deletions pkg/codegen/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -546,27 +546,32 @@ func SanitizeGoIdentity(str string) string {

// SanitizeEnumNames fixes illegal chars in the enum names
// and removes duplicates
func SanitizeEnumNames(enumNames []string) map[string]string {
dupCheck := make(map[string]int, len(enumNames))
deDup := make([]string, 0, len(enumNames))

for _, n := range enumNames {
func SanitizeEnumNames(enumNames, enumValues []string) map[string]string {
dupCheck := make(map[string]int, len(enumValues))
deDup := make([][]string, 0, len(enumValues))

for i, v := range enumValues {
n := v
if i < len(enumNames) {
n = enumNames[i]
}
if _, dup := dupCheck[n]; !dup {
deDup = append(deDup, n)
deDup = append(deDup, []string{n, v})
}
dupCheck[n] = 0
}

dupCheck = make(map[string]int, len(deDup))
sanitizedDeDup := make(map[string]string, len(deDup))

for _, n := range deDup {
for _, p := range deDup {
n, v := p[0], p[1]
sanitized := SanitizeGoIdentity(SchemaNameToTypeName(n))

if _, dup := dupCheck[sanitized]; !dup {
sanitizedDeDup[sanitized] = n
sanitizedDeDup[sanitized] = v
} else {
sanitizedDeDup[sanitized+strconv.Itoa(dupCheck[sanitized])] = n
sanitizedDeDup[sanitized+strconv.Itoa(dupCheck[sanitized])] = v
}
dupCheck[sanitized]++
}
Expand Down