diff --git a/examples/petstore/lib/testdata/doc/openapi.golden.json b/examples/petstore/lib/testdata/doc/openapi.golden.json index 692cced2..c29b9a74 100644 --- a/examples/petstore/lib/testdata/doc/openapi.golden.json +++ b/examples/petstore/lib/testdata/doc/openapi.golden.json @@ -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": [ @@ -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": [ @@ -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" diff --git a/examples/petstore/models/Pet.go b/examples/petstore/models/Pet.go index 5659e609..ae7f5fd7 100644 --- a/examples/petstore/models/Pet.go +++ b/examples/petstore/models/Pet.go @@ -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{} diff --git a/openapi.go b/openapi.go index 5c894257..7423ea73 100644 --- a/openapi.go +++ b/openapi.go @@ -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) @@ -454,6 +453,9 @@ func parseStructTags(t reflect.Type, schemaRef *openapi3.SchemaRef) { slog.Warn("Property not found in schema", "property", jsonFieldName) continue } + if field.Type.Kind() == reflect.Struct { + parseStructTags(field.Type, property) + } propertyCopy := *property propertyValue := *propertyCopy.Value diff --git a/openapi_test.go b/openapi_test.go index 7b695288..4ccb9c7c 100644 --- a/openapi_test.go +++ b/openapi_test.go @@ -11,14 +11,22 @@ import ( "github.com/getkin/kin-openapi/openapi3" "github.com/stretchr/testify/require" + "gotest.tools/v3/assert" ) 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"` @@ -54,6 +62,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: "", @@ -201,6 +217,21 @@ 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.NotNil(t, nestedProperty) + assert.Equal(t, "my struct", nestedProperty.Value.Description) + c := nestedProperty.Value.Properties["c"] + require.NotNil(t, c) + require.NotNil(t, c.Value) + assert.Equal(t, "my description", c.Value.Description) + assert.Equal(t, 8, c.Value.Example) + assert.Equal(t, float64(3), c.Value.Min) + assert.Equal(t, float64(10), c.Value.Max) + }) } func TestServer_generateOpenAPI(t *testing.T) {