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
10 changes: 8 additions & 2 deletions tools/goctl/api/swagger/annotation.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,14 @@ func getBoolFromKVOrDefault(properties map[string]string, key string, def bool)
}
//I think this function and those below should handle error, but they didn't.
//Since a default value (def) is provided, any parsing errors will result in the default being returned.
str, err := strconv.Unquote(val[0])
if err != nil || len(str) == 0 {
str := val[0]
// Try to unquote first, but if it fails, use the original value
// This handles both quoted ("true") and unquoted (true) values
unquoted, err := strconv.Unquote(str)
if err == nil && len(unquoted) > 0 {
str = unquoted
}
if len(str) == 0 {
return def
}
res, _ := strconv.ParseBool(str)
Expand Down
13 changes: 13 additions & 0 deletions tools/goctl/api/swagger/annotation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,19 @@ func Test_getBoolFromKVOrDefault(t *testing.T) {
assert.False(t, getBoolFromKVOrDefault(properties, "empty_value", false))
assert.False(t, getBoolFromKVOrDefault(nil, "nil", false))
assert.False(t, getBoolFromKVOrDefault(map[string]string{}, "empty", false))

// Test unquoted boolean values (as returned by API parser)
unquotedProperties := map[string]string{
"enabled_unquoted": "true",
"disabled_unquoted": "false",
"one": "1",
"zero": "0",
}

assert.True(t, getBoolFromKVOrDefault(unquotedProperties, "enabled_unquoted", false))
assert.False(t, getBoolFromKVOrDefault(unquotedProperties, "disabled_unquoted", true))
assert.True(t, getBoolFromKVOrDefault(unquotedProperties, "one", false))
assert.False(t, getBoolFromKVOrDefault(unquotedProperties, "zero", true))
}

func Test_getStringFromKVOrDefault(t *testing.T) {
Expand Down
50 changes: 37 additions & 13 deletions tools/goctl/api/swagger/response.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,29 +26,53 @@ func jsonResponseFromType(ctx Context, atDoc apiSpec.AtDoc, tp apiSpec.Type) *sp
AdditionalProperties: mapFromGoType(ctx, tp),
Items: itemsFromGoType(ctx, tp),
}

// Handle arrays with useDefinitions
if arrayType, isArray := tp.(apiSpec.ArrayType); isArray && ctx.UseDefinitions {
if structName, containsStruct := containsStruct(arrayType.Value); containsStruct {
// For arrays, set $ref inside items, not at schema level
props.Items = &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Ref: spec.MustCreateRef(getRefName(structName)),
},
},
}
}
}

if ctx.UseDefinitions {
structName, ok := containsStruct(tp)
if ok {
props.Ref = spec.MustCreateRef(getRefName(structName))
return &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusOK: {
ResponseProps: spec.ResponseProps{
Schema: &spec.Schema{
SchemaProps: wrapCodeMsgProps(ctx, props, atDoc),
// For non-array types containing structs, use $ref at schema level
if _, isArray := tp.(apiSpec.ArrayType); !isArray {
structName, ok := containsStruct(tp)
if ok {
props.Ref = spec.MustCreateRef(getRefName(structName))
return &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
http.StatusOK: {
ResponseProps: spec.ResponseProps{
Schema: &spec.Schema{
SchemaProps: wrapCodeMsgProps(ctx, props, atDoc),
},
},
},
},
},
},
}
}
}
}

p, _ := propertiesFromType(ctx, tp)
props.Type = typeFromGoType(ctx, tp)
props.Properties = p

// For array types with useDefinitions, items are already set correctly above
// For non-array types, we need to set properties
if _, isArray := tp.(apiSpec.ArrayType); !isArray {
p, _ := propertiesFromType(ctx, tp)
props.Properties = p
}

return &spec.Responses{
ResponsesProps: spec.ResponsesProps{
StatusCodeResponses: map[int]spec.Response{
Expand Down
78 changes: 78 additions & 0 deletions tools/goctl/api/swagger/swagger_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,81 @@ func TestArrayWithoutDefinitions(t *testing.T) {
assert.Contains(t, arrayField.Items.Schema.Properties, "itemName")
assert.Equal(t, []string{"itemName"}, arrayField.Items.Schema.Required)
}

func TestArrayResponseWithDefinitions(t *testing.T) {
// Test that array responses work correctly with useDefinitions
ctx := Context{
UseDefinitions: true,
}

// Create an array response type
arrayType := spec.ArrayType{
RawName: "[]ItemStruct",
Value: spec.DefineStruct{
RawName: "ItemStruct",
Members: []spec.Member{
{
Name: "ItemName",
Type: spec.PrimitiveType{RawName: "string"},
Tag: `json:"itemName"`,
},
},
},
}

// Get the response schema
resp := jsonResponseFromType(ctx, spec.AtDoc{}, arrayType)

// Check the response structure
assert.NotNil(t, resp)
assert.Contains(t, resp.StatusCodeResponses, 200)

schema := resp.StatusCodeResponses[200].Schema

// Should be array type
assert.Equal(t, "array", schema.Type[0])

// Should NOT have $ref at schema level for arrays
assert.Empty(t, schema.Ref.String(), "Array response should not have $ref at schema level")

// Should have items defined
assert.NotNil(t, schema.Items)
assert.NotNil(t, schema.Items.Schema)

// Items should have $ref when useDefinitions is true
assert.Equal(t, "#/definitions/ItemStruct", schema.Items.Schema.Ref.String())
}

func TestNonArrayResponseWithDefinitions(t *testing.T) {
// Test that non-array responses correctly use $ref with useDefinitions
ctx := Context{
UseDefinitions: true,
}

// Create a struct response type
structType := spec.DefineStruct{
RawName: "ItemStruct",
Members: []spec.Member{
{
Name: "ItemName",
Type: spec.PrimitiveType{RawName: "string"},
Tag: `json:"itemName"`,
},
},
}

// Get the response schema
resp := jsonResponseFromType(ctx, spec.AtDoc{}, structType)

// Check the response structure
assert.NotNil(t, resp)
assert.Contains(t, resp.StatusCodeResponses, 200)

schema := resp.StatusCodeResponses[200].Schema

// Should have $ref at schema level for structs
assert.Equal(t, "#/definitions/ItemStruct", schema.Ref.String())

// Should NOT have properties when using $ref
assert.Nil(t, schema.Properties)
}
Loading