Skip to content

Commit

Permalink
fix: nested struct tags should be respected in openapi spec
Browse files Browse the repository at this point in the history
  • Loading branch information
dylanhitt committed Dec 29, 2024
1 parent 2d6cfa4 commit 060d987
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 12 deletions.
39 changes: 39 additions & 0 deletions examples/petstore/lib/testdata/doc/openapi.golden.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@
"name": {
"example": "Napoleon",
"type": "string"
},
"references": {
"properties": {
"type": {
"description": "type of reference",
"example": "pet-123456",
"type": "string"
},
"value": {
"type": "string"
}
},
"type": "object"
}
},
"required": [
Expand Down Expand Up @@ -100,6 +113,19 @@
"maxLength": 100,
"minLength": 1,
"type": "string"
},
"references": {
"properties": {
"type": {
"description": "type of reference",
"example": "pet-123456",
"type": "string"
},
"value": {
"type": "string"
}
},
"type": "object"
}
},
"required": [
Expand Down Expand Up @@ -137,6 +163,19 @@
"minLength": 1,
"nullable": true,
"type": "string"
},
"references": {
"properties": {
"type": {
"description": "type of reference",
"example": "pet-123456",
"type": "string"
},
"value": {
"type": "string"
}
},
"type": "object"
}
},
"type": "object"
Expand Down
28 changes: 18 additions & 10 deletions examples/petstore/models/Pet.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,30 @@ import (
)

type Pets struct {
ID string `json:"id" validate:"required" example:"pet-123456"`
Name string `json:"name" validate:"required" example:"Napoleon"`
Age int `json:"age" example:"2" description:"Age of the pet, in years"`
IsAdopted bool `json:"is_adopted" description:"Is the pet adopted"`
ID string `json:"id" validate:"required" example:"pet-123456"`
Name string `json:"name" validate:"required" example:"Napoleon"`
Age int `json:"age" example:"2" description:"Age of the pet, in years"`
IsAdopted bool `json:"is_adopted" description:"Is the pet adopted"`
References References `json:"references"`
}

type PetsCreate struct {
Name string `json:"name" validate:"required,min=1,max=100" example:"Napoleon"`
Age int `json:"age" validate:"min=0,max=100" example:"2" description:"Age of the pet, in years"`
IsAdopted bool `json:"is_adopted" description:"Is the pet adopted"`
Name string `json:"name" validate:"required,min=1,max=100" example:"Napoleon"`
Age int `json:"age" validate:"min=0,max=100" example:"2" description:"Age of the pet, in years"`
IsAdopted bool `json:"is_adopted" description:"Is the pet adopted"`
References References `json:"references"`
}

type PetsUpdate struct {
Name string `json:"name,omitempty" validate:"min=1,max=100" example:"Napoleon" description:"Name of the pet"`
Age int `json:"age,omitempty" validate:"max=100" example:"2"`
IsAdopted *bool `json:"is_adopted,omitempty" description:"Is the pet adopted"`
Name string `json:"name,omitempty" validate:"min=1,max=100" example:"Napoleon" description:"Name of the pet"`
Age int `json:"age,omitempty" validate:"max=100" example:"2"`
IsAdopted *bool `json:"is_adopted,omitempty" description:"Is the pet adopted"`
References References `json:"references"`
}

type References struct {
Type string `json:"type" example:"pet-123456" description:"type of reference"`
Value string `json:"value"`
}

var _ fuego.InTransformer = &Pets{}
Expand Down
5 changes: 4 additions & 1 deletion openapi.go
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,6 @@ func parseStructTags(t reflect.Type, schemaRef *openapi3.SchemaRef) {

for i := range t.NumField() {
field := t.Field(i)

if field.Anonymous {
fieldType := field.Type
parseStructTags(fieldType, schemaRef)
Expand All @@ -454,6 +453,10 @@ func parseStructTags(t reflect.Type, schemaRef *openapi3.SchemaRef) {
slog.Warn("Property not found in schema", "property", jsonFieldName)
continue
}
if field.Type.Kind() == reflect.Struct {
fieldType := field.Type
parseStructTags(fieldType, property)
}
propertyCopy := *property
propertyValue := *propertyCopy.Value

Expand Down
28 changes: 27 additions & 1 deletion openapi_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,17 @@ import (

type MyStruct struct {
B string `json:"b"`
C int `json:"c"`
C int `json:"c" example:"8" validate:"min=3,max=10" description:"my description"`
D bool `json:"d"`
}

type MyStructWithNested struct {
E string `json:"e" example:"E"`
F int `json:"f"`
G bool `json:"g"`
Nested MyStruct `json:"nested" description:"my struct"`
}

type MyOutputStruct struct {
Name string `json:"name"`
Quantity int `json:"quantity"`
Expand Down Expand Up @@ -54,6 +61,14 @@ func Test_tagFromType(t *testing.T) {
expectedTagValue: "MyStruct",
expectedTagValueType: &openapi3.Types{"object"},
},
{
name: "nested struct",
description: "",
inputType: MyStructWithNested{},

expectedTagValue: "MyStructWithNested",
expectedTagValueType: &openapi3.Types{"object"},
},
{
name: "is_pointer",
description: "",
Expand Down Expand Up @@ -201,6 +216,17 @@ func Test_tagFromType(t *testing.T) {
}
})
}

t.Run("struct with nested tags", func(t *testing.T) {
s := NewServer()
tag := SchemaTagFromType(s.OpenAPI, MyStructWithNested{})
nestedProperty := tag.Value.Properties["nested"]
require.Equal(t, "my struct", nestedProperty.Value.Description)
require.Equal(t, "my description", nestedProperty.Value.Properties["c"].Value.Description)
require.Equal(t, 8, nestedProperty.Value.Properties["c"].Value.Example)
require.Equal(t, float64(3), *nestedProperty.Value.Properties["c"].Value.Min)
require.Equal(t, float64(10), *nestedProperty.Value.Properties["c"].Value.Max)
})
}

func TestServer_generateOpenAPI(t *testing.T) {
Expand Down

0 comments on commit 060d987

Please sign in to comment.