More improvements on expr vars & rec values
This commit is contained in:
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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`},
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user