Skip to content
Open
Show file tree
Hide file tree
Changes from 2 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
26 changes: 13 additions & 13 deletions tools/goctl/api/swagger/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,23 +62,23 @@ func rangeValueFromOptions(options []string) (minimum *float64, maximum *float64
}

func enumsValueFromOptions(options []string) []any {
if len(options) == 0 {
return []any{}
}
var enums []any
for _, option := range options {
if strings.HasPrefix(option, enumFlag) {
var resp = make([]any, 0)
val := option[8:]
fields := util.FieldsAndTrimSpace(val, func(r rune) bool {
return r == '|'
})
for _, field := range fields {
resp = append(resp, field)
if strings.HasPrefix(option, "options=") {
Copy link

Copilot AI Jun 29, 2025

Choose a reason for hiding this comment

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

[nitpick] Hardcoding the prefix "options=" instead of using the existing enumFlag constant may cause inconsistency. Consider using enumFlag for matching the enum option.

Copilot uses AI. Check for mistakes.
optionValue := strings.TrimPrefix(option, "options=")
for _, enum := range strings.Split(optionValue, "|") {
if enum == "true" {
enums = append(enums, true)
} else if enum == "false" {
enums = append(enums, false)
} else {
Copy link
Collaborator

Choose a reason for hiding this comment

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

else if !stringx.IsWhiteSpace(enum)?

enums = append(enums, enum)
}
}
return resp
break
}
}
return []any{}
return enums
}

func defValueFromOptions(ctx Context, options []string, apiType spec.Type) any {
Expand Down
2 changes: 1 addition & 1 deletion tools/goctl/api/swagger/parameter.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

func isPostJson(ctx Context, method string, tp apiSpec.Type) (string, bool) {
if strings.EqualFold(method, http.MethodPost) {
if !strings.EqualFold(method, http.MethodPost) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

duplicate with #4997

return "", false
}
structType, ok := tp.(apiSpec.DefineStruct)
Expand Down
105 changes: 99 additions & 6 deletions tools/goctl/api/swagger/properties.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,14 @@ func propertiesFromType(ctx Context, tp apiSpec.Type) (spec.SchemaProperties, []
requiredFields = append(requiredFields, jsonTagString)
}

if ctx.UseDefinitions {
schema := buildSchemaWithDefinitions(ctx, member.Type, example, member.Comment, minimum, maximum, exclusiveMinimum, exclusiveMaximum, defaultValue, enum)
if schema != nil {
properties[jsonTagString] = *schema
return
}
}

schema := spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
Example: example,
Expand All @@ -75,12 +83,6 @@ func propertiesFromType(ctx Context, tp apiSpec.Type) (spec.SchemaProperties, []
schema.Properties = p
schema.Required = r
}
if ctx.UseDefinitions {
structName, containsStruct := containsStruct(member.Type)
if containsStruct {
schema.SchemaProps.Ref = spec.MustCreateRef(getRefName(structName))
}
}

properties[jsonTagString] = schema
})
Expand All @@ -89,6 +91,97 @@ func propertiesFromType(ctx Context, tp apiSpec.Type) (spec.SchemaProperties, []
return properties, requiredFields
}

// buildSchemaWithDefinitions Correctly handle $ref references for composite types
func buildSchemaWithDefinitions(ctx Context, tp apiSpec.Type, example any, comment string, minimum, maximum *float64, exclusiveMinimum, exclusiveMaximum bool, defaultValue any, enum []any) *spec.Schema {
switch val := tp.(type) {
case apiSpec.DefineStruct:
return &spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
Example: example,
},
SchemaProps: spec.SchemaProps{
Description: formatComment(comment),
Ref: spec.MustCreateRef(getRefName(val.RawName)),
},
}
case apiSpec.PointerType:
return buildSchemaWithDefinitions(ctx, val.Type, example, comment, minimum, maximum, exclusiveMinimum, exclusiveMaximum, defaultValue, enum)
case apiSpec.ArrayType:
itemSchema := buildSchemaWithDefinitions(ctx, val.Value, nil, "", nil, nil, false, false, nil, nil)
if itemSchema != nil {
return &spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
Example: example,
},
SchemaProps: spec.SchemaProps{
Description: formatComment(comment),
Type: spec.StringOrArray{"array"},
Items: &spec.SchemaOrArray{
Schema: itemSchema,
},
},
}
}

structName, containsStruct := containsStruct(val.Value)
if containsStruct {
return &spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
Example: example,
},
SchemaProps: spec.SchemaProps{
Description: formatComment(comment),
Type: spec.StringOrArray{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef(getRefName(structName)),
},
},
},
},
}
}
case apiSpec.MapType:
valueSchema := buildSchemaWithDefinitions(ctx, val.Value, nil, "", nil, nil, false, false, nil, nil)
if valueSchema != nil {
return &spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
Example: example,
},
SchemaProps: spec.SchemaProps{
Description: formatComment(comment),
Type: spec.StringOrArray{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Schema: valueSchema,
},
},
}
}
structName, containsStruct := containsStruct(val.Value)
if containsStruct {
return &spec.Schema{
SwaggerSchemaProps: spec.SwaggerSchemaProps{
Example: example,
},
SchemaProps: spec.SchemaProps{
Description: formatComment(comment),
Type: spec.StringOrArray{"object"},
AdditionalProperties: &spec.SchemaOrBool{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef(getRefName(structName)),
},
},
},
},
}
}
}

return nil
}

func containsStruct(tp apiSpec.Type) (string, bool) {
switch val := tp.(type) {
case apiSpec.PointerType:
Expand Down
27 changes: 27 additions & 0 deletions tools/goctl/api/swagger/swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,33 @@ func itemsFromGoType(ctx Context, tp apiSpec.Type) *spec.SchemaOrArray {
if !ok {
return nil
}

if ctx.UseDefinitions {
Copy link

Copilot AI Jun 29, 2025

Choose a reason for hiding this comment

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

The logic for handling struct references in itemsFromGoType duplicates similar code in buildSchemaWithDefinitions. Consider extracting a shared helper to reduce duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
structName, containsStruct := containsStruct(array.Value)
if containsStruct {
if _, ok := array.Value.(apiSpec.DefineStruct); ok {
return &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef(getRefName(structName)),
},
},
}
}
if ptr, ok := array.Value.(apiSpec.PointerType); ok {
if _, ok := ptr.Type.(apiSpec.DefineStruct); ok {
return &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef(getRefName(structName)),
},
},
}
}
}
}
}

return itemFromGoType(ctx, array.Value)
}

Expand Down