Skip to content

Commit

Permalink
Merge pull request #10 from iFaceless/support-default-value
Browse files Browse the repository at this point in the history
✨ feat: support default value
  • Loading branch information
iFaceless committed Jan 15, 2020
2 parents 7d2a9b8 + 51a30b9 commit 09d993e
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 14 deletions.
14 changes: 14 additions & 0 deletions USERGUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ type UserSchema struct {
```

### Set Default Value for Field: `default`

Only works for types: pointer/slice/map. For basic types (integer, string, bool), default value will be converted and set to field directly. For complex types (eg. map/slice/pointer to custom struct), set default to `AUTO_INIT`, portal will initialize field to its zero value.

```go
type ContentSchema struct {
BizID *string `json:"biz_id" portal:"default:100"`
SkuID *string `json:"sku_id"` // -> json null
Users []*UserSchema `json:"users" portal:"default:AUTO_INIT"` // -> json []
Members map[string]int `json:"members" portal:"default:AUTO_INIT"` // -> json {}
User *UserSchema `json:"user" portal:"default:AUTO_INIT"`
}
```

## Embedding Schema
```go
type PersonSchema struct {
Expand Down
20 changes: 11 additions & 9 deletions _examples/todo/schema/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ type UserSchema struct {
}

type TaskSchema struct {
ID string `json:"id,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty" portal:"meth:GetDescription;async"`
Description1 string `json:"description1,omitempty" portal:"meth:GetDescription;async"`
Description2 string `json:"description2,omitempty" portal:"meth:GetDescription;async"`
Description3 string `json:"description3,omitempty" portal:"meth:GetDescription;async"`
User *UserSchema `json:"user,omitempty" portal:"nested"`
SimpleUser *UserSchema `json:"simple_user,omitempty" portal:"nested;only:Name;attr:User"`
Unknown string `json:"unknown"`
ID string `json:"id,omitempty"`
Title string `json:"title,omitempty"`
Description string `json:"description,omitempty" portal:"meth:GetDescription;async"`
Description1 string `json:"description1,omitempty" portal:"meth:GetDescription;async"`
Description2 string `json:"description2,omitempty" portal:"meth:GetDescription;async"`
Description3 string `json:"description3,omitempty" portal:"meth:GetDescription;async"`
User *UserSchema `json:"user,omitempty" portal:"nested"`
SimpleUser *UserSchema `json:"simple_user,omitempty" portal:"nested;only:Name;attr:User"`
Unknown string `json:"unknown"`
UsersWithDefault []*UserSchema `json:"users_with_default" portal:"nested;default:AUTO_INIT"`
UserWithDefault *UserSchema `json:"user_with_default" portal:"nested;default:AUTO_INIT"`
}

func (ts *TaskSchema) GetDescription(model *model.TaskModel) (string, error) {
Expand Down
22 changes: 17 additions & 5 deletions chell.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ func (c *Chell) DumpWithContext(ctx context.Context, dst, src interface{}) error
return c.dumpMany(
ctx, dst, src,
extractFilterNodeNames(c.onlyFieldFilters[0], nil),
extractFilterNodeNames(c.excludeFieldFilters[0], &extractOption{ignoreNodeWithChildren: true}))
extractFilterNodeNames(c.excludeFieldFilters[0], &extractOption{ignoreNodeWithChildren: true}),
"",
)
} else {
toSchema := newSchema(dst).withFieldAliasMapTagName(c.fieldAliasMapTagName)
toSchema.setOnlyFields(extractFilterNodeNames(c.onlyFieldFilters[0], nil)...)
Expand Down Expand Up @@ -210,8 +212,13 @@ func (c *Chell) dumpAsyncFields(ctx context.Context, dst *schema, src interface{

func (c *Chell) dumpField(ctx context.Context, field *schemaField, value interface{}) error {
if isNil(value) {
logger.Warnf("[portal.chell] cannot get value for field %s, current input value is %v", field, value)
return nil
if field.hasDefaultValue() {
value = field.defaultValue()
logger.Infof("[portal.chell] use default value for field `%s`", field)
} else {
logger.Warnf("[portal.chell] cannot get value for field `%s`, current input value is %v", field, value)
return nil
}
}

if !field.isNested() {
Expand Down Expand Up @@ -259,6 +266,7 @@ func (c *Chell) dumpFieldNestedMany(ctx context.Context, field *schemaField, src
src,
field.nestedOnlyNames(c.onlyFieldFilters[depth]),
field.nestedExcludeNames(c.excludeFieldFilters[depth]),
field.String(),
)
if err != nil {
return err
Expand All @@ -279,14 +287,18 @@ func (c *Chell) dumpFieldNestedMany(ctx context.Context, field *schemaField, src
return nil
}

func (c *Chell) dumpMany(ctx context.Context, dst, src interface{}, onlyFields, excludeFields []string) error {
func (c *Chell) dumpMany(ctx context.Context, dst, src interface{}, onlyFields, excludeFields []string, field string) error {
rv := reflect.ValueOf(src)
if rv.Kind() == reflect.Ptr {
rv = reflect.Indirect(rv)
}

if rv.Kind() != reflect.Slice {
panic("input src must be a slice")
if field != "" {
panic(fmt.Sprintf("input src must be a slice, current processing field is `%s`", field))
} else {
panic(fmt.Sprintf("input src must be a slice"))
}
}

schemaSlice := reflect.Indirect(reflect.ValueOf(dst))
Expand Down
30 changes: 30 additions & 0 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,36 @@ func (f *schemaField) hasConstValue() bool {
return f.tagHasOption("CONST")
}

func (f *schemaField) defaultValue() interface{} {
val, ok := f.settings["DEFAULT"]
if !ok {
return nil
}

var defaultValue interface{}
if val == "AUTO_INIT" {
// just initialize this field, now support ptr/slice/map
typ := reflect.TypeOf(f.Value())
switch typ.Kind() {
case reflect.Ptr:
defaultValue = reflect.New(typ.Elem()).Interface()
case reflect.Slice:
defaultValue = reflect.MakeSlice(typ, 0, 0).Interface()
case reflect.Map:
defaultValue = reflect.MakeMap(typ).Interface()
default:
defaultValue = reflect.New(typ).Elem().Interface()
}
return defaultValue
}

return val
}

func (f *schemaField) hasDefaultValue() bool {
return f.tagHasOption("DEFAULT")
}

func (f *schemaField) tagHasOption(opt string) bool {
if _, ok := f.settings[opt]; ok {
return true
Expand Down
30 changes: 30 additions & 0 deletions field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,36 @@ func TestField_ConstValue(t *testing.T) {
assert.False(t, f.hasConstValue())
}

func TestField_DefaultValue(t *testing.T) {
type UserSchema struct {
ID string
}

type FooSchema struct {
ID *string `json:"id" portal:"default:100"`
Settings map[string]string `json:"settings" portal:"default:AUTO_INIT"`
User *UserSchema `json:"user" portal:"default:AUTO_INIT"`
Users []*UserSchema `json:"users" portal:"default:AUTO_INIT"`
}

schema := newSchema(&FooSchema{})
f := newField(schema, schema.innerStruct().Field("ID"))
assert.Equal(t, "100", f.defaultValue())
assert.True(t, f.hasDefaultValue())

f = newField(schema, schema.innerStruct().Field("Settings"))
assert.Equal(t, map[string]string{}, f.defaultValue())
assert.True(t, f.hasDefaultValue())

f = newField(schema, schema.innerStruct().Field("User"))
assert.Equal(t, &UserSchema{}, f.defaultValue())
assert.True(t, f.hasDefaultValue())

f = newField(schema, schema.innerStruct().Field("Users"))
assert.Equal(t, make([]*UserSchema, 0), f.defaultValue())
assert.True(t, f.hasDefaultValue())
}

func TestField_Async(t *testing.T) {
type FooSchema struct {
ID int
Expand Down

0 comments on commit 09d993e

Please sign in to comment.