mirror of
https://gitee.com/johng/gf.git
synced 2024-12-02 04:07:47 +08:00
add xextensions feature for package goai
This commit is contained in:
parent
c1c86c026f
commit
707dc6b346
@ -21,8 +21,8 @@ import (
|
||||
|
||||
_ "github.com/gogf/gf/contrib/drivers/mssql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/pgsql/v2"
|
||||
_ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
//_ "github.com/gogf/gf/contrib/drivers/oracle/v2"
|
||||
// _ "github.com/gogf/gf/contrib/drivers/sqlite/v2"
|
||||
// _ "github.com/gogf/gf/contrib/drivers/oracle/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -35,7 +35,7 @@ const (
|
||||
cGenDaoEg = `
|
||||
gf gen dao
|
||||
gf gen dao -l "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
gf gen dao -p ./model -c config.yaml -g user-center -t user,user_detail,user_login
|
||||
gf gen dao -p ./model -g user-center -t user,user_detail,user_login
|
||||
gf gen dao -r user_
|
||||
`
|
||||
|
||||
|
@ -133,9 +133,7 @@ func (m *middleware) callHandlerFunc(funcInfo handlerFuncInfo) {
|
||||
reflect.ValueOf(m.request.Context()),
|
||||
}
|
||||
if funcInfo.Type.NumIn() == 2 {
|
||||
var (
|
||||
inputObject reflect.Value
|
||||
)
|
||||
var inputObject reflect.Value
|
||||
if funcInfo.Type.In(1).Kind() == reflect.Ptr {
|
||||
inputObject = reflect.New(funcInfo.Type.In(1).Elem())
|
||||
m.request.error = m.request.Parse(inputObject.Interface())
|
||||
|
@ -126,7 +126,7 @@ func (s *Server) setHandler(ctx context.Context, in setHandlerInput) {
|
||||
}
|
||||
|
||||
// Repeated router checks, this feature can be disabled by server configuration.
|
||||
routerKey := s.routerMapKey(handler.HookName, method, uri, domain)
|
||||
var routerKey = s.routerMapKey(handler.HookName, method, uri, domain)
|
||||
if !s.config.RouteOverWrite {
|
||||
switch handler.Type {
|
||||
case HandlerTypeHandler, HandlerTypeObject:
|
||||
@ -164,8 +164,10 @@ func (s *Server) setHandler(ctx context.Context, in setHandlerInput) {
|
||||
}
|
||||
// List array, very important for router registering.
|
||||
// There may be multiple lists adding into this array when searching from root to leaf.
|
||||
lists := make([]*glist.List, 0)
|
||||
array := ([]string)(nil)
|
||||
var (
|
||||
array []string
|
||||
lists = make([]*glist.List, 0)
|
||||
)
|
||||
if strings.EqualFold("/", uri) {
|
||||
array = []string{"/"}
|
||||
} else {
|
||||
@ -182,7 +184,7 @@ func (s *Server) setHandler(ctx context.Context, in setHandlerInput) {
|
||||
// priorities from high to low.
|
||||
// 3. There may be repeated router items in the router lists. The lists' priorities
|
||||
// from root to leaf are from low to high.
|
||||
p := s.serveTree[domain]
|
||||
var p = s.serveTree[domain]
|
||||
for i, part := range array {
|
||||
// Ignore empty URI part, like: /user//index
|
||||
if part == "" {
|
||||
@ -223,7 +225,7 @@ func (s *Server) setHandler(ctx context.Context, in setHandlerInput) {
|
||||
}
|
||||
// It iterates the list array of `lists`, compares priorities and inserts the new router item in
|
||||
// the proper position of each list. The priority of the list is ordered from high to low.
|
||||
item := (*handlerItem)(nil)
|
||||
var item *handlerItem
|
||||
for _, l := range lists {
|
||||
pushed := false
|
||||
for e := l.Front(); e != nil; e = e.Next() {
|
||||
@ -247,7 +249,7 @@ func (s *Server) setHandler(ctx context.Context, in setHandlerInput) {
|
||||
s.routesMap[routerKey] = make([]registeredRouteItem, 0)
|
||||
}
|
||||
|
||||
routeItem := registeredRouteItem{
|
||||
var routeItem = registeredRouteItem{
|
||||
Source: handler.Source,
|
||||
Handler: handler,
|
||||
}
|
||||
@ -391,7 +393,7 @@ func (s *Server) patternToRegular(rule string) (regular string, names []string)
|
||||
return rule, nil
|
||||
}
|
||||
regular = "^"
|
||||
array := strings.Split(rule[1:], "/")
|
||||
var array = strings.Split(rule[1:], "/")
|
||||
for _, v := range array {
|
||||
if len(v) == 0 {
|
||||
continue
|
||||
|
@ -151,13 +151,13 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, structName, meth
|
||||
if pkgPath != "" {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`invalid handler: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" or "func(context.Context, BizRequest)(BizResponse, error)" is required`,
|
||||
`invalid handler: %s.%s.%s defined as "%s", but "func(*ghttp.Request)" or "func(context.Context, *BizRequest)(*BizResponse, error)" is required`,
|
||||
pkgPath, structName, methodName, reflect.TypeOf(f).String(),
|
||||
)
|
||||
} else {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`invalid handler: defined as "%s", but "func(*ghttp.Request)" or "func(context.Context, BizRequest)(BizResponse, error)" is required`,
|
||||
`invalid handler: defined as "%s", but "func(*ghttp.Request)" or "func(context.Context, *BizRequest)(*BizResponse, error)" is required`,
|
||||
reflect.TypeOf(f).String(),
|
||||
)
|
||||
}
|
||||
@ -186,7 +186,7 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, structName, meth
|
||||
if !gstr.HasSuffix(reflectType.In(1).String(), `Req`) {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`invalid struct naming for request: defined as "%s", but it should be named with "Req" suffix like "xxxReq"`,
|
||||
`invalid struct naming for request: defined as "%s", but it should be named with "Req" suffix like "XxxReq"`,
|
||||
reflectType.In(1).String(),
|
||||
)
|
||||
return
|
||||
@ -196,7 +196,7 @@ func (s *Server) checkAndCreateFuncInfo(f interface{}, pkgPath, structName, meth
|
||||
if !gstr.HasSuffix(reflectType.Out(0).String(), `Res`) {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`invalid struct naming for response: defined as "%s", but it should be named with "Res" suffix like "xxxRes"`,
|
||||
`invalid struct naming for response: defined as "%s", but it should be named with "Res" suffix like "XxxRes"`,
|
||||
reflectType.Out(0).String(),
|
||||
)
|
||||
return
|
||||
|
@ -22,9 +22,7 @@ import (
|
||||
// The optional parameter `method` is used to specify the method to be registered, which
|
||||
// supports multiple method names; multiple methods are separated by char ',', case-sensitive.
|
||||
func (s *Server) BindObject(pattern string, object interface{}, method ...string) {
|
||||
var (
|
||||
bindMethod = ""
|
||||
)
|
||||
var bindMethod = ""
|
||||
if len(method) > 0 {
|
||||
bindMethod = method[0]
|
||||
}
|
||||
|
@ -208,7 +208,7 @@ func getFieldValuesByTagPriority(pointer interface{}, priority []string, tagMap
|
||||
tagFields = append(tagFields, tagField)
|
||||
}
|
||||
// If this is an embedded attribute, it retrieves the tags recursively.
|
||||
if field.IsEmbedded() {
|
||||
if field.IsEmbedded() && field.OriginalKind() == reflect.Struct {
|
||||
if subTagFields, err := getFieldValuesByTagPriority(field.Value, priority, tagMap); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
|
@ -6,6 +6,12 @@
|
||||
|
||||
package goai
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// Operation represents "operation" specified by OpenAPI/Swagger 3.0 standard.
|
||||
type Operation struct {
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
@ -20,5 +26,36 @@ type Operation struct {
|
||||
Security *SecurityRequirements `json:"security,omitempty"`
|
||||
Servers *Servers `json:"servers,omitempty"`
|
||||
ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
|
||||
Sort int `json:"sort"`
|
||||
XExtensions `json:"-"`
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) tagMapToOperation(tagMap map[string]string, operation *Operation) error {
|
||||
var mergedTagMap = oai.fileMapWithShortTags(tagMap)
|
||||
if err := gconv.Struct(mergedTagMap, operation); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Operation failed`)
|
||||
}
|
||||
oai.tagMapToXExtensions(mergedTagMap, operation.XExtensions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (o Operation) MarshalJSON() ([]byte, error) {
|
||||
var (
|
||||
b []byte
|
||||
m map[string]json.RawMessage
|
||||
err error
|
||||
)
|
||||
type tempOperation Operation // To prevent JSON marshal recursion error.
|
||||
if b, err = json.Marshal(tempOperation(o)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range o.XExtensions {
|
||||
if b, err = json.Marshal(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[k] = b
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
@ -7,14 +7,8 @@
|
||||
package goai
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gstructs"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
@ -34,81 +28,36 @@ type Parameter struct {
|
||||
Example interface{} `json:"example,omitempty"`
|
||||
Examples *Examples `json:"examples,omitempty"`
|
||||
Content *Content `json:"content,omitempty"`
|
||||
XExtensions `json:"-"`
|
||||
}
|
||||
|
||||
// Parameters is specified by OpenAPI/Swagger 3.0 standard.
|
||||
type Parameters []ParameterRef
|
||||
|
||||
type ParameterRef struct {
|
||||
Ref string
|
||||
Value *Parameter
|
||||
func (oai *OpenApiV3) tagMapToParameter(tagMap map[string]string, parameter *Parameter) error {
|
||||
var mergedTagMap = oai.fileMapWithShortTags(tagMap)
|
||||
if err := gconv.Struct(mergedTagMap, parameter); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Parameter failed`)
|
||||
}
|
||||
oai.tagMapToXExtensions(mergedTagMap, parameter.XExtensions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path, method string) (*ParameterRef, error) {
|
||||
func (p Parameter) MarshalJSON() ([]byte, error) {
|
||||
var (
|
||||
tagMap = field.TagMap()
|
||||
parameter = &Parameter{
|
||||
Name: field.TagJsonName(),
|
||||
}
|
||||
b []byte
|
||||
m map[string]json.RawMessage
|
||||
err error
|
||||
)
|
||||
if parameter.Name == "" {
|
||||
parameter.Name = field.Name()
|
||||
}
|
||||
if len(tagMap) > 0 {
|
||||
err := gconv.Struct(oai.fileMapWithShortTags(tagMap), parameter)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, `mapping struct tags to Parameter failed`)
|
||||
}
|
||||
}
|
||||
if parameter.In == "" {
|
||||
// Automatically detect its "in" attribute.
|
||||
if gstr.ContainsI(path, fmt.Sprintf(`{%s}`, parameter.Name)) {
|
||||
parameter.In = ParameterInPath
|
||||
} else {
|
||||
// Default the parameter input to "query" if method is "GET/DELETE".
|
||||
switch gstr.ToUpper(method) {
|
||||
case HttpMethodGet, HttpMethodDelete:
|
||||
parameter.In = ParameterInQuery
|
||||
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch parameter.In {
|
||||
case ParameterInPath:
|
||||
// Required for path parameter.
|
||||
parameter.Required = true
|
||||
|
||||
case ParameterInCookie, ParameterInHeader, ParameterInQuery:
|
||||
|
||||
default:
|
||||
return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid tag value "%s" for In`, parameter.In)
|
||||
}
|
||||
// Necessary schema or content.
|
||||
schemaRef, err := oai.newSchemaRefWithGolangType(field.Type().Type, tagMap)
|
||||
if err != nil {
|
||||
type tempParameter Parameter // To prevent JSON marshal recursion error.
|
||||
if b, err = json.Marshal(tempParameter(p)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parameter.Schema = schemaRef
|
||||
|
||||
// Required check.
|
||||
if parameter.Schema.Value != nil && parameter.Schema.Value.Pattern != "" {
|
||||
if gset.NewStrSetFrom(gstr.Split(parameter.Schema.Value.Pattern, "|")).Contains(patternKeyForRequired) {
|
||||
parameter.Required = true
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range p.XExtensions {
|
||||
if b, err = json.Marshal(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[k] = b
|
||||
}
|
||||
|
||||
return &ParameterRef{
|
||||
Ref: "",
|
||||
Value: parameter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r ParameterRef) MarshalJSON() ([]byte, error) {
|
||||
if r.Ref != "" {
|
||||
return formatRefToBytes(r.Ref), nil
|
||||
}
|
||||
return json.Marshal(r.Value)
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
95
protocol/goai/goai_parameter_ref.go
Normal file
95
protocol/goai/goai_parameter_ref.go
Normal file
@ -0,0 +1,95 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package goai
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gstructs"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// Parameters is specified by OpenAPI/Swagger 3.0 standard.
|
||||
type Parameters []ParameterRef
|
||||
|
||||
type ParameterRef struct {
|
||||
Ref string
|
||||
Value *Parameter
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path, method string) (*ParameterRef, error) {
|
||||
var (
|
||||
tagMap = field.TagMap()
|
||||
parameter = &Parameter{
|
||||
Name: field.TagJsonName(),
|
||||
XExtensions: make(XExtensions),
|
||||
}
|
||||
)
|
||||
if parameter.Name == "" {
|
||||
parameter.Name = field.Name()
|
||||
}
|
||||
if len(tagMap) > 0 {
|
||||
if err := oai.tagMapToParameter(tagMap, parameter); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if parameter.In == "" {
|
||||
// Automatically detect its "in" attribute.
|
||||
if gstr.ContainsI(path, fmt.Sprintf(`{%s}`, parameter.Name)) {
|
||||
parameter.In = ParameterInPath
|
||||
} else {
|
||||
// Default the parameter input to "query" if method is "GET/DELETE".
|
||||
switch gstr.ToUpper(method) {
|
||||
case HttpMethodGet, HttpMethodDelete:
|
||||
parameter.In = ParameterInQuery
|
||||
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch parameter.In {
|
||||
case ParameterInPath:
|
||||
// Required for path parameter.
|
||||
parameter.Required = true
|
||||
|
||||
case ParameterInCookie, ParameterInHeader, ParameterInQuery:
|
||||
|
||||
default:
|
||||
return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid tag value "%s" for In`, parameter.In)
|
||||
}
|
||||
// Necessary schema or content.
|
||||
schemaRef, err := oai.newSchemaRefWithGolangType(field.Type().Type, tagMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
parameter.Schema = schemaRef
|
||||
|
||||
// Required check.
|
||||
if parameter.Schema.Value != nil && parameter.Schema.Value.Pattern != "" {
|
||||
if gset.NewStrSetFrom(gstr.Split(parameter.Schema.Value.Pattern, "|")).Contains(patternKeyForRequired) {
|
||||
parameter.Required = true
|
||||
}
|
||||
}
|
||||
|
||||
return &ParameterRef{
|
||||
Ref: "",
|
||||
Value: parameter,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r ParameterRef) MarshalJSON() ([]byte, error) {
|
||||
if r.Ref != "" {
|
||||
return formatRefToBytes(r.Ref), nil
|
||||
}
|
||||
return json.Marshal(r.Value)
|
||||
}
|
@ -11,6 +11,7 @@ import (
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gstructs"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
@ -32,7 +33,7 @@ type Path struct {
|
||||
Trace *Operation `json:"trace,omitempty"`
|
||||
Servers Servers `json:"servers,omitempty"`
|
||||
Parameters Parameters `json:"parameters,omitempty"`
|
||||
Sort int `json:"sort"`
|
||||
XExtensions `json:"-"`
|
||||
}
|
||||
|
||||
// Paths are specified by OpenAPI/Swagger standard version 3.0.
|
||||
@ -82,14 +83,15 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
|
||||
var (
|
||||
mime string
|
||||
path = Path{}
|
||||
path = Path{XExtensions: make(XExtensions)}
|
||||
inputMetaMap = gmeta.Data(inputObject.Interface())
|
||||
outputMetaMap = gmeta.Data(outputObject.Interface())
|
||||
isInputStructEmpty = oai.doesStructHasNoFields(inputObject.Interface())
|
||||
inputStructTypeName = oai.golangTypeToSchemaName(inputObject.Type())
|
||||
outputStructTypeName = oai.golangTypeToSchemaName(outputObject.Type())
|
||||
operation = Operation{
|
||||
Responses: map[string]ResponseRef{},
|
||||
Responses: map[string]ResponseRef{},
|
||||
XExtensions: make(XExtensions),
|
||||
}
|
||||
)
|
||||
// Path check.
|
||||
@ -128,12 +130,11 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
}
|
||||
|
||||
if len(inputMetaMap) > 0 {
|
||||
inputMetaMap = oai.fileMapWithShortTags(inputMetaMap)
|
||||
if err := gconv.Struct(inputMetaMap, &path); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Path failed`)
|
||||
if err := oai.tagMapToPath(inputMetaMap, &path); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := gconv.Struct(inputMetaMap, &operation); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Operation failed`)
|
||||
if err := oai.tagMapToOperation(inputMetaMap, &operation); err != nil {
|
||||
return err
|
||||
}
|
||||
// Allowed request mime.
|
||||
if mime = inputMetaMap[TagNameMime]; mime == "" {
|
||||
@ -207,12 +208,13 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
if _, ok := operation.Responses[responseOkKey]; !ok {
|
||||
var (
|
||||
response = Response{
|
||||
Content: map[string]MediaType{},
|
||||
Content: map[string]MediaType{},
|
||||
XExtensions: make(XExtensions),
|
||||
}
|
||||
)
|
||||
if len(outputMetaMap) > 0 {
|
||||
if err := gconv.Struct(oai.fileMapWithShortTags(outputMetaMap), &response); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Response failed`)
|
||||
if err := oai.tagMapToResponse(outputMetaMap, &response); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Supported mime types of response.
|
||||
@ -288,3 +290,34 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
func (oai *OpenApiV3) doesStructHasNoFields(s interface{}) bool {
|
||||
return reflect.TypeOf(s).NumField() == 0
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) tagMapToPath(tagMap map[string]string, path *Path) error {
|
||||
var mergedTagMap = oai.fileMapWithShortTags(tagMap)
|
||||
if err := gconv.Struct(mergedTagMap, path); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Path failed`)
|
||||
}
|
||||
oai.tagMapToXExtensions(mergedTagMap, path.XExtensions)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p Path) MarshalJSON() ([]byte, error) {
|
||||
var (
|
||||
b []byte
|
||||
m map[string]json.RawMessage
|
||||
err error
|
||||
)
|
||||
type tempPath Path // To prevent JSON marshal recursion error.
|
||||
if b, err = json.Marshal(tempPath(p)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range p.XExtensions {
|
||||
if b, err = json.Marshal(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[k] = b
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
@ -7,11 +7,9 @@
|
||||
package goai
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gstructs"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// Response is specified by OpenAPI/Swagger 3.0 standard.
|
||||
@ -20,90 +18,36 @@ type Response struct {
|
||||
Headers Headers `json:"headers,omitempty"`
|
||||
Content Content `json:"content,omitempty"`
|
||||
Links Links `json:"links,omitempty"`
|
||||
XExtensions `json:"-"`
|
||||
}
|
||||
|
||||
// Responses is specified by OpenAPI/Swagger 3.0 standard.
|
||||
type Responses map[string]ResponseRef
|
||||
|
||||
type ResponseRef struct {
|
||||
Ref string
|
||||
Value *Response
|
||||
}
|
||||
|
||||
func (r ResponseRef) MarshalJSON() ([]byte, error) {
|
||||
if r.Ref != "" {
|
||||
return formatRefToBytes(r.Ref), nil
|
||||
func (oai *OpenApiV3) tagMapToResponse(tagMap map[string]string, response *Response) error {
|
||||
var mergedTagMap = oai.fileMapWithShortTags(tagMap)
|
||||
if err := gconv.Struct(mergedTagMap, response); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Response failed`)
|
||||
}
|
||||
return json.Marshal(r.Value)
|
||||
oai.tagMapToXExtensions(mergedTagMap, response.XExtensions)
|
||||
return nil
|
||||
}
|
||||
|
||||
type getResponseSchemaRefInput struct {
|
||||
BusinessStructName string // The business struct name.
|
||||
CommonResponseObject interface{} // Common response object.
|
||||
CommonResponseDataField string // Common response data field.
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) getResponseSchemaRef(in getResponseSchemaRefInput) (*SchemaRef, error) {
|
||||
if in.CommonResponseObject == nil {
|
||||
return &SchemaRef{
|
||||
Ref: in.BusinessStructName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r Response) MarshalJSON() ([]byte, error) {
|
||||
var (
|
||||
dataFieldsPartsArray = gstr.Split(in.CommonResponseDataField, ".")
|
||||
bizResponseStructSchemaRef = oai.Components.Schemas.Get(in.BusinessStructName)
|
||||
schema, err = oai.structToSchema(in.CommonResponseObject)
|
||||
b []byte
|
||||
m map[string]json.RawMessage
|
||||
err error
|
||||
)
|
||||
if err != nil {
|
||||
type tempResponse Response // To prevent JSON marshal recursion error.
|
||||
if b, err = json.Marshal(tempResponse(r)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.CommonResponseDataField == "" && bizResponseStructSchemaRef != nil {
|
||||
// Normal response.
|
||||
bizResponseStructSchemaRef.Value.Properties.Iterator(func(key string, ref SchemaRef) bool {
|
||||
schema.Properties.Set(key, ref)
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
// Common response.
|
||||
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
|
||||
Pointer: in.CommonResponseObject,
|
||||
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
|
||||
})
|
||||
for _, structField := range structFields {
|
||||
var fieldName = structField.Name()
|
||||
if jsonName := structField.TagJsonName(); jsonName != "" {
|
||||
fieldName = jsonName
|
||||
}
|
||||
switch len(dataFieldsPartsArray) {
|
||||
case 1:
|
||||
if structField.Name() == dataFieldsPartsArray[0] {
|
||||
if err = oai.tagMapToSchema(structField.TagMap(), bizResponseStructSchemaRef.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties.Set(fieldName, *bizResponseStructSchemaRef)
|
||||
break
|
||||
}
|
||||
default:
|
||||
// Recursively creating common response object schema.
|
||||
if structField.Name() == dataFieldsPartsArray[0] {
|
||||
var structFieldInstance = reflect.New(structField.Type().Type).Elem()
|
||||
schemaRef, err := oai.getResponseSchemaRef(getResponseSchemaRefInput{
|
||||
BusinessStructName: in.BusinessStructName,
|
||||
CommonResponseObject: structFieldInstance,
|
||||
CommonResponseDataField: gstr.Join(dataFieldsPartsArray[1:], "."),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties.Set(fieldName, *schemaRef)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &SchemaRef{
|
||||
Value: schema,
|
||||
}, nil
|
||||
for k, v := range r.XExtensions {
|
||||
if b, err = json.Marshal(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[k] = b
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
101
protocol/goai/goai_response_ref.go
Normal file
101
protocol/goai/goai_response_ref.go
Normal file
@ -0,0 +1,101 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package goai
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/os/gstructs"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
type ResponseRef struct {
|
||||
Ref string
|
||||
Value *Response
|
||||
}
|
||||
|
||||
// Responses is specified by OpenAPI/Swagger 3.0 standard.
|
||||
type Responses map[string]ResponseRef
|
||||
|
||||
func (r ResponseRef) MarshalJSON() ([]byte, error) {
|
||||
if r.Ref != "" {
|
||||
return formatRefToBytes(r.Ref), nil
|
||||
}
|
||||
return json.Marshal(r.Value)
|
||||
}
|
||||
|
||||
type getResponseSchemaRefInput struct {
|
||||
BusinessStructName string // The business struct name.
|
||||
CommonResponseObject interface{} // Common response object.
|
||||
CommonResponseDataField string // Common response data field.
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) getResponseSchemaRef(in getResponseSchemaRefInput) (*SchemaRef, error) {
|
||||
if in.CommonResponseObject == nil {
|
||||
return &SchemaRef{
|
||||
Ref: in.BusinessStructName,
|
||||
}, nil
|
||||
}
|
||||
|
||||
var (
|
||||
dataFieldsPartsArray = gstr.Split(in.CommonResponseDataField, ".")
|
||||
bizResponseStructSchemaRef = oai.Components.Schemas.Get(in.BusinessStructName)
|
||||
schema, err = oai.structToSchema(in.CommonResponseObject)
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.CommonResponseDataField == "" && bizResponseStructSchemaRef != nil {
|
||||
// Normal response.
|
||||
bizResponseStructSchemaRef.Value.Properties.Iterator(func(key string, ref SchemaRef) bool {
|
||||
schema.Properties.Set(key, ref)
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
// Common response.
|
||||
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
|
||||
Pointer: in.CommonResponseObject,
|
||||
RecursiveOption: gstructs.RecursiveOptionEmbeddedNoTag,
|
||||
})
|
||||
for _, structField := range structFields {
|
||||
var fieldName = structField.Name()
|
||||
if jsonName := structField.TagJsonName(); jsonName != "" {
|
||||
fieldName = jsonName
|
||||
}
|
||||
switch len(dataFieldsPartsArray) {
|
||||
case 1:
|
||||
if structField.Name() == dataFieldsPartsArray[0] {
|
||||
if err = oai.tagMapToSchema(structField.TagMap(), bizResponseStructSchemaRef.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties.Set(fieldName, *bizResponseStructSchemaRef)
|
||||
break
|
||||
}
|
||||
default:
|
||||
// Recursively creating common response object schema.
|
||||
if structField.Name() == dataFieldsPartsArray[0] {
|
||||
var structFieldInstance = reflect.New(structField.Type().Type).Elem()
|
||||
schemaRef, err := oai.getResponseSchemaRef(getResponseSchemaRefInput{
|
||||
BusinessStructName: in.BusinessStructName,
|
||||
CommonResponseObject: structFieldInstance,
|
||||
CommonResponseDataField: gstr.Join(dataFieldsPartsArray[1:], "."),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties.Set(fieldName, *schemaRef)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &SchemaRef{
|
||||
Value: schema,
|
||||
}, nil
|
||||
}
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/container/gset"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
"github.com/gogf/gf/v2/internal/utils"
|
||||
"github.com/gogf/gf/v2/os/gstructs"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
@ -58,6 +59,29 @@ type Schema struct {
|
||||
MaxProps *uint64 `json:"maxProperties,omitempty"`
|
||||
AdditionalProperties *SchemaRef `json:"additionalProperties,omitempty"`
|
||||
Discriminator *Discriminator `json:"discriminator,omitempty"`
|
||||
XExtensions `json:"-"`
|
||||
}
|
||||
|
||||
func (s Schema) MarshalJSON() ([]byte, error) {
|
||||
var (
|
||||
b []byte
|
||||
m map[string]json.RawMessage
|
||||
err error
|
||||
)
|
||||
type tempSchema Schema // To prevent JSON marshal recursion error.
|
||||
if b, err = json.Marshal(tempSchema(s)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = json.Unmarshal(b, &m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for k, v := range s.XExtensions {
|
||||
if b, err = json.Marshal(v); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m[k] = b
|
||||
}
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// Discriminator is specified by OpenAPI/Swagger standard version 3.0.
|
||||
@ -111,7 +135,8 @@ func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) {
|
||||
var (
|
||||
tagMap = gmeta.Data(object)
|
||||
schema = &Schema{
|
||||
Properties: createSchemas(),
|
||||
Properties: createSchemas(),
|
||||
XExtensions: make(XExtensions),
|
||||
}
|
||||
)
|
||||
if len(tagMap) > 0 {
|
||||
@ -173,6 +198,7 @@ func (oai *OpenApiV3) tagMapToSchema(tagMap map[string]string, schema *Schema) e
|
||||
if err := gconv.Struct(mergedTagMap, schema); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Schema failed`)
|
||||
}
|
||||
oai.tagMapToXExtensions(mergedTagMap, schema.XExtensions)
|
||||
// Validation info to OpenAPI schema pattern.
|
||||
for _, tag := range gvalid.GetTags() {
|
||||
if validationTagValue, ok := tagMap[tag]; ok {
|
||||
|
@ -25,8 +25,9 @@ func (oai *OpenApiV3) newSchemaRefWithGolangType(golangType reflect.Type, tagMap
|
||||
oaiFormat = oai.golangTypeToOAIFormat(golangType)
|
||||
schemaRef = &SchemaRef{}
|
||||
schema = &Schema{
|
||||
Type: oaiType,
|
||||
Format: oaiFormat,
|
||||
Type: oaiType,
|
||||
Format: oaiFormat,
|
||||
XExtensions: make(XExtensions),
|
||||
}
|
||||
)
|
||||
if len(tagMap) > 0 {
|
22
protocol/goai/goai_xextensions.go
Normal file
22
protocol/goai/goai_xextensions.go
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package goai
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
)
|
||||
|
||||
// XExtensions stores the `x-` custom extensions.
|
||||
type XExtensions map[string]interface{}
|
||||
|
||||
func (oai *OpenApiV3) tagMapToXExtensions(tagMap map[string]string, extensions XExtensions) {
|
||||
for k, v := range tagMap {
|
||||
if gstr.HasPrefix(k, "x-") || gstr.HasPrefix(k, "X-") {
|
||||
extensions[k] = v
|
||||
}
|
||||
}
|
||||
}
|
@ -802,7 +802,7 @@ func Test_Required_In_Schema(t *testing.T) {
|
||||
|
||||
func Test_Properties_In_Sequence(t *testing.T) {
|
||||
type ResourceCreateReq struct {
|
||||
g.Meta `path:"/resource" tags:"OSS Resource" method:"put" summary:"创建实例(发货)"`
|
||||
g.Meta `path:"/resource" tags:"OSS Resource" method:"put" x-sort:"1" summary:"创建实例(发货)"`
|
||||
AppId uint64 `v:"required" dc:"应用Id"`
|
||||
Uin string `v:"required" dc:"主用户账号,该资源隶属于的账号"`
|
||||
CreateUin string `v:"required" dc:"创建实例的用户账号"`
|
||||
|
@ -194,7 +194,8 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
|
||||
var (
|
||||
tempName string
|
||||
elemFieldType reflect.StructField
|
||||
elemFieldValue reflect.Value
|
||||
elemTempValue reflect.Value
|
||||
elemOriginKind reflect.Kind
|
||||
elemType = pointerElemReflectValue.Type()
|
||||
attrMap = make(map[string]string) // Attribute name to its check name which has no symbols.
|
||||
)
|
||||
@ -204,17 +205,15 @@ func doStruct(params interface{}, pointer interface{}, mapping map[string]string
|
||||
if !utils.IsLetterUpper(elemFieldType.Name[0]) {
|
||||
continue
|
||||
}
|
||||
elemTempValue = pointerElemReflectValue.Field(i)
|
||||
elemOriginKind = elemTempValue.Kind()
|
||||
for elemOriginKind == reflect.Ptr || elemOriginKind == reflect.Interface {
|
||||
elemTempValue = elemTempValue.Elem()
|
||||
elemOriginKind = elemTempValue.Kind()
|
||||
}
|
||||
// Maybe it's struct/*struct embedded.
|
||||
if elemFieldType.Anonymous {
|
||||
elemFieldValue = pointerElemReflectValue.Field(i)
|
||||
// Ignore the interface attribute if it's nil.
|
||||
if elemFieldValue.Kind() == reflect.Interface {
|
||||
elemFieldValue = elemFieldValue.Elem()
|
||||
if !elemFieldValue.IsValid() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err = doStruct(paramsMap, elemFieldValue, mapping, priorityTag); err != nil {
|
||||
if elemFieldType.Anonymous && elemOriginKind == reflect.Struct {
|
||||
if err = doStruct(paramsMap, pointerElemReflectValue.Field(i), mapping, priorityTag); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
|
Loading…
Reference in New Issue
Block a user