mirror of
https://gitee.com/johng/gf.git
synced 2024-11-29 18:57:44 +08:00
add automatically detectting feature for 'in' attribute of parameters for package goai;add 'datetime' rule for package gvalid
This commit is contained in:
parent
a19ba3d530
commit
2cf84e020f
@ -7,6 +7,7 @@
|
||||
package goai
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/v2/errors/gcode"
|
||||
"github.com/gogf/gf/v2/errors/gerror"
|
||||
"github.com/gogf/gf/v2/internal/json"
|
||||
@ -41,7 +42,7 @@ type ParameterRef struct {
|
||||
Value *Parameter
|
||||
}
|
||||
|
||||
func (oai *OpenApiV3) newParameterRefWithStructMethod(field *structs.Field, method string) (*ParameterRef, error) {
|
||||
func (oai *OpenApiV3) newParameterRefWithStructMethod(field *structs.Field, path, method string) (*ParameterRef, error) {
|
||||
var (
|
||||
tagMap = field.TagMap()
|
||||
parameter = &Parameter{
|
||||
@ -58,13 +59,18 @@ func (oai *OpenApiV3) newParameterRefWithStructMethod(field *structs.Field, meth
|
||||
}
|
||||
}
|
||||
if parameter.In == "" {
|
||||
// Default the parameter input to "query" if method is "GET/DELETE".
|
||||
switch gstr.ToUpper(method) {
|
||||
case HttpMethodGet, HttpMethodDelete:
|
||||
parameter.In = ParameterInQuery
|
||||
// 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
|
||||
default:
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -180,7 +180,7 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
Value: &requestBody,
|
||||
}
|
||||
}
|
||||
// Request parameters.
|
||||
// It also sets request parameters.
|
||||
structFields, _ := structs.Fields(structs.FieldsInput{
|
||||
Pointer: inputObject.Interface(),
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
@ -189,7 +189,7 @@ func (oai *OpenApiV3) addPath(in addPathInput) error {
|
||||
if operation.Parameters == nil {
|
||||
operation.Parameters = []ParameterRef{}
|
||||
}
|
||||
parameterRef, err := oai.newParameterRefWithStructMethod(structField, in.Method)
|
||||
parameterRef, err := oai.newParameterRefWithStructMethod(structField, in.Path, in.Method)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -190,6 +190,50 @@ func TestOpenApiV3_Add_EmptyReqAndRes(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestOpenApiV3_Add_AutoDetectIn(t *testing.T) {
|
||||
type Req struct {
|
||||
gmeta.Meta `method:"get" tags:"default"`
|
||||
Name string
|
||||
Product string
|
||||
Region string
|
||||
}
|
||||
|
||||
type Res struct {
|
||||
gmeta.Meta `description:"Demo Response Struct"`
|
||||
}
|
||||
|
||||
f := func(ctx context.Context, req *Req) (res *Res, err error) {
|
||||
return
|
||||
}
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var (
|
||||
err error
|
||||
oai = goai.New()
|
||||
path = `/test/{product}/{name}`
|
||||
)
|
||||
err = oai.Add(goai.AddInput{
|
||||
Path: path,
|
||||
Method: goai.HttpMethodGet,
|
||||
Object: f,
|
||||
})
|
||||
t.AssertNil(err)
|
||||
|
||||
fmt.Println(oai.String())
|
||||
|
||||
t.Assert(len(oai.Components.Schemas), 2)
|
||||
t.Assert(len(oai.Paths), 1)
|
||||
t.AssertNE(oai.Paths[path].Get, nil)
|
||||
t.Assert(len(oai.Paths[path].Get.Parameters), 3)
|
||||
t.Assert(oai.Paths[path].Get.Parameters[0].Value.Name, `Name`)
|
||||
t.Assert(oai.Paths[path].Get.Parameters[0].Value.In, goai.ParameterInPath)
|
||||
t.Assert(oai.Paths[path].Get.Parameters[1].Value.Name, `Product`)
|
||||
t.Assert(oai.Paths[path].Get.Parameters[1].Value.In, goai.ParameterInPath)
|
||||
t.Assert(oai.Paths[path].Get.Parameters[2].Value.Name, `Region`)
|
||||
t.Assert(oai.Paths[path].Get.Parameters[2].Value.In, goai.ParameterInQuery)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOpenApiV3_CommonRequest(t *testing.T) {
|
||||
type CommonRequest struct {
|
||||
Code int `json:"code" description:"Error code"`
|
||||
|
@ -29,6 +29,7 @@ import (
|
||||
// required-without-all format: required-without-all:field1,field2,...brief: Required if all given fields are empty.
|
||||
// bail format: bail brief: Stop validating when this field's validation failed.
|
||||
// date format: date brief: Standard date, like: 2006-01-02, 20060102, 2006.01.02
|
||||
// datetime format: datetime brief: Standard datetime, like: 2006-01-02 12:00:00
|
||||
// date-format format: date-format:format brief: Custom date format.
|
||||
// email format: email brief: Email address.
|
||||
// phone format: phone brief: Phone number.
|
||||
@ -133,6 +134,7 @@ var (
|
||||
"required-without-all": {},
|
||||
"bail": {},
|
||||
"date": {},
|
||||
"datetime": {},
|
||||
"date-format": {},
|
||||
"email": {},
|
||||
"phone": {},
|
||||
@ -192,6 +194,7 @@ var (
|
||||
"required-without": "The :attribute field is required",
|
||||
"required-without-all": "The :attribute field is required",
|
||||
"date": "The :attribute value is not a valid date",
|
||||
"datetime": "The :attribute value is not a valid datetime",
|
||||
"date-format": "The :attribute value does not match the format :format",
|
||||
"email": "The :attribute value must be a valid email address",
|
||||
"phone": "The :attribute value must be a valid phone number",
|
||||
|
@ -13,7 +13,7 @@ import "context"
|
||||
// The parameter `rule` specifies the validation rule string, like "required", "between:1,100", etc.
|
||||
// The parameter `value` specifies the value for this rule to validate.
|
||||
// The parameter `message` specifies the custom error message or configured i18n message for this rule.
|
||||
// The parameter `data` specifies the `data` which is passed to the Validator. It might be type of map/struct or a nil value.
|
||||
// The parameter `data` specifies the `data` which is passed to the Validator. It might be a type of map/struct or a nil value.
|
||||
// You can ignore the parameter `data` if you do not really need it in your custom validation rule.
|
||||
type RuleFunc func(ctx context.Context, rule string, value interface{}, message string, data interface{}) error
|
||||
|
||||
|
@ -258,16 +258,28 @@ func (v *Validator) doCheckBuildInRules(ctx context.Context, input doCheckBuildI
|
||||
}
|
||||
match = gregex.IsMatchString(`\d{4}[\.\-\_/]{0,1}\d{2}[\.\-\_/]{0,1}\d{2}`, valueStr)
|
||||
|
||||
// Datetime rule.
|
||||
case "datetime":
|
||||
// support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time.
|
||||
if v, ok := input.Value.(iTime); ok {
|
||||
return !v.IsZero(), nil
|
||||
}
|
||||
if _, err = gtime.StrToTimeFormat(valueStr, `Y-m-d H:i:s`); err == nil {
|
||||
match = true
|
||||
}
|
||||
|
||||
// Date rule with specified format.
|
||||
case "date-format":
|
||||
// support for time value, eg: gtime.Time/*gtime.Time, time.Time/*time.Time.
|
||||
if v, ok := input.Value.(iTime); ok {
|
||||
return !v.IsZero(), nil
|
||||
}
|
||||
if _, err := gtime.StrToTimeFormat(valueStr, input.RulePattern); err == nil {
|
||||
if _, err = gtime.StrToTimeFormat(valueStr, input.RulePattern); err == nil {
|
||||
match = true
|
||||
} else {
|
||||
var msg string
|
||||
var (
|
||||
msg string
|
||||
)
|
||||
msg = v.getErrorMessageByRule(ctx, input.RuleKey, input.CustomMsgMap)
|
||||
msg = strings.Replace(msg, ":format", input.RulePattern, -1)
|
||||
return match, gerror.NewOption(gerror.Option{
|
||||
|
@ -262,6 +262,27 @@ func Test_Date(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Datetime(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := g.MapStrBool{
|
||||
"2010": false,
|
||||
"2010.11": false,
|
||||
"2010-11-01": false,
|
||||
"2010-11-01 12:00": false,
|
||||
"2010-11-01 12:00:00": true,
|
||||
"2010.11.01 12:00:00": false,
|
||||
}
|
||||
for k, v := range m {
|
||||
err := g.Validator().Rules(`datetime`).CheckValue(ctx, k)
|
||||
if v {
|
||||
t.AssertNil(err)
|
||||
} else {
|
||||
t.AssertNE(err, nil)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DateFormat(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
val1 := "2010"
|
||||
|
@ -5,7 +5,8 @@
|
||||
"gf.gvalid.rule.required-with-all" = ":attribute 字段不能为空"
|
||||
"gf.gvalid.rule.required-without" = ":attribute 字段不能为空"
|
||||
"gf.gvalid.rule.required-without-all" = ":attribute 字段不能为空"
|
||||
"gf.gvalid.rule.date" = ":attribute 日期格式不正确"
|
||||
"gf.gvalid.rule.date" = ":attribute 日期格式不满足Y-m-d格式,例如: 2001-02-03"
|
||||
"gf.gvalid.rule.datetime" = ":attribute 日期格式不满足Y-m-d H:i:s格式,例如: 2001-02-03 12:00:00"
|
||||
"gf.gvalid.rule.date-format" = ":attribute 日期格式不满足:format"
|
||||
"gf.gvalid.rule.email" = ":attribute 邮箱地址格式不正确"
|
||||
"gf.gvalid.rule.phone" = ":attribute 手机号码格式不正确"
|
||||
|
@ -6,6 +6,7 @@
|
||||
"gf.gvalid.rule.required-without" = "The :attribute field is required"
|
||||
"gf.gvalid.rule.required-without-all" = "The :attribute field is required"
|
||||
"gf.gvalid.rule.date" = "The :attribute value is not a valid date"
|
||||
"gf.gvalid.rule.datetime" = "The :attribute value is not a valid datetime"
|
||||
"gf.gvalid.rule.date-format" = "The :attribute value does not match the format :format"
|
||||
"gf.gvalid.rule.email" = "The :attribute value must be a valid email address"
|
||||
"gf.gvalid.rule.phone" = "The :attribute value must be a valid phone number"
|
||||
|
Loading…
Reference in New Issue
Block a user