mirror of
https://gitee.com/johng/gf.git
synced 2024-11-30 11:18:02 +08:00
Merge branch 'master' of https://github.com/gogf/gf
This commit is contained in:
commit
f08c18594b
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -7,6 +7,9 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/glist"
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
"github.com/gogf/gf/v2/internal/empty"
|
||||
@ -15,6 +18,13 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
// ListMap is a map that preserves insertion-order.
|
||||
//
|
||||
// It is backed by a hash table to store values and doubly-linked list to store ordering.
|
||||
//
|
||||
// Structure is not thread safe.
|
||||
//
|
||||
// Reference: http://en.wikipedia.org/wiki/Associative_array
|
||||
type ListMap struct {
|
||||
mu rwmutex.RWMutex
|
||||
data map[interface{}]*glist.Element
|
||||
@ -58,7 +68,7 @@ func (m *ListMap) IteratorAsc(f func(key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if m.list != nil {
|
||||
node := (*gListMapNode)(nil)
|
||||
var node *gListMapNode
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
@ -72,7 +82,7 @@ func (m *ListMap) IteratorDesc(f func(key interface{}, value interface{}) bool)
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
if m.list != nil {
|
||||
node := (*gListMapNode)(nil)
|
||||
var node *gListMapNode
|
||||
m.list.IteratorDesc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
@ -146,8 +156,10 @@ func (m *ListMap) MapStrAny() map[string]interface{} {
|
||||
func (m *ListMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
if m.list != nil {
|
||||
keys := make([]interface{}, 0)
|
||||
node := (*gListMapNode)(nil)
|
||||
var (
|
||||
keys = make([]interface{}, 0)
|
||||
node *gListMapNode
|
||||
)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if empty.IsEmpty(node.value) {
|
||||
@ -495,7 +507,7 @@ func (m *ListMap) Merge(other *ListMap) {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
node := (*gListMapNode)(nil)
|
||||
var node *gListMapNode
|
||||
other.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if e, ok := m.data[node.key]; !ok {
|
||||
@ -514,8 +526,26 @@ func (m *ListMap) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m ListMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(m.Map()))
|
||||
func (m ListMap) MarshalJSON() (jsonBytes []byte, err error) {
|
||||
if m.data == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('{')
|
||||
m.Iterator(func(key, value interface{}) bool {
|
||||
valueBytes, valueJsonErr := json.Marshal(value)
|
||||
if valueJsonErr != nil {
|
||||
err = valueJsonErr
|
||||
return false
|
||||
}
|
||||
if buffer.Len() > 1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`"%v":%s`, key, valueBytes))
|
||||
return true
|
||||
})
|
||||
buffer.WriteByte('}')
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
|
@ -8,6 +8,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gmap"
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
@ -195,7 +196,7 @@ func ExampleListMap_Sets() {
|
||||
m.Sets(addMap)
|
||||
fmt.Println(m)
|
||||
|
||||
// Output:
|
||||
// May Output:
|
||||
// {"key1":"val1","key2":"val2","key3":"val3"}
|
||||
}
|
||||
|
||||
@ -562,12 +563,10 @@ func ExampleListMap_String() {
|
||||
|
||||
func ExampleListMap_MarshalJSON() {
|
||||
var m gmap.ListMap
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
m.Set("k1", "v1")
|
||||
m.Set("k2", "v2")
|
||||
m.Set("k3", "v3")
|
||||
m.Set("k4", "v4")
|
||||
|
||||
bytes, err := json.Marshal(&m)
|
||||
if err == nil {
|
||||
|
@ -190,8 +190,9 @@ func Test_ListMap_Json(t *testing.T) {
|
||||
}
|
||||
m1 := gmap.NewListMapFrom(data)
|
||||
b1, err1 := json.Marshal(m1)
|
||||
t.AssertNil(err1)
|
||||
b2, err2 := json.Marshal(gconv.Map(data))
|
||||
t.Assert(err1, err2)
|
||||
t.AssertNil(err2)
|
||||
t.Assert(b1, b2)
|
||||
})
|
||||
// Unmarshal
|
||||
@ -226,6 +227,27 @@ func Test_ListMap_Json(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Json_Sequence(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
for i := 'z'; i >= 'a'; i-- {
|
||||
m.Set(string(i), i)
|
||||
}
|
||||
b, err := json.Marshal(m)
|
||||
t.AssertNil(err)
|
||||
t.Assert(b, `{"z":122,"y":121,"x":120,"w":119,"v":118,"u":117,"t":116,"s":115,"r":114,"q":113,"p":112,"o":111,"n":110,"m":109,"l":108,"k":107,"j":106,"i":105,"h":104,"g":103,"f":102,"e":101,"d":100,"c":99,"b":98,"a":97}`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
for i := 'a'; i <= 'z'; i++ {
|
||||
m.Set(string(i), i)
|
||||
}
|
||||
b, err := json.Marshal(m)
|
||||
t.AssertNil(err)
|
||||
t.Assert(b, `{"a":97,"b":98,"c":99,"d":100,"e":101,"f":102,"g":103,"h":104,"i":105,"j":106,"k":107,"l":108,"m":109,"n":110,"o":111,"p":112,"q":113,"r":114,"s":115,"t":116,"u":117,"v":118,"w":119,"x":120,"y":121,"z":122}`)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMapFrom(g.MapAnyAny{
|
||||
|
@ -109,7 +109,7 @@ func (p *Pool) Get() (interface{}, error) {
|
||||
if f.expireAt == 0 || f.expireAt > gtime.TimestampMilli() {
|
||||
return f.value, nil
|
||||
} else if p.ExpireFunc != nil {
|
||||
// TODO: move expire function calling asynchronously from `Get` operation.
|
||||
// TODO: move expire function calling asynchronously out from `Get` operation.
|
||||
p.ExpireFunc(f.value)
|
||||
}
|
||||
} else {
|
||||
@ -129,7 +129,7 @@ func (p *Pool) Size() int {
|
||||
|
||||
// Close closes the pool. If `p` has ExpireFunc,
|
||||
// then it automatically closes all items using this function before it's closed.
|
||||
// Commonly you do not need call this function manually.
|
||||
// Commonly you do not need to call this function manually.
|
||||
func (p *Pool) Close() {
|
||||
p.closed.Set(true)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
package gtree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
@ -780,8 +781,26 @@ func output(node *AVLTreeNode, prefix string, isTail bool, str *string) {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (tree AVLTree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(tree.MapStrAny())
|
||||
func (tree AVLTree) MarshalJSON() (jsonBytes []byte, err error) {
|
||||
if tree.root == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('{')
|
||||
tree.Iterator(func(key, value interface{}) bool {
|
||||
valueBytes, valueJsonErr := json.Marshal(value)
|
||||
if valueJsonErr != nil {
|
||||
err = valueJsonErr
|
||||
return false
|
||||
}
|
||||
if buffer.Len() > 1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`"%v":%s`, key, valueBytes))
|
||||
return true
|
||||
})
|
||||
buffer.WriteByte('}')
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
|
@ -944,8 +944,26 @@ func (tree *BTree) deleteChild(node *BTreeNode, index int) {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (tree BTree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(tree.MapStrAny())
|
||||
func (tree BTree) MarshalJSON() (jsonBytes []byte, err error) {
|
||||
if tree.root == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('{')
|
||||
tree.Iterator(func(key, value interface{}) bool {
|
||||
valueBytes, valueJsonErr := json.Marshal(value)
|
||||
if valueJsonErr != nil {
|
||||
err = valueJsonErr
|
||||
return false
|
||||
}
|
||||
if buffer.Len() > 1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`"%v":%s`, key, valueBytes))
|
||||
return true
|
||||
})
|
||||
buffer.WriteByte('}')
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
|
@ -7,6 +7,7 @@
|
||||
package gtree
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/v2/container/gvar"
|
||||
@ -925,8 +926,26 @@ func (tree *RedBlackTree) nodeColor(node *RedBlackTreeNode) color {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (tree RedBlackTree) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(tree.Map()))
|
||||
func (tree RedBlackTree) MarshalJSON() (jsonBytes []byte, err error) {
|
||||
if tree.root == nil {
|
||||
return []byte("null"), nil
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteByte('{')
|
||||
tree.Iterator(func(key, value interface{}) bool {
|
||||
valueBytes, valueJsonErr := json.Marshal(value)
|
||||
if valueJsonErr != nil {
|
||||
err = valueJsonErr
|
||||
return false
|
||||
}
|
||||
if buffer.Len() > 1 {
|
||||
buffer.WriteByte(',')
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf(`"%v":%s`, key, valueBytes))
|
||||
return true
|
||||
})
|
||||
buffer.WriteByte('}')
|
||||
return buffer.Bytes(), nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
|
@ -23,8 +23,8 @@ import (
|
||||
|
||||
// UploadFile wraps the multipart uploading file with more and convenient features.
|
||||
type UploadFile struct {
|
||||
*multipart.FileHeader
|
||||
ctx context.Context
|
||||
*multipart.FileHeader `json:"-"`
|
||||
ctx context.Context
|
||||
}
|
||||
|
||||
// UploadFiles is array type for *UploadFile.
|
||||
@ -32,7 +32,7 @@ type UploadFiles []*UploadFile
|
||||
|
||||
// Save saves the single uploading file to directory path and returns the saved file name.
|
||||
//
|
||||
// The parameter `dirPath` should be a directory path or it returns error.
|
||||
// The parameter `dirPath` should be a directory path, or it returns error.
|
||||
//
|
||||
// Note that it will OVERWRITE the target file if there's already a same name file exist.
|
||||
func (f *UploadFile) Save(dirPath string, randomlyRename ...bool) (filename string, err error) {
|
||||
|
@ -66,14 +66,10 @@ func (r *Request) GetRequestMap(kvMap ...map[string]interface{}) map[string]inte
|
||||
var (
|
||||
ok, filter bool
|
||||
)
|
||||
var length int
|
||||
if len(kvMap) > 0 && kvMap[0] != nil {
|
||||
length = len(kvMap[0])
|
||||
filter = true
|
||||
} else {
|
||||
length = len(r.routerMap) + len(r.queryMap) + len(r.formMap) + len(r.bodyMap) + len(r.paramsMap)
|
||||
}
|
||||
m := make(map[string]interface{}, length)
|
||||
m := make(map[string]interface{})
|
||||
for k, v := range r.routerMap {
|
||||
if filter {
|
||||
if _, ok = kvMap[0][k]; !ok {
|
||||
@ -114,6 +110,16 @@ func (r *Request) GetRequestMap(kvMap ...map[string]interface{}) map[string]inte
|
||||
}
|
||||
m[k] = v
|
||||
}
|
||||
// File uploading.
|
||||
if r.MultipartForm != nil {
|
||||
for name := range r.MultipartForm.File {
|
||||
if uploadFiles := r.GetUploadFiles(name); len(uploadFiles) == 1 {
|
||||
m[name] = uploadFiles[0]
|
||||
} else {
|
||||
m[name] = uploadFiles
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check none exist parameters and assign it with default value.
|
||||
if filter {
|
||||
for k, v := range kvMap[0] {
|
||||
@ -171,9 +177,11 @@ func (r *Request) doGetRequestStruct(pointer interface{}, mapping ...map[string]
|
||||
if data == nil {
|
||||
data = map[string]interface{}{}
|
||||
}
|
||||
// Default struct values.
|
||||
if err = r.mergeDefaultStructValue(data, pointer); err != nil {
|
||||
return data, nil
|
||||
}
|
||||
|
||||
return data, gconv.Struct(data, pointer, mapping...)
|
||||
}
|
||||
|
||||
|
@ -92,7 +92,7 @@ func (r *Response) ServeFileDownload(path string, name ...string) {
|
||||
}
|
||||
r.Header().Set("Content-Type", "application/force-download")
|
||||
r.Header().Set("Accept-Ranges", "bytes")
|
||||
r.Header().Set("Content-Disposition", fmt.Sprintf(`attachment;filename="%s"`, url.QueryEscape(downloadName)))
|
||||
r.Header().Set("Content-Disposition", fmt.Sprintf(`attachment;filename=%s`, url.QueryEscape(downloadName)))
|
||||
r.Server.serveFile(r.Request, serveFile)
|
||||
}
|
||||
|
||||
|
@ -128,7 +128,7 @@ func (s *Server) Start() error {
|
||||
swaggerui.Init()
|
||||
s.AddStaticPath(s.config.SwaggerPath, swaggerUIPackedPath)
|
||||
s.BindHookHandler(s.config.SwaggerPath+"/*", HookBeforeServe, s.swaggerUI)
|
||||
s.Logger().Debugf(
|
||||
s.Logger().Infof(
|
||||
ctx,
|
||||
`swagger ui is serving at address: %s%s/`,
|
||||
s.getListenAddress(),
|
||||
|
@ -150,6 +150,18 @@ type ServerConfig struct {
|
||||
// It also affects the default storage for session id.
|
||||
CookieDomain string `json:"cookieDomain"`
|
||||
|
||||
// CookieSameSite specifies cookie SameSite property.
|
||||
// It also affects the default storage for session id.
|
||||
CookieSameSite string `json:"cookieSameSite"`
|
||||
|
||||
// CookieSameSite specifies cookie Secure property.
|
||||
// It also affects the default storage for session id.
|
||||
CookieSecure bool `json:"cookieSecure"`
|
||||
|
||||
// CookieSameSite specifies cookie HttpOnly property.
|
||||
// It also affects the default storage for session id.
|
||||
CookieHttpOnly bool `json:"cookieHttpOnly"`
|
||||
|
||||
// ======================================================================================================
|
||||
// Session.
|
||||
// ======================================================================================================
|
||||
|
@ -7,6 +7,7 @@
|
||||
package ghttp
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
@ -39,3 +40,25 @@ func (s *Server) GetCookiePath() string {
|
||||
func (s *Server) GetCookieDomain() string {
|
||||
return s.config.CookieDomain
|
||||
}
|
||||
|
||||
// GetCookieSameSite return CookieSameSite of server.
|
||||
func (s *Server) GetCookieSameSite() http.SameSite {
|
||||
switch s.config.CookieSameSite {
|
||||
case "lax":
|
||||
return http.SameSiteLaxMode
|
||||
case "none":
|
||||
return http.SameSiteNoneMode
|
||||
case "strict":
|
||||
return http.SameSiteStrictMode
|
||||
default:
|
||||
return http.SameSiteDefaultMode
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) GetCookieSecure() bool {
|
||||
return s.config.CookieSecure
|
||||
}
|
||||
|
||||
func (s *Server) GetCookieHttpOnly() bool {
|
||||
return s.config.CookieHttpOnly
|
||||
}
|
||||
|
@ -21,6 +21,13 @@ type Cookie struct {
|
||||
response *Response // Belonged HTTP response.
|
||||
}
|
||||
|
||||
// CookieOptions provides security config for cookies
|
||||
type CookieOptions struct {
|
||||
SameSite http.SameSite // cookie SameSite property
|
||||
Secure bool // cookie Secure property
|
||||
HttpOnly bool // cookie HttpOnly property
|
||||
}
|
||||
|
||||
// cookieItem is the item stored in Cookie.
|
||||
type cookieItem struct {
|
||||
*http.Cookie // Underlying cookie items.
|
||||
@ -88,24 +95,31 @@ func (c *Cookie) Set(key, value string) {
|
||||
c.request.Server.GetCookieDomain(),
|
||||
c.request.Server.GetCookiePath(),
|
||||
c.request.Server.GetCookieMaxAge(),
|
||||
CookieOptions{
|
||||
SameSite: c.request.Server.GetCookieSameSite(),
|
||||
Secure: c.request.Server.GetCookieSecure(),
|
||||
HttpOnly: c.request.Server.GetCookieHttpOnly(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// SetCookie sets cookie item with given domain, path and expiration age.
|
||||
// The optional parameter `httpOnly` specifies if the cookie item is only available in HTTP,
|
||||
// The optional parameter `options` specifies extra security configurations,
|
||||
// which is usually empty.
|
||||
func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration, httpOnly ...bool) {
|
||||
func (c *Cookie) SetCookie(key, value, domain, path string, maxAge time.Duration, options ...CookieOptions) {
|
||||
c.init()
|
||||
isHttpOnly := false
|
||||
if len(httpOnly) > 0 {
|
||||
isHttpOnly = httpOnly[0]
|
||||
config := CookieOptions{}
|
||||
if len(options) > 0 {
|
||||
config = options[0]
|
||||
}
|
||||
httpCookie := &http.Cookie{
|
||||
Name: key,
|
||||
Value: value,
|
||||
Path: path,
|
||||
Domain: domain,
|
||||
HttpOnly: isHttpOnly,
|
||||
HttpOnly: config.HttpOnly,
|
||||
SameSite: config.SameSite,
|
||||
Secure: config.Secure,
|
||||
}
|
||||
if maxAge != 0 {
|
||||
httpCookie.Expires = time.Now().Add(maxAge)
|
||||
@ -136,6 +150,11 @@ func (c *Cookie) SetSessionId(id string) {
|
||||
c.request.Server.GetCookieDomain(),
|
||||
c.request.Server.GetCookiePath(),
|
||||
c.server.GetSessionCookieMaxAge(),
|
||||
CookieOptions{
|
||||
SameSite: c.request.Server.GetCookieSameSite(),
|
||||
Secure: c.request.Server.GetCookieSecure(),
|
||||
HttpOnly: c.request.Server.GetCookieHttpOnly(),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
swaggerUIDefaultURL = `https://petstore.swagger.io/v2/swagger.json`
|
||||
swaggerUITemplate = `
|
||||
swaggerUIDocName = `redoc.standalone.js`
|
||||
swaggerUIDocNamePlaceHolder = `{SwaggerUIDocName}`
|
||||
swaggerUIDocURLPlaceHolder = `{SwaggerUIDocUrl}`
|
||||
swaggerUITemplate = `
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
@ -27,8 +29,8 @@ const (
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<redoc spec-url="https://petstore.swagger.io/v2/swagger.json"></redoc>
|
||||
<script src="redoc.standalone.js"> </script>
|
||||
<redoc spec-url="{SwaggerUIDocUrl}"></redoc>
|
||||
<script src="{SwaggerUIDocName}"> </script>
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
@ -41,11 +43,11 @@ func (s *Server) swaggerUI(r *Request) {
|
||||
return
|
||||
}
|
||||
if r.StaticFile != nil && r.StaticFile.File != nil && r.StaticFile.IsDir {
|
||||
r.Response.Write(gstr.Replace(
|
||||
swaggerUITemplate,
|
||||
swaggerUIDefaultURL,
|
||||
s.config.OpenApiPath,
|
||||
))
|
||||
content := gstr.ReplaceByMap(swaggerUITemplate, map[string]string{
|
||||
swaggerUIDocURLPlaceHolder: s.config.OpenApiPath,
|
||||
swaggerUIDocNamePlaceHolder: gstr.TrimRight(r.GetUrl(), "/") + "/" + swaggerUIDocName,
|
||||
})
|
||||
r.Response.Write(content)
|
||||
r.ExitAll()
|
||||
}
|
||||
}
|
||||
|
@ -29,6 +29,9 @@ func Test_ConfigFromMap(t *testing.T) {
|
||||
"indexFiles": g.Slice{"index.php", "main.php"},
|
||||
"errorLogEnabled": true,
|
||||
"cookieMaxAge": "1y",
|
||||
"cookieSameSite": "lax",
|
||||
"cookieSecure": true,
|
||||
"cookieHttpOnly": true,
|
||||
}
|
||||
config, err := ghttp.ConfigFromMap(m)
|
||||
t.Assert(err, nil)
|
||||
@ -39,6 +42,9 @@ func Test_ConfigFromMap(t *testing.T) {
|
||||
t.Assert(config.CookieMaxAge, d2)
|
||||
t.Assert(config.IndexFiles, m["indexFiles"])
|
||||
t.Assert(config.ErrorLogEnabled, m["errorLogEnabled"])
|
||||
t.Assert(config.CookieSameSite, m["cookieSameSite"])
|
||||
t.Assert(config.CookieSecure, m["cookieSecure"])
|
||||
t.Assert(config.CookieHttpOnly, m["cookieHttpOnly"])
|
||||
})
|
||||
}
|
||||
|
||||
@ -55,6 +61,9 @@ func Test_SetConfigWithMap(t *testing.T) {
|
||||
"SessionIdName": "MySessionId",
|
||||
"SessionPath": "/tmp/MySessionStoragePath",
|
||||
"SessionMaxAge": 24 * time.Hour,
|
||||
"cookieSameSite": "lax",
|
||||
"cookieSecure": true,
|
||||
"cookieHttpOnly": true,
|
||||
}
|
||||
s := g.Server()
|
||||
err := s.SetConfigWithMap(m)
|
||||
|
@ -9,6 +9,7 @@ package ghttp_test
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -101,3 +102,67 @@ func Test_SetHttpCookie(t *testing.T) {
|
||||
//t.Assert(client.GetContent(ctx, "/get?k=key2"), "200")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CookieOptionsDefault(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/test", func(r *ghttp.Request) {
|
||||
r.Cookie.Set(r.Get("k").String(), r.Get("v").String())
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetBrowserMode(true)
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
r1, e1 := client.Get(ctx, "/test?k=key1&v=100")
|
||||
if r1 != nil {
|
||||
defer r1.Close()
|
||||
}
|
||||
|
||||
t.Assert(e1, nil)
|
||||
t.Assert(r1.ReadAllString(), "")
|
||||
|
||||
parts := strings.Split(r1.Header.Get("Set-Cookie"), "; ")
|
||||
|
||||
t.AssertIN(len(parts), []int{3, 4}) // For go < 1.16 cookie always output "SameSite", see: https://github.com/golang/go/commit/542693e00529fbb4248fac614ece68b127a5ec4d
|
||||
})
|
||||
}
|
||||
|
||||
func Test_CookieOptions(t *testing.T) {
|
||||
s := g.Server(guid.S())
|
||||
s.SetConfigWithMap(g.Map{
|
||||
"cookieSameSite": "lax",
|
||||
"cookieSecure": true,
|
||||
"cookieHttpOnly": true,
|
||||
})
|
||||
s.BindHandler("/test", func(r *ghttp.Request) {
|
||||
r.Cookie.Set(r.Get("k").String(), r.Get("v").String())
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetBrowserMode(true)
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
r1, e1 := client.Get(ctx, "/test?k=key1&v=100")
|
||||
if r1 != nil {
|
||||
defer r1.Close()
|
||||
}
|
||||
|
||||
t.Assert(e1, nil)
|
||||
t.Assert(r1.ReadAllString(), "")
|
||||
|
||||
parts := strings.Split(r1.Header.Get("Set-Cookie"), "; ")
|
||||
|
||||
t.AssertEQ(len(parts), 6)
|
||||
t.Assert(parts[3], "HttpOnly")
|
||||
t.Assert(parts[4], "Secure")
|
||||
t.Assert(parts[5], "SameSite=Lax")
|
||||
})
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
package ghttp_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"testing"
|
||||
"time"
|
||||
@ -18,6 +19,7 @@ import (
|
||||
"github.com/gogf/gf/v2/os/gtime"
|
||||
"github.com/gogf/gf/v2/test/gtest"
|
||||
"github.com/gogf/gf/v2/text/gstr"
|
||||
"github.com/gogf/gf/v2/util/gmeta"
|
||||
"github.com/gogf/gf/v2/util/guid"
|
||||
)
|
||||
|
||||
@ -169,3 +171,49 @@ func Test_Params_File_Batch(t *testing.T) {
|
||||
t.Assert(gfile.GetContents(dstPath2), gfile.GetContents(srcPath2))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Params_Strict_Route_File_Single(t *testing.T) {
|
||||
type Req struct {
|
||||
gmeta.Meta `method:"post" mime:"multipart/form-data"`
|
||||
File *ghttp.UploadFile `type:"file"`
|
||||
}
|
||||
type Res struct{}
|
||||
|
||||
dstDirPath := gfile.Temp(gtime.TimestampNanoStr())
|
||||
s := g.Server(guid.S())
|
||||
s.BindHandler("/upload/single", func(ctx context.Context, req *Req) (res *Res, err error) {
|
||||
var (
|
||||
r = g.RequestFromCtx(ctx)
|
||||
file = req.File
|
||||
)
|
||||
if file == nil {
|
||||
r.Response.WriteExit("upload file cannot be empty")
|
||||
}
|
||||
name, err := file.Save(dstDirPath)
|
||||
if err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit(name)
|
||||
return
|
||||
})
|
||||
s.SetDumpRouterMap(false)
|
||||
s.Start()
|
||||
defer s.Shutdown()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
// normal name
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
client := g.Client()
|
||||
client.SetPrefix(fmt.Sprintf("http://127.0.0.1:%d", s.GetListenedPort()))
|
||||
|
||||
srcPath := gdebug.TestDataPath("upload", "file1.txt")
|
||||
dstPath := gfile.Join(dstDirPath, "file1.txt")
|
||||
content := client.PostContent(ctx, "/upload/single", g.Map{
|
||||
"file": "@file:" + srcPath,
|
||||
})
|
||||
t.AssertNE(content, "")
|
||||
t.AssertNE(content, "upload file cannot be empty")
|
||||
t.AssertNE(content, "upload failed")
|
||||
t.Assert(content, "file1.txt")
|
||||
t.Assert(gfile.GetContents(dstPath), gfile.GetContents(srcPath))
|
||||
})
|
||||
}
|
||||
|
@ -221,9 +221,7 @@ func newCommandFromMethod(object interface{}, method reflect.Value) (command *Co
|
||||
return
|
||||
}
|
||||
|
||||
var (
|
||||
inputObject reflect.Value
|
||||
)
|
||||
var inputObject reflect.Value
|
||||
if method.Type().In(1).Kind() == reflect.Ptr {
|
||||
inputObject = reflect.New(method.Type().In(1).Elem()).Elem()
|
||||
} else {
|
||||
@ -264,8 +262,19 @@ func newCommandFromMethod(object interface{}, method reflect.Value) (command *Co
|
||||
}
|
||||
} else {
|
||||
// Read argument from command line option name.
|
||||
if arg.Orphan && parser.GetOpt(arg.Name) != nil {
|
||||
data[arg.Name] = "true"
|
||||
if arg.Orphan {
|
||||
if orphanValue := parser.GetOpt(arg.Name); orphanValue != nil {
|
||||
if orphanValue.String() == "" {
|
||||
// Eg: gf -f
|
||||
data[arg.Name] = "true"
|
||||
} else {
|
||||
// Adapter with common user habits.
|
||||
// Eg:
|
||||
// `gf -f=0`: which parameter `f` is parsed as false
|
||||
// `gf -f=1`: which parameter `f` is parsed as true
|
||||
data[arg.Name] = orphanValue.Bool()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -258,7 +258,7 @@ func Test_Command_Pointer(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
t.Assert(value, `{"Content":"john"}`)
|
||||
})
|
||||
return
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
ctx = gctx.New()
|
||||
@ -272,3 +272,42 @@ func Test_Command_Pointer(t *testing.T) {
|
||||
t.Assert(value, `{"Content":"john"}`)
|
||||
})
|
||||
}
|
||||
|
||||
type TestCommandOrphan struct {
|
||||
g.Meta `name:"root" root:"root"`
|
||||
}
|
||||
|
||||
type TestCommandOrphanIndexInput struct {
|
||||
g.Meta `name:"index"`
|
||||
Orphan1 bool `short:"n1" orphan:"true"`
|
||||
Orphan2 bool `short:"n2" orphan:"true"`
|
||||
Orphan3 bool `short:"n3" orphan:"true"`
|
||||
}
|
||||
type TestCommandOrphanIndexOutput struct {
|
||||
Orphan1 bool
|
||||
Orphan2 bool
|
||||
Orphan3 bool
|
||||
}
|
||||
|
||||
func (c *TestCommandOrphan) Index(ctx context.Context, in TestCommandOrphanIndexInput) (out *TestCommandOrphanIndexOutput, err error) {
|
||||
out = &TestCommandOrphanIndexOutput{
|
||||
Orphan1: in.Orphan1,
|
||||
Orphan2: in.Orphan2,
|
||||
Orphan3: in.Orphan3,
|
||||
}
|
||||
return
|
||||
}
|
||||
func Test_Command_Orphan_Parameter(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var ctx = gctx.New()
|
||||
cmd, err := gcmd.NewFromObject(TestCommandOrphan{})
|
||||
t.AssertNil(err)
|
||||
|
||||
os.Args = []string{"root", "index", "-n1", "-n2=0", "-n3=1"}
|
||||
value, err := cmd.RunWithValueError(ctx)
|
||||
t.AssertNil(err)
|
||||
t.Assert(value.(*TestCommandOrphanIndexOutput).Orphan1, true)
|
||||
t.Assert(value.(*TestCommandOrphanIndexOutput).Orphan2, false)
|
||||
t.Assert(value.(*TestCommandOrphanIndexOutput).Orphan3, true)
|
||||
})
|
||||
}
|
||||
|
@ -107,10 +107,12 @@ func getCommPidFolderPath() (folderPath string, err error) {
|
||||
break
|
||||
}
|
||||
}
|
||||
err = gerror.Newf(
|
||||
`cannot find available folder for storing pid to port mapping files in paths: %+v`,
|
||||
availablePaths,
|
||||
)
|
||||
if commPidFolderPath == "" {
|
||||
err = gerror.Newf(
|
||||
`cannot find available folder for storing pid to port mapping files in paths: %+v`,
|
||||
availablePaths,
|
||||
)
|
||||
}
|
||||
})
|
||||
folderPath = commPidFolderPath
|
||||
return
|
||||
|
@ -43,7 +43,6 @@ type ExternalDocs struct {
|
||||
}
|
||||
|
||||
const (
|
||||
HttpMethodAll = `ALL`
|
||||
HttpMethodGet = `GET`
|
||||
HttpMethodPut = `PUT`
|
||||
HttpMethodPost = `POST`
|
||||
@ -82,9 +81,14 @@ const (
|
||||
TagNamePath = `path`
|
||||
TagNameMethod = `method`
|
||||
TagNameMime = `mime`
|
||||
TagNameConsumes = `consumes`
|
||||
TagNameType = `type`
|
||||
TagNameDomain = `domain`
|
||||
TagNameValidate = `v`
|
||||
)
|
||||
|
||||
const (
|
||||
patternKeyForRequired = `required`
|
||||
patternKeyForIn = `in:`
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -9,6 +9,7 @@ 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"
|
||||
@ -54,7 +55,8 @@ func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path
|
||||
parameter.Name = field.Name()
|
||||
}
|
||||
if len(tagMap) > 0 {
|
||||
if err := gconv.Struct(oai.fileMapWithShortTags(tagMap), parameter); err != nil {
|
||||
err := gconv.Struct(oai.fileMapWithShortTags(tagMap), parameter)
|
||||
if err != nil {
|
||||
return nil, gerror.Wrap(err, `mapping struct tags to Parameter failed`)
|
||||
}
|
||||
}
|
||||
@ -80,10 +82,6 @@ func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path
|
||||
parameter.Required = true
|
||||
|
||||
case ParameterInCookie, ParameterInHeader, ParameterInQuery:
|
||||
// Check validation tag.
|
||||
if validateTagValue := field.Tag(TagNameValidate); gstr.ContainsI(validateTagValue, `required`) {
|
||||
parameter.Required = true
|
||||
}
|
||||
|
||||
default:
|
||||
return nil, gerror.NewCodef(gcode.CodeInvalidParameter, `invalid tag value "%s" for In`, parameter.In)
|
||||
@ -95,6 +93,13 @@ func (oai *OpenApiV3) newParameterRefWithStructMethod(field gstructs.Field, path
|
||||
}
|
||||
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,
|
||||
|
@ -80,6 +80,7 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
}
|
||||
|
||||
var (
|
||||
mime string
|
||||
path = Path{}
|
||||
inputMetaMap = gmeta.Data(inputObject.Interface())
|
||||
outputMetaMap = gmeta.Data(outputObject.Interface())
|
||||
@ -126,12 +127,17 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
}
|
||||
|
||||
if len(inputMetaMap) > 0 {
|
||||
if err := gconv.Struct(oai.fileMapWithShortTags(inputMetaMap), &path); err != nil {
|
||||
inputMetaMap = oai.fileMapWithShortTags(inputMetaMap)
|
||||
if err := gconv.Struct(inputMetaMap, &path); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Path failed`)
|
||||
}
|
||||
if err := gconv.Struct(oai.fileMapWithShortTags(inputMetaMap), &operation); err != nil {
|
||||
if err := gconv.Struct(inputMetaMap, &operation); err != nil {
|
||||
return gerror.Wrap(err, `mapping struct tags to Operation failed`)
|
||||
}
|
||||
// Allowed request mime.
|
||||
if mime = inputMetaMap[TagNameMime]; mime == "" {
|
||||
mime = inputMetaMap[TagNameConsumes]
|
||||
}
|
||||
}
|
||||
|
||||
// =================================================================================================================
|
||||
|
@ -47,18 +47,19 @@ func (oai *OpenApiV3) getRequestSchemaRef(in getRequestSchemaRefInput) (*SchemaR
|
||||
}
|
||||
|
||||
var (
|
||||
dataFieldsPartsArray = gstr.Split(in.RequestDataField, ".")
|
||||
bizRequestStructSchemaRef, bizRequestStructSchemaRefExist = oai.Components.Schemas[in.BusinessStructName]
|
||||
schema, err = oai.structToSchema(in.RequestObject)
|
||||
dataFieldsPartsArray = gstr.Split(in.RequestDataField, ".")
|
||||
bizRequestStructSchemaRef = oai.Components.Schemas.Get(in.BusinessStructName)
|
||||
schema, err = oai.structToSchema(in.RequestObject)
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if in.RequestDataField == "" && bizRequestStructSchemaRefExist {
|
||||
if in.RequestDataField == "" && bizRequestStructSchemaRef != nil {
|
||||
// Normal request.
|
||||
for k, v := range bizRequestStructSchemaRef.Value.Properties {
|
||||
schema.Properties[k] = v
|
||||
}
|
||||
bizRequestStructSchemaRef.Value.Properties.Iterator(func(key string, ref SchemaRef) bool {
|
||||
schema.Properties.Set(key, ref)
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
// Common request.
|
||||
structFields, _ := gstructs.Fields(gstructs.FieldsInput{
|
||||
@ -76,7 +77,7 @@ func (oai *OpenApiV3) getRequestSchemaRef(in getRequestSchemaRefInput) (*SchemaR
|
||||
if err = oai.tagMapToSchema(structField.TagMap(), bizRequestStructSchemaRef.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties[fieldName] = bizRequestStructSchemaRef
|
||||
schema.Properties.Set(fieldName, *bizRequestStructSchemaRef)
|
||||
break
|
||||
}
|
||||
default:
|
||||
@ -90,13 +91,12 @@ func (oai *OpenApiV3) getRequestSchemaRef(in getRequestSchemaRefInput) (*SchemaR
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties[fieldName] = *schemaRef
|
||||
schema.Properties.Set(fieldName, *schemaRef)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return &SchemaRef{
|
||||
Value: schema,
|
||||
}, nil
|
||||
|
@ -51,18 +51,19 @@ func (oai *OpenApiV3) getResponseSchemaRef(in getResponseSchemaRefInput) (*Schem
|
||||
}
|
||||
|
||||
var (
|
||||
dataFieldsPartsArray = gstr.Split(in.CommonResponseDataField, ".")
|
||||
bizResponseStructSchemaRef, bizResponseStructSchemaRefExist = oai.Components.Schemas[in.BusinessStructName]
|
||||
schema, err = oai.structToSchema(in.CommonResponseObject)
|
||||
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 == "" && bizResponseStructSchemaRefExist {
|
||||
if in.CommonResponseDataField == "" && bizResponseStructSchemaRef != nil {
|
||||
// Normal response.
|
||||
for k, v := range bizResponseStructSchemaRef.Value.Properties {
|
||||
schema.Properties[k] = v
|
||||
}
|
||||
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{
|
||||
@ -80,7 +81,7 @@ func (oai *OpenApiV3) getResponseSchemaRef(in getResponseSchemaRefInput) (*Schem
|
||||
if err = oai.tagMapToSchema(structField.TagMap(), bizResponseStructSchemaRef.Value); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties[fieldName] = bizResponseStructSchemaRef
|
||||
schema.Properties.Set(fieldName, *bizResponseStructSchemaRef)
|
||||
break
|
||||
}
|
||||
default:
|
||||
@ -95,7 +96,7 @@ func (oai *OpenApiV3) getResponseSchemaRef(in getResponseSchemaRefInput) (*Schem
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties[fieldName] = *schemaRef
|
||||
schema.Properties.Set(fieldName, *schemaRef)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,8 @@ package goai
|
||||
import (
|
||||
"reflect"
|
||||
|
||||
"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/utils"
|
||||
"github.com/gogf/gf/v2/os/gstructs"
|
||||
@ -18,8 +20,6 @@ import (
|
||||
"github.com/gogf/gf/v2/util/gvalid"
|
||||
)
|
||||
|
||||
type Schemas map[string]SchemaRef
|
||||
|
||||
// Schema is specified by OpenAPI/Swagger 3.0 standard.
|
||||
type Schema struct {
|
||||
OneOf SchemaRefs `json:"oneOf,omitempty" yaml:"oneOf,omitempty"`
|
||||
@ -78,8 +78,8 @@ func (oai *OpenApiV3) addSchema(object ...interface{}) error {
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) doAddSchemaSingle(object interface{}) error {
|
||||
if oai.Components.Schemas == nil {
|
||||
oai.Components.Schemas = map[string]SchemaRef{}
|
||||
if oai.Components.Schemas.refs == nil {
|
||||
oai.Components.Schemas.refs = gmap.NewListMap()
|
||||
}
|
||||
|
||||
var (
|
||||
@ -88,21 +88,21 @@ func (oai *OpenApiV3) doAddSchemaSingle(object interface{}) error {
|
||||
)
|
||||
|
||||
// Already added.
|
||||
if _, ok := oai.Components.Schemas[structTypeName]; ok {
|
||||
if oai.Components.Schemas.Get(structTypeName) != nil {
|
||||
return nil
|
||||
}
|
||||
// Take the holder first.
|
||||
oai.Components.Schemas[structTypeName] = SchemaRef{}
|
||||
oai.Components.Schemas.Set(structTypeName, SchemaRef{})
|
||||
|
||||
schema, err := oai.structToSchema(object)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oai.Components.Schemas[structTypeName] = SchemaRef{
|
||||
oai.Components.Schemas.Set(structTypeName, SchemaRef{
|
||||
Ref: "",
|
||||
Value: schema,
|
||||
}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -111,7 +111,7 @@ func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) {
|
||||
var (
|
||||
tagMap = gmeta.Data(object)
|
||||
schema = &Schema{
|
||||
Properties: map[string]SchemaRef{},
|
||||
Properties: createSchemas(),
|
||||
}
|
||||
)
|
||||
if len(tagMap) > 0 {
|
||||
@ -153,8 +153,18 @@ func (oai *OpenApiV3) structToSchema(object interface{}) (*Schema, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
schema.Properties[fieldName] = *schemaRef
|
||||
schema.Properties.Set(fieldName, *schemaRef)
|
||||
}
|
||||
|
||||
schema.Properties.Iterator(func(key string, ref SchemaRef) bool {
|
||||
if ref.Value != nil && ref.Value.Pattern != "" {
|
||||
validationRuleSet := gset.NewStrSetFrom(gstr.Split(ref.Value.Pattern, "|"))
|
||||
if validationRuleSet.Contains(patternKeyForRequired) {
|
||||
schema.Required = append(schema.Required, key)
|
||||
}
|
||||
}
|
||||
return true
|
||||
})
|
||||
return schema, nil
|
||||
}
|
||||
|
||||
@ -166,8 +176,16 @@ func (oai *OpenApiV3) tagMapToSchema(tagMap map[string]string, schema *Schema) e
|
||||
// Validation info to OpenAPI schema pattern.
|
||||
for _, tag := range gvalid.GetTags() {
|
||||
if validationTagValue, ok := tagMap[tag]; ok {
|
||||
_, validationRule, _ := gvalid.ParseTagValue(validationTagValue)
|
||||
schema.Pattern = validationRule
|
||||
_, validationRules, _ := gvalid.ParseTagValue(validationTagValue)
|
||||
schema.Pattern = validationRules
|
||||
// Enum checks.
|
||||
if len(schema.Enum) == 0 {
|
||||
for _, rule := range gstr.SplitAndTrim(validationRules, "|") {
|
||||
if gstr.HasPrefix(rule, patternKeyForIn) {
|
||||
schema.Enum = gconv.Interfaces(gstr.SplitAndTrim(rule[len(patternKeyForIn):], ","))
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ func (oai *OpenApiV3) newSchemaRefWithGolangType(golangType reflect.Type, tagMap
|
||||
var (
|
||||
structTypeName = oai.golangTypeToSchemaName(golangType)
|
||||
)
|
||||
if _, ok := oai.Components.Schemas[structTypeName]; !ok {
|
||||
if oai.Components.Schemas.Get(structTypeName) == nil {
|
||||
if err := oai.addSchema(reflect.New(golangType).Interface()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -81,7 +81,7 @@ func (oai *OpenApiV3) newSchemaRefWithGolangType(golangType reflect.Type, tagMap
|
||||
default:
|
||||
// Normal struct object.
|
||||
var structTypeName = oai.golangTypeToSchemaName(golangType)
|
||||
if _, ok := oai.Components.Schemas[structTypeName]; !ok {
|
||||
if oai.Components.Schemas.Get(structTypeName) == nil {
|
||||
if err := oai.addSchema(reflect.New(golangType).Elem().Interface()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
64
protocol/goai/goai_shemas.go
Normal file
64
protocol/goai/goai_shemas.go
Normal file
@ -0,0 +1,64 @@
|
||||
// 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/container/gmap"
|
||||
)
|
||||
|
||||
type Schemas struct {
|
||||
refs *gmap.ListMap
|
||||
}
|
||||
|
||||
func createSchemas() Schemas {
|
||||
return Schemas{
|
||||
refs: gmap.NewListMap(),
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Schemas) init() {
|
||||
if s.refs == nil {
|
||||
s.refs = gmap.NewListMap()
|
||||
}
|
||||
}
|
||||
|
||||
func (s Schemas) Get(name string) *SchemaRef {
|
||||
s.init()
|
||||
value := s.refs.Get(name)
|
||||
if value != nil {
|
||||
ref := value.(SchemaRef)
|
||||
return &ref
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s Schemas) Set(name string, ref SchemaRef) {
|
||||
s.init()
|
||||
s.refs.Set(name, ref)
|
||||
}
|
||||
|
||||
func (s Schemas) Map() map[string]SchemaRef {
|
||||
s.init()
|
||||
m := make(map[string]SchemaRef)
|
||||
s.refs.Iterator(func(key, value interface{}) bool {
|
||||
m[key.(string)] = value.(SchemaRef)
|
||||
return true
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
func (s Schemas) Iterator(f func(key string, ref SchemaRef) bool) {
|
||||
s.init()
|
||||
s.refs.Iterator(func(key, value interface{}) bool {
|
||||
return f(key.(string), value.(SchemaRef))
|
||||
})
|
||||
}
|
||||
|
||||
func (s Schemas) MarshalJSON() ([]byte, error) {
|
||||
s.init()
|
||||
return s.refs.MarshalJSON()
|
||||
}
|
@ -48,14 +48,14 @@ func Test_Basic(t *testing.T) {
|
||||
})
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
t.Assert(len(oai.Components.Schemas), 2)
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].Value.Type, goai.TypeObject)
|
||||
t.Assert(len(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].Value.Properties), 7)
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].Value.Properties[`appId`].Value.Type, goai.TypeNumber)
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].Value.Properties[`resourceId`].Value.Type, goai.TypeString)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 2)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Type, goai.TypeObject)
|
||||
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Map()), 7)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Get(`appId`).Value.Type, goai.TypeNumber)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Get(`resourceId`).Value.Type, goai.TypeString)
|
||||
|
||||
t.Assert(len(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`].Value.Properties), 3)
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`].Value.Properties[`Params`].Value.Type, goai.TypeArray)
|
||||
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`).Value.Properties.Map()), 3)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`).Value.Properties.Get(`Params`).Value.Type, goai.TypeArray)
|
||||
})
|
||||
}
|
||||
|
||||
@ -108,14 +108,14 @@ func TestOpenApiV3_Add(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
// fmt.Println(oai.String())
|
||||
// Schema asserts.
|
||||
t.Assert(len(oai.Components.Schemas), 3)
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].Value.Type, goai.TypeObject)
|
||||
t.Assert(len(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].Value.Properties), 7)
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].Value.Properties[`appId`].Value.Type, goai.TypeNumber)
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].Value.Properties[`resourceId`].Value.Type, goai.TypeString)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Type, goai.TypeObject)
|
||||
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Map()), 7)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Get(`appId`).Value.Type, goai.TypeNumber)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).Value.Properties.Get(`resourceId`).Value.Type, goai.TypeString)
|
||||
|
||||
t.Assert(len(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`].Value.Properties), 3)
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`].Value.Properties[`Params`].Value.Type, goai.TypeArray)
|
||||
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`).Value.Properties.Map()), 3)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`).Value.Properties.Get(`Params`).Value.Type, goai.TypeArray)
|
||||
|
||||
// Paths.
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
@ -158,9 +158,9 @@ func TestOpenApiV3_Add_Recursive(t *testing.T) {
|
||||
})
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
t.Assert(len(oai.Components.Schemas), 3)
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CategoryTreeItem`].Value.Type, goai.TypeObject)
|
||||
t.Assert(len(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CategoryTreeItem`].Value.Properties), 3)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CategoryTreeItem`).Value.Type, goai.TypeObject)
|
||||
t.Assert(len(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CategoryTreeItem`).Value.Properties.Map()), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -222,7 +222,7 @@ func TestOpenApiV3_Add_AutoDetectIn(t *testing.T) {
|
||||
|
||||
fmt.Println(oai.String())
|
||||
|
||||
t.Assert(len(oai.Components.Schemas), 2)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 2)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.AssertNE(oai.Paths[path].Get, nil)
|
||||
t.Assert(len(oai.Paths[path].Get.Parameters), 3)
|
||||
@ -274,9 +274,9 @@ func TestOpenApiV3_CommonRequest(t *testing.T) {
|
||||
})
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
t.Assert(len(oai.Components.Schemas), 3)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties), 3)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -319,9 +319,9 @@ func TestOpenApiV3_CommonRequest_WithoutDataField_Setting(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
// fmt.Println(oai.String())
|
||||
t.Assert(len(oai.Components.Schemas), 3)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties), 5)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 5)
|
||||
})
|
||||
}
|
||||
|
||||
@ -359,9 +359,9 @@ func TestOpenApiV3_CommonRequest_EmptyRequest(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
// fmt.Println(oai.String())
|
||||
t.Assert(len(oai.Components.Schemas), 3)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties), 3)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -414,10 +414,10 @@ func TestOpenApiV3_CommonRequest_SubDataField(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
// fmt.Println(oai.String())
|
||||
t.Assert(len(oai.Components.Schemas), 4)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 4)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties[`Request`].Value.Properties), 4)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Map()), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Value.Properties.Get(`Request`).Value.Properties.Map()), 4)
|
||||
})
|
||||
}
|
||||
|
||||
@ -460,13 +460,13 @@ func TestOpenApiV3_CommonResponse(t *testing.T) {
|
||||
})
|
||||
t.AssertNil(err)
|
||||
|
||||
//g.Dump(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties)
|
||||
//g.Dump(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map())
|
||||
// Schema asserts.
|
||||
t.Assert(len(oai.Components.Schemas), 3)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties), 3)
|
||||
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
|
||||
t.Assert(
|
||||
oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties["data"].Value.Description,
|
||||
oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Get("data").Value.Description,
|
||||
`Result data for certain request according API definition`,
|
||||
)
|
||||
})
|
||||
@ -511,9 +511,9 @@ func TestOpenApiV3_CommonResponse_WithoutDataField_Setting(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
fmt.Println(oai.String())
|
||||
t.Assert(len(oai.Components.Schemas), 3)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties), 8)
|
||||
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 8)
|
||||
})
|
||||
}
|
||||
|
||||
@ -551,10 +551,10 @@ func TestOpenApiV3_CommonResponse_EmptyResponse(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
// fmt.Println(oai.String())
|
||||
t.Assert(len(oai.Components.Schemas), 3)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(oai.Paths["/index"].Put.RequestBody.Value.Content["application/json"].Schema.Ref, `github.com.gogf.gf.v2.protocol.goai_test.Req`)
|
||||
t.Assert(len(oai.Paths["/index"].Put.Responses["200"].Value.Content["application/json"].Schema.Value.Properties), 3)
|
||||
t.Assert(len(oai.Paths["/index"].Put.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -607,10 +607,10 @@ func TestOpenApiV3_CommonResponse_SubDataField(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
// Schema asserts.
|
||||
// fmt.Println(oai.String())
|
||||
t.Assert(len(oai.Components.Schemas), 4)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 4)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties[`Response`].Value.Properties), 7)
|
||||
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Map()), 1)
|
||||
t.Assert(len(oai.Paths["/index"].Get.Responses["200"].Value.Content["application/json"].Schema.Value.Properties.Get(`Response`).Value.Properties.Map()), 7)
|
||||
})
|
||||
}
|
||||
|
||||
@ -663,12 +663,12 @@ func TestOpenApiV3_ShortTags(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
// fmt.Println(oai.String())
|
||||
// Schema asserts.
|
||||
t.Assert(len(oai.Components.Schemas), 3)
|
||||
t.Assert(len(oai.Components.Schemas.Map()), 3)
|
||||
t.Assert(oai.Paths[`/test1/{appId}`].Summary, `CreateResourceReq sum`)
|
||||
t.Assert(oai.
|
||||
Components.
|
||||
Schemas[`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`].
|
||||
Value.Properties[`resourceId`].Value.Description, `资源Id`)
|
||||
Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`).
|
||||
Value.Properties.Get(`resourceId`).Value.Description, `资源Id`)
|
||||
})
|
||||
}
|
||||
|
||||
@ -698,7 +698,7 @@ func TestOpenApiV3_HtmlResponse(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
|
||||
// fmt.Println(oai.String())
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.Res`].Value.Type, goai.TypeString)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.Res`).Value.Type, goai.TypeString)
|
||||
})
|
||||
}
|
||||
|
||||
@ -745,6 +745,57 @@ func TestOpenApiV3_HtmlResponseWithCommonResponse(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
|
||||
// fmt.Println(oai.String())
|
||||
t.Assert(oai.Components.Schemas[`github.com.gogf.gf.v2.protocol.goai_test.Res`].Value.Type, goai.TypeString)
|
||||
t.Assert(oai.Components.Schemas.Get(`github.com.gogf.gf.v2.protocol.goai_test.Res`).Value.Type, goai.TypeString)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Required_In_Schema(t *testing.T) {
|
||||
type CommonReq struct {
|
||||
AppId int64 `json:"appId" v:"required" in:"cookie" description:"应用Id"`
|
||||
ResourceId string `json:"resourceId" in:"query" description:"资源Id"`
|
||||
}
|
||||
type SetSpecInfo struct {
|
||||
StorageType string `v:"required|in:CLOUD_PREMIUM,CLOUD_SSD,CLOUD_HSSD" description:"StorageType"`
|
||||
Shards int32 `description:"shards 分片数"`
|
||||
Params []string `description:"默认参数(json 串-ClickHouseParams)"`
|
||||
}
|
||||
type CreateResourceReq struct {
|
||||
CommonReq
|
||||
gmeta.Meta `path:"/CreateResourceReq" method:"POST" tags:"default"`
|
||||
Name string `description:"实例名称"`
|
||||
Product string `description:"业务类型"`
|
||||
Region string `v:"required|min:1" description:"区域"`
|
||||
SetMap map[string]*SetSpecInfo `v:"required|min:1" description:"配置Map"`
|
||||
SetSlice []SetSpecInfo `v:"required|min:1" description:"配置Slice"`
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
err error
|
||||
oai = goai.New()
|
||||
req = new(CreateResourceReq)
|
||||
)
|
||||
err = oai.Add(goai.AddInput{
|
||||
Object: req,
|
||||
})
|
||||
t.AssertNil(err)
|
||||
var (
|
||||
schemaKey1 = `github.com.gogf.gf.v2.protocol.goai_test.CreateResourceReq`
|
||||
schemaKey2 = `github.com.gogf.gf.v2.protocol.goai_test.SetSpecInfo`
|
||||
)
|
||||
t.Assert(oai.Components.Schemas.Map()[schemaKey1].Value.Required, g.Slice{
|
||||
"appId",
|
||||
"Region",
|
||||
"SetMap",
|
||||
"SetSlice",
|
||||
})
|
||||
t.Assert(oai.Components.Schemas.Map()[schemaKey2].Value.Required, g.Slice{
|
||||
"StorageType",
|
||||
})
|
||||
t.Assert(oai.Components.Schemas.Map()[schemaKey2].Value.Properties.Map()["StorageType"].Value.Enum, g.Slice{
|
||||
"CLOUD_PREMIUM",
|
||||
"CLOUD_SSD",
|
||||
"CLOUD_HSSD",
|
||||
})
|
||||
})
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user