Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat #36 support map kind #38

Closed
wants to merge 1 commit into from
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
14 changes: 13 additions & 1 deletion api.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,19 @@ type Object struct {

// Property represents the property entity from the swagger definition
type Property struct {
GoType reflect.Type `json:"-"`
GoType reflect.Type `json:"-"`
Type string `json:"type,omitempty"`
Description string `json:"description,omitempty"`
Enum []string `json:"enum,omitempty"`
Format string `json:"format,omitempty"`
Ref string `json:"$ref,omitempty"`
Example string `json:"example,omitempty"`
Items *Items `json:"items,omitempty"`
AddPropertie *AdditionalProperties `json:"additionalProperties,omitempty"`
}

type AdditionalProperties struct {
GoType reflect.Type `json:"-,omitempty"`
Type string `json:"type,omitempty"`
Description string `json:"description,omitempty"`
Enum []string `json:"enum,omitempty"`
Expand Down
150 changes: 147 additions & 3 deletions reflect.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,16 +67,40 @@
name := makeName(p.GoType)
p.Ref = makeRef(name)

case reflect.Map:
p.Type = "object"
p.AddPropertie = buildMapType(p.GoType)

case reflect.Slice:
p.Type = types.Array.String()
p.Items = &Items{}

p.GoType = t.Elem() // dereference the slice
// dereference the slice,get the element object of the slice
// Example:
// t ==> *[]*Struct|Struct|*string|string
// p.GoType.Kind() ==> []*Struct|Struct|*string|string
if p.GoType.Kind() == reflect.Slice {
Copy link
Owner

Choose a reason for hiding this comment

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

Is it a repeated judgment?
The outer switch has determined Slice.

//p.GoType ==> *Struct|Struct|*string|string
p.GoType = p.GoType.Elem()
}

switch p.GoType.Kind() {
case reflect.Ptr:
//p.GoType ==> Struct|string
p.GoType = p.GoType.Elem()
name := makeName(p.GoType)
p.Items.Ref = makeRef(name)

// determine the type of element object in the slice
isPrimitive := isPrimitiveType(p.GoType.Name(), p.GoType)

if isPrimitive {
// golang built-in primitive type
kind_type := jsonSchemaType(p.GoType.String(), p.GoType)
p.Items.Type = kind_type
} else {
// Struct types
name := makeName(p.GoType)
p.Items.Ref = makeRef(name)
}

case reflect.Struct:
name := makeName(p.GoType)
Expand Down Expand Up @@ -106,6 +130,126 @@
return p
}

// buildMapType build map type swag info
func buildMapType(mapType reflect.Type) *AdditionalProperties {

prop := AdditionalProperties{}

// get the element object of the map
// Example:
// mapType ==> map[string][]*Struct|Struct|*string|string
// or mapTYpe ==> map[string]*Struct|Struct|*string|string
// mapType.Elem().Kind() ==> []*Struct|Struct|*string|string
// or mapType.Elem().Kind() ==> *Struct|Struct|*string|string
if mapType.Elem().Kind().String() != "interface" {
isSlice := isSliceOrArryType(mapType.Elem().Kind())
if isSlice || isByteArrayType(mapType.Elem()) {
// Example:
// mapType.Elem()==> []*Struct|Struct|*string|string
mapType = mapType.Elem()
}

isPrimitive := isPrimitiveType(mapType.Elem().Name(), mapType.Elem())

if isByteArrayType(mapType.Elem()) {
prop.Type = "string"

Check warning on line 155 in reflect.go

View check run for this annotation

Codecov / codecov/patch

reflect.go#L155

Added line #L155 was not covered by tests
} else {
if isSlice {
prop.Type = types.Array.String()
prop.Items = &Items{}
if isPrimitive {
prop.Items.Type = jsonSchemaType(mapType.Elem().String(), mapType.Elem())
} else {
prop.Items.Ref = makeMapRef(mapType.Elem().String())
}
} else if isPrimitive {
prop.Type = jsonSchemaType(mapType.Elem().String(), mapType.Elem())
} else {
prop.Ref = makeMapRef(mapType.Elem().String())
}

Check warning on line 169 in reflect.go

View check run for this annotation

Codecov / codecov/patch

reflect.go#L165-L169

Added lines #L165 - L169 were not covered by tests
}
}

return &prop
}

func makeMapRef(typeName string) string {
type_name := strings.Trim(typeName, "*")
return makeRef(type_name)
}

func isSliceOrArryType(t reflect.Kind) bool {
return t == reflect.Slice || t == reflect.Array
}

func isByteArrayType(t reflect.Type) bool {
return (t.Kind() == reflect.Slice || t.Kind() == reflect.Array) &&
t.Elem().Kind() == reflect.Uint8
}

// isPrimitiveType Whether it is a built-in primitive type
func isPrimitiveType(modelName string, modelType reflect.Type) bool {
var modelKind reflect.Kind

if modelType.Kind() == reflect.Ptr {
modelKind = modelType.Elem().Kind()
} else {
modelKind = modelType.Kind()
}

switch modelKind {
case reflect.Bool:
return true

Check warning on line 202 in reflect.go

View check run for this annotation

Codecov / codecov/patch

reflect.go#L201-L202

Added lines #L201 - L202 were not covered by tests
case reflect.Float32, reflect.Float64,
reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return true

Check warning on line 206 in reflect.go

View check run for this annotation

Codecov / codecov/patch

reflect.go#L205-L206

Added lines #L205 - L206 were not covered by tests
case reflect.String:
return true
}

if len(modelName) == 0 {
return false
}

return strings.Contains("time.Time time.Duration json.Number", modelName)
}

func jsonSchemaType(modelName string, modelType reflect.Type) string {
var modelKind reflect.Kind

if modelType.Kind() == reflect.Ptr {
modelKind = modelType.Elem().Kind()
} else {
modelKind = modelType.Kind()
}

schemaMap := map[string]string{
"time.Time": "string",
"time.Duration": "integer",
"json.Number": "number",
}
Comment on lines +227 to +231
Copy link
Owner

Choose a reason for hiding this comment

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

It is recommended to put it in a global variable and reuse it.


if mapped, ok := schemaMap[modelName]; ok {
return mapped
}

Check warning on line 235 in reflect.go

View check run for this annotation

Codecov / codecov/patch

reflect.go#L234-L235

Added lines #L234 - L235 were not covered by tests

// check if original type is primitive
switch modelKind {
case reflect.Bool:
return "boolean"
case reflect.Float32, reflect.Float64:
return "number"

Check warning on line 242 in reflect.go

View check run for this annotation

Codecov / codecov/patch

reflect.go#L239-L242

Added lines #L239 - L242 were not covered by tests
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
return "integer"

Check warning on line 245 in reflect.go

View check run for this annotation

Codecov / codecov/patch

reflect.go#L244-L245

Added lines #L244 - L245 were not covered by tests
case reflect.String:
return "string"
}

return modelName // use as is (custom or struct)

Check warning on line 250 in reflect.go

View check run for this annotation

Codecov / codecov/patch

reflect.go#L250

Added line #L250 was not covered by tests
}

func buildProperty(t reflect.Type) (map[string]Property, []string) {
properties := make(map[string]Property)
required := make([]string, 0)
Expand Down
17 changes: 17 additions & 0 deletions reflect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,20 @@ func TestMakeSchemaType(t *testing.T) {
objSchema := MakeSchema(struct{}{})
assert.Equal(t, "", objSchema.Type, "expect array type but get %s", objSchema.Type)
}

type PetCat struct {
MapSlicePtr map[string][]*string
MapSlice map[string][]string
MapSliceStructPtr map[string][]*Person
MapSliceStruct map[string][]Person
SliceStructPtr *[]*Person
SliceStruct *[]Person
SliceStringPtr *[]*string
SliceString *[]string
}

func TestDefineWithNewType(t *testing.T) {
v := define(PetCat{})
b, _ := json.Marshal(v)
fmt.Printf("result:%v\n", string(b))
Copy link
Owner

Choose a reason for hiding this comment

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

Do not use fmt output, instead use the correct expect and actual values for comparison.

}
Loading