Skip to content

Commit

Permalink
Adds support for nested and deeply complex struct types (#26)
Browse files Browse the repository at this point in the history
* adds a test for a deeply complex type with arrays objects and pointers

* fix array at top level
  • Loading branch information
Acepie authored Apr 22, 2024
1 parent ac72b82 commit d9a10d0
Show file tree
Hide file tree
Showing 4 changed files with 167 additions and 1 deletion.
29 changes: 28 additions & 1 deletion components/definition/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package definition
import (
"fmt"
"reflect"
"strings"

"github.com/go-swagno/swagno/components/fields"
"github.com/go-swagno/swagno/components/http/response"
Expand Down Expand Up @@ -59,6 +60,7 @@ func (g DefinitionGenerator) CreateDefinition(t interface{}) {
if reflectReturn.Kind() == reflect.Struct {
properties = g.createStructDefinitions(reflectReturn)
}
definitionName, _ = strings.CutPrefix(definitionName, "[]")
case reflect.Struct:
if reflectReturn == reflect.TypeOf(response.CustomResponse{}) {
// if CustomResponseType, use Model struct in it
Expand Down Expand Up @@ -122,7 +124,10 @@ func (g DefinitionGenerator) createStructDefinitions(structType reflect.Type) ma
} else if field.Type.String() == "time.Duration" {
properties[fieldJsonTag] = g.durationProperty(field)
} else {
properties[fieldJsonTag] = g.refProperty(field)
properties[fieldJsonTag] = DefinitionProperties{
Example: fields.ExampleTag(field),
Ref: fmt.Sprintf("#/definitions/%s", field.Type.String()),
}
g.CreateDefinition(reflect.New(field.Type).Elem().Interface())
}

Expand All @@ -142,6 +147,28 @@ func (g DefinitionGenerator) createStructDefinitions(structType reflect.Type) ma
properties[fieldJsonTag] = g.refProperty(field)
g.CreateDefinition(reflect.New(field.Type.Elem()).Elem().Interface())
}
} else if field.Type.Elem().Kind() == reflect.Array || field.Type.Elem().Kind() == reflect.Slice {
if field.Type.Elem().Elem().Kind() == reflect.Struct {
properties[fieldJsonTag] = DefinitionProperties{
Example: fields.ExampleTag(field),
Type: fields.Type(field.Type.Elem().Kind().String()),
Items: &DefinitionPropertiesItems{
Ref: fmt.Sprintf("#/definitions/%s", field.Type.Elem().Elem().String()),
},
}
if structType == field.Type.Elem().Elem() {
continue // prevent recursion
}
g.CreateDefinition(reflect.New(field.Type.Elem().Elem()).Elem().Interface())
} else {
properties[fieldJsonTag] = DefinitionProperties{
Example: fields.ExampleTag(field),
Type: fields.Type(field.Type.Elem().Kind().String()),
Items: &DefinitionPropertiesItems{
Type: fields.Type(field.Type.Elem().Elem().Kind().String()),
},
}
}
} else {
properties[fieldJsonTag] = DefinitionProperties{
Example: fields.ExampleTag(field),
Expand Down
18 changes: 18 additions & 0 deletions example/models/complex.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package models

type Object struct {
Name string `json:"name" example:"John Smith"`
}

type Nested struct {
Objects *[]Object `json:"objects,omitempty"`
Strings *[]string `json:"strings,omitempty"`
}

type Deeply struct {
Nested Nested `json:"nested"`
}

type ComplexSuccessfulResponse struct {
Data *Deeply `json:"deeply"`
}
24 changes: 24 additions & 0 deletions generate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,30 @@ func TestSwaggerGeneration(t *testing.T) {
},
file: "testdata/expected_output/bft.json",
},
{
name: "Deeply Nested Model Test",
endpoints: []*endpoint.EndPoint{
endpoint.New(
endpoint.GET,
"/deeplynested",
endpoint.WithSuccessfulReturns([]response.Response{response.New(models.ComplexSuccessfulResponse{}, "OK", "200")}),
endpoint.WithDescription(desc),
endpoint.WithProduce([]mime.MIME{mime.JSON, mime.XML}),
endpoint.WithConsume([]mime.MIME{mime.JSON}),
endpoint.WithSummary("this is a test summary"),
),
endpoint.New(
endpoint.GET,
"/arraydeeplynested",
endpoint.WithSuccessfulReturns([]response.Response{response.New([]models.ComplexSuccessfulResponse{}, "OK", "200")}),
endpoint.WithDescription(desc),
endpoint.WithProduce([]mime.MIME{mime.JSON, mime.XML}),
endpoint.WithConsume([]mime.MIME{mime.JSON}),
endpoint.WithSummary("this is a test summary"),
),
},
file: "testdata/expected_output/dnmt.json",
},
}

// Iterate through test cases
Expand Down
97 changes: 97 additions & 0 deletions testdata/expected_output/dnmt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
{
"swagger": "2.0",
"info": {
"title": "Testing API",
"version": "v1.0.0"
},
"paths": {
"/deeplynested": {
"get": {
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed id malesuada lorem, et fermentum sapien. Vivamus non pharetra risus, in efficitur leo. Suspendisse sed metus sit amet mi laoreet imperdiet. Donec aliquam eros eu blandit feugiat. Quisque scelerisque justo ac vehicula bibendum. Fusce suscipit arcu nisl, eu maximus odio consequat quis. Curabitur fermentum eleifend tellus, lobortis hendrerit velit varius vitae.",
"consumes": ["application/json"],
"produces": ["application/json", "application/xml"],
"tags": [],
"summary": "this is a test summary",
"operationId": "get-/deeplynested",
"parameters": [],
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/models.ComplexSuccessfulResponse"
}
}
}
}
},
"/arraydeeplynested": {
"get": {
"description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed id malesuada lorem, et fermentum sapien. Vivamus non pharetra risus, in efficitur leo. Suspendisse sed metus sit amet mi laoreet imperdiet. Donec aliquam eros eu blandit feugiat. Quisque scelerisque justo ac vehicula bibendum. Fusce suscipit arcu nisl, eu maximus odio consequat quis. Curabitur fermentum eleifend tellus, lobortis hendrerit velit varius vitae.",
"consumes": ["application/json"],
"produces": ["application/json", "application/xml"],
"tags": [],
"summary": "this is a test summary",
"operationId": "get-/arraydeeplynested",
"parameters": [],
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "array",
"items": {
"$ref": "#/definitions/models.ComplexSuccessfulResponse"
}
}
}
}
}
}
},
"basePath": "/",
"host": "localhost",
"definitions": {
"models.ComplexSuccessfulResponse": {
"type": "object",
"properties": {
"deeply": {
"$ref": "#/definitions/models.Deeply"
}
}
},
"models.Deeply": {
"type": "object",
"properties": {
"nested": {
"$ref": "#/definitions/models.Nested"
}
}
},
"models.Nested": {
"type": "object",
"properties": {
"objects": {
"type": "array",
"items": {
"$ref": "#/definitions/models.Object"
}
},
"strings": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"models.Object": {
"type": "object",
"properties": {
"name": {
"type": "string",
"example": "John Smith"
}
}
}
},
"schemes": ["http", "https"]
}

0 comments on commit d9a10d0

Please sign in to comment.