add automatically detectting feature for 'in' attribute of parameters for package goai;add 'datetime' rule for package gvalid

This commit is contained in:
John Guo 2021-10-27 15:33:29 +08:00
parent a19ba3d530
commit 2cf84e020f
9 changed files with 101 additions and 13 deletions

View File

@ -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
}
}
}

View File

@ -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
}

View File

@ -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"`

View File

@ -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",

View File

@ -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

View File

@ -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{

View File

@ -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"

View File

@ -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 手机号码格式不正确"

View File

@ -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"