More improvements on expr vars & rec values

This commit is contained in:
Denis Arh
2021-03-30 15:53:13 +02:00
parent 597c18fd16
commit 333b2f344f
11 changed files with 75 additions and 92 deletions
+11 -52
View File
@@ -111,7 +111,7 @@ var _ gval.Selector = &ComposeRecord{}
// It allows gval lib to access Record's underlying value (*types.Record)
// and it's fields
//
func (t ComposeRecord) SelectGVal(ctx context.Context, k string) (interface{}, error) {
func (t ComposeRecord) SelectGVal(_ context.Context, k string) (interface{}, error) {
if k == "values" {
if t.value.Values == nil {
t.value.Values = types.RecordValueSet{}
@@ -242,12 +242,12 @@ func composeRecordValuesGValSelector(res *types.Record, k string) (interface{},
return nil, nil
case len(vv) == 1 && !multiValueField:
return recordValueCast(field, vv[0])
return vv[0].Cast(field)
default:
out := make([]interface{}, 0, len(vv))
return out, vv.Walk(func(v *types.RecordValue) error {
i, err := recordValueCast(field, v)
i, err := v.Cast(field)
if err != nil {
return err
}
@@ -429,61 +429,20 @@ func CastToComposeRecordValueErrorSet(val interface{}) (out *types.RecordValueEr
}
}
func recordValueCast(field *types.ModuleField, rv *types.RecordValue) (interface{}, error) {
if field == nil {
// safe fallback to string
return rv.Value, nil
}
switch {
case field.IsRef():
return rv.Ref, nil
case field.IsDateTime():
return cast.ToTimeE(rv.Value)
case field.IsBoolean():
return cast.ToBoolE(rv.Value)
case field.IsNumeric():
if field.Options.Precision() == 0 {
return cast.ToInt64E(rv.Value)
}
return cast.ToFloat64E(rv.Value)
default:
return rv.Value, nil
}
}
func recordValueToExprTypedValue(field *types.ModuleField, rv *types.RecordValue) (expr.TypedValue, error) {
if field == nil {
// safe fallback to string
return expr.NewString(rv.Value)
}
switch {
case field == nil:
return expr.NewString(rv.Value)
case field.IsRef():
return expr.NewID(rv.Ref)
case field.IsDateTime():
return expr.NewDateTime(rv.Value)
case field.IsBoolean():
return expr.NewBoolean(rv.Value)
case field.IsNumeric():
if field.Options.Precision() == 0 {
return expr.NewInteger(rv.Value)
}
return expr.NewFloat(rv.Value)
default:
return expr.NewString(rv.Value)
if v, err := rv.Cast(field); err != nil {
return nil, err
} else {
return expr.Typify(v)
}
}
}
+13 -7
View File
@@ -52,10 +52,10 @@ func TestRecordFieldValuesAccess(t *testing.T) {
v expr.TypedValue
mod = &types.Module{Fields: types.ModuleFieldSet{
&types.ModuleField{Name: "s1", Multi: false},
&types.ModuleField{Name: "m1", Multi: true},
&types.ModuleField{Name: "m2", Multi: true},
&types.ModuleField{Name: "s2", Multi: false},
&types.ModuleField{Name: "s1", Multi: false, Kind: "String"},
&types.ModuleField{Name: "m1", Multi: true, Kind: "String"},
&types.ModuleField{Name: "m2", Multi: true, Kind: "String"},
&types.ModuleField{Name: "s2", Multi: false, Kind: "String"},
&types.ModuleField{Name: "b0", Multi: false, Kind: "Bool"},
&types.ModuleField{Name: "b1", Multi: false, Kind: "Bool"},
}}
@@ -108,18 +108,24 @@ func TestRecordFieldValuesAccess(t *testing.T) {
test bool
expr string
}{
// interaction with set values
{true, `rec.values.s1 == "sVal1"`},
{false, `rec.values.s1 == "sVal2"`},
{true, `rec.values.s1`},
{false, `rec.values.s2`},
{true, `rec.values.s1 != "foo"`},
{true, `rec.values.s2 != "foo"`},
{true, `!rec.values.s2`},
// interaction with unset (= nil) values
{true, `rec.values.s2 != "foo"`},
{false, `rec.values.s2 == "foo"`},
{true, `!rec.values.s2`},
{false, `rec.values.s2`},
// multival
{true, `rec.values.m1[0] == "mVal1.0"`},
{true, `rec.values.m1[1] == "mVal1.1"`},
{true, `rec.values.m2[0] == "mVal2.0"`},
// booleans
{true, `!rec.values.b0`},
{false, `rec.values.b0`},
{true, `rec.values.b1`},
+34 -22
View File
@@ -5,10 +5,8 @@ import (
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/pkg/filter"
"github.com/cortezaproject/corteza-server/pkg/payload"
"github.com/pkg/errors"
"strconv"
"strings"
"github.com/spf13/cast"
"time"
)
@@ -53,6 +51,34 @@ func (v RecordValue) Clone() *RecordValue {
}
}
func (v RecordValue) Cast(f *ModuleField) (interface{}, error) {
if f == nil {
// safe fallback to string
return v.Value, nil
}
switch {
case f.IsRef():
return v.Ref, nil
case f.IsDateTime():
return cast.ToTimeE(v.Value)
case f.IsBoolean():
return cast.ToBoolE(v.Value)
case f.IsNumeric():
if f.Options.Precision() == 0 {
return cast.ToInt64E(v.Value)
}
return cast.ToFloat64E(v.Value)
default:
return v.Value, nil
}
}
func (set RecordValueSet) Clone() (vv RecordValueSet) {
vv = make(RecordValueSet, len(vv))
for i := range set {
@@ -337,23 +363,6 @@ func (set RecordValueSet) String() (o string) {
func (set RecordValueSet) Dict(fields ModuleFieldSet) map[string]interface{} {
var (
rval = make(map[string]interface{})
format = func(f *ModuleField, v string) interface{} {
switch strings.ToLower(f.Kind) {
case "bool":
return payload.ParseBool(v)
case "number":
if f.Options.Precision() > 0 {
num, _ := strconv.ParseFloat(v, 64)
return num
}
num, _ := strconv.ParseInt(v, 10, 64)
return num
}
return v
}
)
if len(fields) == 0 {
@@ -361,17 +370,20 @@ func (set RecordValueSet) Dict(fields ModuleFieldSet) map[string]interface{} {
}
_ = fields.Walk(func(f *ModuleField) error {
// make sure all fields are set at least to nil
rval[f.Name] = nil
if f.Multi {
var (
rv = set.FilterByName(f.Name)
vv = make([]interface{}, len(rv))
)
for i, val := range rv {
vv[i] = format(f, val.Value)
vv[i], _ = val.Cast(f)
}
rval[f.Name] = vv
} else if v := set.Get(f.Name, 0); v != nil {
rval[f.Name] = format(f, v.Value)
rval[f.Name], _ = v.Cast(f)
}
return nil