Skip to content

Commit

Permalink
support fromData parameters for swagger json
Browse files Browse the repository at this point in the history
  • Loading branch information
yoyofx committed Feb 2, 2024
1 parent 69c430b commit 23591c6
Show file tree
Hide file tree
Showing 5 changed files with 147 additions and 68 deletions.
18 changes: 13 additions & 5 deletions examples/simpleweb/contollers/usercontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/yoyofx/yoyogo/web/captcha"
"github.com/yoyofx/yoyogo/web/context"
"github.com/yoyofx/yoyogo/web/mvc"
"gorm.io/gorm/utils"
"mime/multipart"
"simpleweb/models"
)
Expand Down Expand Up @@ -66,11 +67,6 @@ func (controller UserController) GetHtmlBody() actionresult.IActionResult {
})
}

func (controller UserController) GetDoc() mvc.ApiDocResult[string] {

return mvc.ApiDocumentResult[string]().Success().Data("ok").Message("hello").Build()
}

func (controller UserController) GetInfo() mvc.ApiResult {

return controller.OK(controller.userAction.Login("zhang"))
Expand Down Expand Up @@ -158,6 +154,7 @@ func (controller UserController) Upload(form *UploadForm) mvc.ApiResult {

}

// TestFunc attribute routing @route("/v1/user/{id}/test")
func (controller UserController) TestFunc(request *struct {
mvc.RequestGET `route:"/v1/user/:id/test" doc:"测试接口"`
Name string `uri:"name" doc:"测试用户名"`
Expand All @@ -166,3 +163,14 @@ func (controller UserController) TestFunc(request *struct {

return mvc.Success(request)
}

// GetDocumentById TestFunc attribute routing @route("/v1/user/doc/{id}")
func (controller UserController) GetDocumentById(request *struct {
mvc.RequestGET `route:"/v1/user/doc/:id" doc:"根据ID获取文档"`
Id uint64 `path:"id" doc:"文档ID"`
}) mvc.ApiDocResult[string] {

return mvc.ApiDocumentResult[string]().Success().
Data("Document Id:" + utils.ToString(request.Id)).
Message("GetDocumentById").Build()
}
73 changes: 73 additions & 0 deletions pkg/swagger/swagger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
package swagger

import (
"reflect"
"strings"
)

// GetSwaggerType returns the type of the swagger type that corresponds to the go type.
func GetSwaggerType(goType string) string {
if strings.Contains(goType, "file") {
return "file"
}
switch goType {
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
return "integer"
case "float32", "float64":
return "number"
case "string":
return "string"
case "bool":
return "boolean"
default:
return "object"
}
}

func getArrayElementType(goType string) string {
return goType[2 : len(goType)-1]
}

// ConvertToSwaggerResponse converts a struct to a swagger response.
func ConvertToSwaggerResponse(data interface{}) map[string]interface{} {
response := make(map[string]interface{})
response["type"] = "object"
response["properties"] = make(map[string]interface{})

dataType := reflect.TypeOf(data)
dataValue := reflect.ValueOf(data)

for i := 0; i < dataType.NumField(); i++ {
field := dataType.Field(i)
fieldName := field.Tag.Get("json")
if fieldName == "" {
fieldName = field.Name
}
fieldValue := dataValue.Field(i).Interface()

fieldType := field.Type.String()
swaggerType := GetSwaggerType(fieldType)
//if swaggerType == "object" && fieldValue == nil {
// fieldValue = reflect.New(field.Type).Elem().Interface()
//}

if swaggerType == "array" {
response["properties"].(map[string]interface{})[fieldName] = map[string]interface{}{
"type": "array",
"items": map[string]interface{}{"type": GetSwaggerType(getArrayElementType(fieldType))},
}
} else {
response["properties"].(map[string]interface{})[fieldName] = map[string]interface{}{
"type": swaggerType,
}
}

if swaggerType == "object" {
if fieldValue != nil {
response["properties"].(map[string]interface{})[fieldName].(map[string]interface{})["properties"] = ConvertToSwaggerResponse(fieldValue)
}
}
}

return response
}
2 changes: 1 addition & 1 deletion pkg/swagger/swagger_path.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ type Property struct {

type ResponsesItem struct {
Description string `json:"description"`
Content map[string]interface{} `json:"content"`
Content map[string]interface{} `json:"content,omitempty"`
}

type Security struct {
Expand Down
108 changes: 54 additions & 54 deletions web/endpoints/swagger.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,33 +100,31 @@ func FilterValidParams(controller mvc.ControllerDescriptor, openapi *swagger.Ope
paramSourceData := param.ParameterType.Elem()
fieldNum := paramSourceData.NumField()
//根据请求方法分类
if act.ActionMethod == "post" || act.ActionMethod == "any" {
for i := 0; i < fieldNum; i++ {
// 获取方法注释
filed := paramSourceData.Field(i)
if strings.HasPrefix(filed.Type.Name(), "Request") {
if filed.Type.Name() == "RequestBody" || filed.Type.Name() == "RequestPOST" {
act.ActionMethod = "post"
} else {
actionMethodDef := filed.Type.Name()
actionMethodDef = strings.ReplaceAll(actionMethodDef, "Request", "")
act.ActionMethod = strings.ToLower(actionMethodDef) // get / head / delete / options / patch / put
}
// 获取BODY参数注释 RequestBody or RequestGET or RequestPOST
body, parameters := RequestBody(param)
if body.Content != nil {
pathInfo.RequestBody = body
}
if len(parameters) > 0 {
pathInfo.Parameters = parameters
}

pathInfo.Description = filed.Tag.Get("doc")
pathInfo.Summary = filed.Tag.Get("doc")
break
for i := 0; i < fieldNum; i++ {
// 获取方法注释
filed := paramSourceData.Field(i)
if strings.HasPrefix(filed.Type.Name(), "Request") {
if filed.Type.Name() == "RequestBody" || filed.Type.Name() == "RequestPOST" {
act.ActionMethod = "post"
} else {
actionMethodDef := filed.Type.Name()
actionMethodDef = strings.ReplaceAll(actionMethodDef, "Request", "")
act.ActionMethod = strings.ToLower(actionMethodDef) // get / head / delete / options / patch / put
}
// 获取BODY参数注释 RequestBody or RequestGET or RequestPOST
body, parameters := RequestBody(param)
if body.Content != nil {
pathInfo.RequestBody = body
}
if len(parameters) > 0 {
pathInfo.Parameters = parameters
}

pathInfo.Description = filed.Tag.Get("doc")
pathInfo.Summary = filed.Tag.Get("doc")
break
}

}

}
Expand All @@ -150,6 +148,13 @@ func FilterValidParams(controller mvc.ControllerDescriptor, openapi *swagger.Ope
if responseType != nil && responseType.Kind() == reflect.Struct {
// struct , ApiResult , ApiDocResult[?]
println(responseType.Name())
// new struct type to object
responseObject := reflect.New(responseType).Elem().Interface()

swaggerResponse := swagger.ConvertToSwaggerResponse(responseObject)
responseItem := swagger.ResponsesItem{Description: "OK", Content: make(map[string]interface{})}
responseItem.Content["application/json"] = map[string]interface{}{"schema": swaggerResponse}
pathInfo.Responses["200"] = responseItem
} else {
pathInfo.Responses["200"] = swagger.ResponsesItem{Description: "OK"}
}
Expand All @@ -172,7 +177,11 @@ func RequestBody(param reflectx.MethodParameterInfo) (swagger.RequestBody, []swa
schema.Properties = schemaProperties
for i := 0; i < fieldNum; i++ {
filed := paramSourceData.Field(i)
if strings.HasPrefix(filed.Type.Name(), "Request") {
fieldTypeName := strings.ToLower(filed.Type.Name())
if fieldTypeName == "" {
fieldTypeName = strings.ToLower(filed.Type.Elem().Name())
}
if strings.HasPrefix(fieldTypeName, "Request") {
continue
}
uriField := filed.Tag.Get("uri")
Expand All @@ -181,7 +190,7 @@ func RequestBody(param reflectx.MethodParameterInfo) (swagger.RequestBody, []swa
pathField := filed.Tag.Get("path")
headerField := filed.Tag.Get("header")

if uriField != "" || pathField != "" || headerField != "" {
if uriField != "" || pathField != "" || headerField != "" || formField != "" {
// 构建参数
params := swagger.Parameters{}
params.In = "query"
Expand All @@ -194,28 +203,37 @@ func RequestBody(param reflectx.MethodParameterInfo) (swagger.RequestBody, []swa
fieldName = headerField
params.In = "header"
}
if fieldName == "" {
fieldName = formField
params.In = "formData"
}

params.Name = fieldName
params.Schema = struct {
Type string `json:"type"`
}(struct{ Type string }{
Type: swagger.GetSwaggerType(fieldTypeName),
})

params.Description = filed.Tag.Get("doc")
parameterList = append(parameterList, params)
}

if formField != "" || jsonField != "" {
if jsonField != "" {
//if contentTypeStr == "" {
// contentTypeStr = "application/x-www-form-urlencoded"
//}
fieldName := formField
if fieldName == "" {
fieldName = jsonField
}
fieldName := jsonField

property := swagger.Property{}
property.Type = strings.ToLower(filed.Type.Name())
if property.Type == "" {
property.Type = strings.ToLower(filed.Type.Elem().Name())
}
property.Type = getSwaggerType(property.Type)
if property.Type == "file" {
property.Format = "binary"
}
property.Type = swagger.GetSwaggerType(property.Type)
//if property.Type == "file" {
// property.Format = "binary"
//}

property.Description = filed.Tag.Get("doc")
schemaProperties[fieldName] = property
Expand All @@ -227,29 +245,11 @@ func RequestBody(param reflectx.MethodParameterInfo) (swagger.RequestBody, []swa
if len(schemaProperties) > 0 {
//if contentTypeStr == "" {
contentTypeStr = "application/json"
//}

content := swagger.ContentType{Schema: schema}
contentType[contentTypeStr] = content
} else {
contentType = nil
}
return swagger.RequestBody{Content: contentType}, parameterList
}

func getSwaggerType(goType string) string {
if strings.Contains(goType, "file") {
return "string"
}
switch goType {
case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
return "integer"
case "float32", "float64":
return "number"
case "string":
return "string"
case "bool":
return "boolean"
default:
return "object"
}
}
14 changes: 6 additions & 8 deletions web/mvc/controller_descriptor.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,13 @@ func NewControllerDescriptor(name string, controllerType reflect.Type, controlle
MethodInfo: action,
}
// Action Descriptors
attributeRoute, err := addAttributeRouteActionDescriptor(name, actionDescriptor)
if err != nil {
logger.Error(err.Error())
} else {
if attributeRoute != nil {
actionDescriptor.IsAttributeRoute = true
actionDescriptor.Route = attributeRoute
}
attributeRoute, _ := addAttributeRouteActionDescriptor(name, actionDescriptor)

if attributeRoute != nil {
actionDescriptor.IsAttributeRoute = true
actionDescriptor.Route = attributeRoute
}

actionDescriptors[actionName] = actionDescriptor
}
}
Expand Down

0 comments on commit 23591c6

Please sign in to comment.