3
0

Rework/simplify expr.Vars handling

This commit is contained in:
Denis Arh
2021-04-01 11:11:20 +02:00
parent fc331cc388
commit fdf916b7f9
30 changed files with 436 additions and 324 deletions
+11 -21
View File
@@ -24,14 +24,12 @@ func (i *sequenceIterator) more() bool {
func (i *sequenceIterator) Start(context.Context, *Vars) error { return nil }
func (i *sequenceIterator) Next(context.Context, *Vars) (*Vars, error) {
scope := RVars{
"counter": Must(NewInteger(i.counter)),
"isFirst": Must(NewBoolean(i.counter == i.cFirst)),
"isLast": Must(NewBoolean(!i.more())),
}.Vars()
out := &Vars{}
out.Set("counter", i.counter)
out.Set("isFirst", i.counter == i.cFirst)
out.Set("isLast", !i.more())
i.counter = i.counter + i.cStep
return scope, nil
return out, nil
}
type (
@@ -66,19 +64,10 @@ func (i *collectionIterator) More(context.Context, *Vars) (bool, error) {
func (i *collectionIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
func (i *collectionIterator) Next(context.Context, *Vars) (out *Vars, err error) {
var item TypedValue
switch c := i.set[i.ptr].(type) {
case TypedValue:
item = c
default:
if item, err = Typify(c); err != nil {
return
}
}
out = &Vars{}
out.Set("line", i.set[i.ptr])
i.ptr++
return RVars{"item": item}.Vars(), nil
return out, nil
}
type (
@@ -101,6 +90,7 @@ func (i *lineIterator) Next(context.Context, *Vars) (*Vars, error) {
return nil, err
}
return RVars{"line": Must(NewString(i.s.Text()))}.Vars(), nil
out := &Vars{}
out.Set("line", i.s.Text())
return out, nil
}
+1 -1
View File
@@ -111,7 +111,7 @@ func (set ExprSet) Eval(ctx context.Context, in *expr.Vars) (*expr.Vars, error)
scope = (&expr.Vars{}).Merge(in)
// Prepare output scope
out = &expr.Vars{}
out, _ = expr.NewVars(nil)
// Untyped evaluation result
value interface{}
+14 -14
View File
@@ -14,25 +14,25 @@ func TestExprSet_Eval(t *testing.T) {
cc = []struct {
name string
set ExprSet
input RVars
output RVars
input map[string]interface{}
output map[string]interface{}
err string
}{
{
name: "empty",
set: ExprSet{},
output: nil,
output: make(map[string]interface{}),
},
{
name: "constant assignment",
set: ExprSet{&Expr{Target: "foo", Expr: `"bar"`}},
output: RVars{"foo": Must(NewString("bar"))},
output: map[string]interface{}{"foo": Must(NewString("bar"))},
},
{
name: "vars with path",
set: ExprSet{&Expr{Target: "l1.l2", Expr: `"bar"`}},
input: RVars{"l1": RVars{}.Vars()},
output: RVars{"l1": RVars{"l2": Must(Typify("bar"))}.Vars()},
input: map[string]interface{}{"l1": map[string]interface{}{}},
output: map[string]interface{}{"l1": map[string]interface{}{"l2": Must(Typify("bar"))}},
},
{
name: "copy vars with same types",
@@ -40,7 +40,7 @@ func TestExprSet_Eval(t *testing.T) {
&Expr{Target: "aa", Value: "vv", typ: &String{}},
&Expr{Target: "bb", Source: "aa", typ: &String{}},
},
output: RVars{
output: map[string]interface{}{
"aa": Must(NewString("vv")),
"bb": Must(NewString("vv")),
},
@@ -51,7 +51,7 @@ func TestExprSet_Eval(t *testing.T) {
&Expr{Target: "aa", Value: "should be always String", typ: &String{}},
&Expr{Target: "bb", Source: "aa"},
},
output: RVars{
output: map[string]interface{}{
"aa": Must(NewString("should be always String")),
"bb": Must(NewString("should be always String")),
},
@@ -62,7 +62,7 @@ func TestExprSet_Eval(t *testing.T) {
&Expr{Target: "aa", Value: "42", typ: &String{}},
&Expr{Target: "bb", Source: "aa", typ: &Integer{}},
},
output: RVars{
output: map[string]interface{}{
"aa": Must(NewString("42")),
"bb": Must(NewInteger(42)),
},
@@ -81,7 +81,7 @@ func TestExprSet_Eval(t *testing.T) {
&Expr{Target: "a", typ: &KV{}},
&Expr{Target: "a.b", Value: "c", typ: &String{}},
},
output: RVars{
output: map[string]interface{}{
"a": Must(NewKV(map[string]string{
"b": "c",
})),
@@ -93,7 +93,7 @@ func TestExprSet_Eval(t *testing.T) {
&Expr{Target: "arr", typ: &Array{}, Expr: `[]`},
&Expr{Target: "arr", typ: &Array{}, Expr: `push(arr, "foo")`},
},
output: RVars{
output: map[string]interface{}{
"arr": Must(NewArray([]string{
"foo",
})),
@@ -105,7 +105,7 @@ func TestExprSet_Eval(t *testing.T) {
&Expr{Target: "arr", typ: &Array{}, Expr: `[]`},
&Expr{Target: "arr", typ: &Any{}, Expr: `push(arr, "foo")`},
},
output: RVars{
output: map[string]interface{}{
"arr": Must(NewArray([]string{
"foo",
})),
@@ -116,7 +116,7 @@ func TestExprSet_Eval(t *testing.T) {
set: ExprSet{
&Expr{Target: "arr", typ: &Array{}, Expr: `push([], "foo")`},
},
output: RVars{
output: map[string]interface{}{
"arr": Must(NewArray([]TypedValue{
Must(Typify("foo")),
})),
@@ -153,7 +153,7 @@ func TestExprSet_Eval(t *testing.T) {
return
}
req.Equal(c.output.Vars(), output)
req.Equal(Must(Typify(c.output)), output)
})
}
}
+5 -3
View File
@@ -77,11 +77,11 @@ func TestRecordFieldValuesAccess(t *testing.T) {
&types.RecordValue{Name: "ref2", Value: "", Ref: 2, Place: 0},
}}
tval = &ComposeRecord{value: raw}
scope = expr.RVars{
tval = &ComposeRecord{value: raw}
scope, _ = expr.NewVars(map[string]interface{}{
"rec": tval,
"nil": nil,
}.Vars()
})
)
// @todo see not above re. back-ref to record
@@ -121,6 +121,8 @@ func TestRecordFieldValuesAccess(t *testing.T) {
expr string
}{
{false, `nil`},
{true, `rec`},
{true, `rec.values`},
// interaction with set values
{true, `rec.values.s1 == "sVal1"`},
+5 -6
View File
@@ -318,12 +318,11 @@ func (i *recordSetIterator) More(context.Context, *Vars) (bool, error) {
func (i *recordSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
func (i *recordSetIterator) Next(context.Context, *Vars) (*Vars, error) {
out := RVars{
"record": Must(NewComposeRecord(i.set[i.ptr])),
"index": Must(NewUnsignedInteger(i.ptr)),
"total": Must(NewUnsignedInteger(i.filter.Total)),
}
out := &Vars{}
out.Set("record", Must(NewComposeRecord(i.set[i.ptr])))
out.Set("index", i.ptr)
out.Set("total", i.filter.Total)
i.ptr++
return out.Vars(), nil
return out, nil
}
+91 -47
View File
@@ -425,14 +425,14 @@ func (res composeBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res composeBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res composeBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -849,26 +849,38 @@ func (res moduleBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res moduleBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res moduleBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
if rvars["module"], err = automation.NewComposeModule(res.module); err != nil {
return nil, err
if v, err = automation.NewComposeModule(res.module); err == nil {
err = out.Set("module", v)
}
if rvars["oldModule"], err = automation.NewComposeModule(res.oldModule); err != nil {
return nil, err
if err != nil {
return
}
if rvars["namespace"], err = automation.NewComposeNamespace(res.namespace); err != nil {
return nil, err
if v, err = automation.NewComposeModule(res.oldModule); err == nil {
err = out.Set("oldModule", v)
}
if err != nil {
return
}
if v, err = automation.NewComposeNamespace(res.namespace); err == nil {
err = out.Set("namespace", v)
}
if err != nil {
return
}
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -1277,22 +1289,30 @@ func (res namespaceBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res namespaceBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res namespaceBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
if rvars["namespace"], err = automation.NewComposeNamespace(res.namespace); err != nil {
return nil, err
if v, err = automation.NewComposeNamespace(res.namespace); err == nil {
err = out.Set("namespace", v)
}
if rvars["oldNamespace"], err = automation.NewComposeNamespace(res.oldNamespace); err != nil {
return nil, err
if err != nil {
return
}
if v, err = automation.NewComposeNamespace(res.oldNamespace); err == nil {
err = out.Set("oldNamespace", v)
}
if err != nil {
return
}
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -1737,22 +1757,26 @@ func (res pageBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res pageBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res pageBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
// Could not found expression-type counterpart for *types.Page
// Could not found expression-type counterpart for *types.Page
if rvars["namespace"], err = automation.NewComposeNamespace(res.namespace); err != nil {
return nil, err
if v, err = automation.NewComposeNamespace(res.namespace); err == nil {
err = out.Set("namespace", v)
}
if err != nil {
return
}
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -2330,34 +2354,54 @@ func (res recordBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res recordBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res recordBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
if rvars["record"], err = automation.NewComposeRecord(res.record); err != nil {
return nil, err
if v, err = automation.NewComposeRecord(res.record); err == nil {
err = out.Set("record", v)
}
if rvars["oldRecord"], err = automation.NewComposeRecord(res.oldRecord); err != nil {
return nil, err
if err != nil {
return
}
if rvars["module"], err = automation.NewComposeModule(res.module); err != nil {
return nil, err
if v, err = automation.NewComposeRecord(res.oldRecord); err == nil {
err = out.Set("oldRecord", v)
}
if rvars["namespace"], err = automation.NewComposeNamespace(res.namespace); err != nil {
return nil, err
if err != nil {
return
}
if rvars["recordValueErrors"], err = automation.NewComposeRecordValueErrorSet(res.recordValueErrors); err != nil {
return nil, err
if v, err = automation.NewComposeModule(res.module); err == nil {
err = out.Set("module", v)
}
if err != nil {
return
}
if v, err = automation.NewComposeNamespace(res.namespace); err == nil {
err = out.Set("namespace", v)
}
if err != nil {
return
}
if v, err = automation.NewComposeRecordValueErrorSet(res.recordValueErrors); err == nil {
err = out.Set("recordValueErrors", v)
}
if err != nil {
return
}
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
+11 -9
View File
@@ -147,25 +147,27 @@ func (res {{ camelCase .ResourceIdent "base" }}) Encode() (args map[string][]byt
}
// Encode internal data to be passed as event params & arguments to workflow
func (res {{ camelCase .ResourceIdent "base" }}) EncodeVars() (vars *expr.Vars, err error) {
func (res {{ camelCase .ResourceIdent "base" }}) EncodeVars() (out *expr.Vars, err error) {
{{- if $r.Properties }}
var (
rvars = expr.RVars{}
)
out = &expr.Vars{}
var v expr.TypedValue
{{ range $r.Properties }}
{{- if .ExprType }}
if rvars[{{ printf "%q" .Name }}], err = automation.{{ export "new" .ExprType }}(res.{{ .Name }}); err != nil {
return nil, err
if v, err = automation.{{ export "new" .ExprType }}(res.{{ .Name }}); err == nil {
err = out.Set({{ printf "%q" .Name }},v)
}
if err != nil {
return
}
{{- else }}
// Could not found expression-type counterpart for {{ .Type }}
{{- end }}
{{ end }}
return rvars.Vars(), err
{{ else }}
return
{{ end -}}
_ = v
return
}
// Decode return values from Corredor script into struct props
+10 -10
View File
@@ -39,19 +39,19 @@ func TestVars(t *testing.T) {
var (
req = require.New(t)
vars = RVars{
vars = Must(Typify(map[string]interface{}{
"int": Must(NewInteger(42)),
"sub": RVars{
"sub": map[string]interface{}{
"foo": Must(NewString("foo")),
}.Vars(),
"three": RVars{
"two": RVars{
"one": RVars{
},
"three": map[string]interface{}{
"two": map[string]interface{}{
"one": map[string]interface{}{
"go": Must(NewString("!")),
}.Vars(),
}.Vars(),
}.Vars(),
}.Vars()
},
},
},
}))
tv = func(in interface{}) TypedValue {
switch cnv := in.(type) {
+8 -4
View File
@@ -550,8 +550,12 @@ func (t *UnsignedInteger) Assign(val interface{}) error {
}
}
// Vars is an expression type, wrapper for RVars type
type Vars struct{ value RVars }
// Vars is an expression type, wrapper for map[string]TypedValue type
type Vars struct{ value map[string]TypedValue }
func EmptyVars() *Vars {
return &Vars{make(map[string]TypedValue)}
}
// NewVars creates new instance of Vars expression type
func NewVars(val interface{}) (*Vars, error) {
@@ -566,12 +570,12 @@ func NewVars(val interface{}) (*Vars, error) {
func (t Vars) Get() interface{} { return t.value }
// Return underlying value on Vars
func (t Vars) GetValue() RVars { return t.value }
func (t Vars) GetValue() map[string]TypedValue { return t.value }
// Return type name
func (Vars) Type() string { return "Vars" }
// Convert value to RVars
// Convert value to map[string]TypedValue
func (Vars) Cast(val interface{}) (TypedValue, error) {
return NewVars(val)
}
+12 -3
View File
@@ -24,6 +24,10 @@ type (
}
)
func EmptyVars() *Vars {
return &Vars{value: map[string]TypedValue{}}
}
func ResolveTypes(rt resolvableType, resolver func(typ string) Type) error {
return rt.ResolveTypes(resolver)
}
@@ -36,9 +40,6 @@ func Typify(in interface{}) (tv TypedValue, err error) {
}
switch c := in.(type) {
// @todo
//case map[string]interface{}:
// return NewVars()
case []TypedValue:
return &Array{value: c}, nil
case bool:
@@ -75,6 +76,14 @@ func Typify(in interface{}) (tv TypedValue, err error) {
return &Duration{value: *c}, nil
case time.Duration:
return &Duration{value: c}, nil
case map[string]interface{}:
if v, err := CastToVars(c); err != nil {
return nil, err
} else {
return &Vars{v}, nil
}
case map[string]TypedValue:
return &Vars{c}, nil
case map[string]string:
return &KV{value: c}, nil
case map[string][]string:
+1 -1
View File
@@ -11,7 +11,7 @@ types:
as: '[]TypedValue'
Vars:
as: 'RVars'
as: 'map[string]TypedValue'
Boolean:
as: 'bool'
+15 -11
View File
@@ -9,13 +9,13 @@ import (
)
func TestTypedValueOperations(t *testing.T) {
scope := RVars{
"xUint": Must(NewUnsignedInteger(1)),
"xInt": Must(NewInteger(1)),
"xBoolT": Must(NewBoolean(true)),
"xBoolF": Must(NewBoolean(false)),
"xString": Must(NewString("foo")),
}.Vars()
scope, _ := NewVars(map[string]interface{}{
"xUint": uint(1),
"xInt": 1,
"xBoolT": true,
"xBoolF": false,
"xString": "foo",
})
tcc := []struct {
expects interface{}
@@ -27,11 +27,11 @@ func TestTypedValueOperations(t *testing.T) {
// uint ops
{true, "xUint == 1"},
{uint64(1), "xUint"},
{uint(1), "xUint"},
// uint ops
{true, "xInt == 1"},
{int64(1), "xInt"},
{int(1), "xInt"},
// string ops
{true, `xString == "foo"`},
@@ -157,12 +157,16 @@ func TestArrayDecode(t *testing.T) {
})
req.NoError(err)
req.NoError(RVars{
vars, err := NewVars(map[string]interface{}{
"strings": &Array{arr},
"iface": Must(NewString("typed")),
"typed": Must(NewString("typed")),
"values": &Array{arr},
}.Vars().Decode(&foo))
})
req.NoError(err)
req.NoError(vars.Decode(&foo))
req.Len(foo.Strings, 2)
req.Len(foo.Values, 2)
}
@@ -58,7 +58,7 @@ func isEmpty(i interface{}) bool {
}
switch reflect.TypeOf(i).Kind() {
case reflect.Slice, reflect.Array, reflect.Ptr, reflect.Map:
case reflect.Slice, reflect.Array, reflect.Map:
return reflect.ValueOf(i).Len() == 0
}
+2 -2
View File
@@ -66,11 +66,11 @@ func (p *gvalParser) ParseEvaluators(ee ...Evaluator) error {
}
func (e *gvalEval) Eval(ctx context.Context, scope *Vars) (interface{}, error) {
return e.evaluable(ctx, scope.Dict())
return e.evaluable(ctx, scope)
}
func (e *gvalEval) Test(ctx context.Context, scope *Vars) (bool, error) {
r, err := e.evaluable(ctx, scope.Dict())
r, err := e.evaluable(ctx, scope)
if err != nil {
return false, err
}
+50 -33
View File
@@ -12,16 +12,6 @@ import (
"strings"
)
type (
// RVars or raw-vars, used as internal type for Vars expr type
RVars map[string]TypedValue
)
// Vars is a utility func that returns RVars wrapped in Vars
func (v RVars) Vars() *Vars {
return &Vars{value: v}
}
func (t Vars) Len() int {
return len(t.value)
}
@@ -36,7 +26,7 @@ func (t Vars) Select(k string) (TypedValue, error) {
func (t *Vars) AssignFieldValue(key string, val TypedValue) (err error) {
if t.value == nil {
t.value = make(RVars)
t.value = make(map[string]TypedValue)
}
t.value[key] = val
@@ -70,7 +60,7 @@ func (t Vars) ResolveTypes(res func(typ string) Type) (err error) {
// Assign takes base variables and assigns all new variables
func (t *Vars) Merge(nn ...Iterator) *Vars {
var (
out = &Vars{value: make(RVars)}
out = &Vars{value: make(map[string]TypedValue)}
)
nn = append([]Iterator{t}, nn...)
@@ -92,7 +82,7 @@ func (t *Vars) Copy(dst *Vars, kk ...string) {
}
if dst.value == nil {
dst.value = make(RVars)
dst.value = make(map[string]TypedValue)
}
for _, k := range kk {
@@ -135,17 +125,12 @@ func (t *Vars) HasAny(key string, kk ...string) bool {
return false
}
func (t *Vars) Dict() map[string]interface{} {
if t == nil {
return nil
}
var _ gval.Selector = &Vars{}
func (t *Vars) Dict() map[string]interface{} {
dict := make(map[string]interface{})
for k, v := range t.value {
switch v := v.(type) {
case gval.Selector:
dict[k] = v
case Dict:
dict[k] = v.Dict()
@@ -227,25 +212,31 @@ func (t *Vars) Value() (driver.Value, error) {
}
func (t Vars) SelectGVal(_ context.Context, k string) (interface{}, error) {
return t.Select(k)
val, err := t.Select(k)
switch c := val.(type) {
case gval.Selector:
return c, nil
default:
return UntypedValue(val), err
}
}
// UnmarshalJSON
func (t *Vars) UnmarshalJSON(in []byte) (err error) {
if len(in) == 0 {
return nil
}
var (
aux = make(map[string]*typedValueWrap)
)
if err = json.Unmarshal(in, &aux); err != nil {
return
if t.value == nil {
t.value = make(map[string]TypedValue)
}
if t.value == nil && len(aux) > 0 {
t.value = make(map[string]TypedValue)
if len(in) == 0 {
return nil
}
if err = json.Unmarshal(in, &aux); err != nil {
return
}
for k, v := range aux {
@@ -271,6 +262,15 @@ func (t *Vars) Each(fn func(k string, v TypedValue) error) (err error) {
return
}
func (t *Vars) Set(k string, v interface{}) (err error) {
if t.value == nil {
t.value = make(map[string]TypedValue)
}
t.value[k], err = Typify(v)
return
}
// UnmarshalJSON parses sort expression when passed inside JSON
func (t Vars) MarshalJSON() ([]byte, error) {
aux := make(map[string]*typedValueWrap)
@@ -307,8 +307,12 @@ func decode(dst reflect.Value, src TypedValue) (err error) {
return
}
raw := UntypedValue(src)
if reflect.ValueOf(src).Type().ConvertibleTo(dst.Type()) {
dst.Set(reflect.ValueOf(src))
return
}
raw := UntypedValue(src)
// Optimistically try to decode source to destination by comparing (internal) value type for destination
if reflect.ValueOf(raw).Type().ConvertibleTo(dst.Type()) {
dst.Set(reflect.ValueOf(raw))
@@ -349,6 +353,9 @@ func decode(dst reflect.Value, src TypedValue) (err error) {
dst.SetString(vString)
}
case reflect.Map:
dst.Set(reflect.ValueOf(src.Get()))
//case reflect.Interface:
// dst.Set(reflect.ValueOf(raw))
@@ -363,18 +370,28 @@ func decode(dst reflect.Value, src TypedValue) (err error) {
return nil
}
func CastToVars(val interface{}) (out RVars, err error) {
func CastToVars(val interface{}) (out map[string]TypedValue, err error) {
val = UntypedValue(val)
if val == nil {
return make(RVars), nil
return make(map[string]TypedValue), nil
}
switch c := val.(type) {
case *Vars:
return c.value, nil
case RVars:
case map[string]TypedValue:
return c, nil
case map[string]interface{}:
out = make(map[string]TypedValue)
for k, v := range c {
out[k], err = Typify(v)
if err != nil {
return
}
}
return
}
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
+39 -26
View File
@@ -9,7 +9,6 @@ import (
// extract typed-value
func TestVars_Decode(t *testing.T) {
t.Run("mix", func(t *testing.T) {
var (
req = require.New(t)
@@ -23,12 +22,12 @@ func TestVars_Decode(t *testing.T) {
Unexisting byte
}{}
vars = RVars{
vars, _ = NewVars(map[string]interface{}{
"int": Must(NewInteger(42)),
"STRING": Must(NewString("foo")),
"bool": Must(NewBoolean(true)),
"missing": Must(NewBoolean(true)),
}.Vars()
})
)
req.NoError(vars.Decode(dst))
@@ -48,11 +47,11 @@ func TestVars_Decode(t *testing.T) {
IBool interface{} `var:"iBool"`
}{}
vars = RVars{
vars, _ = NewVars(map[string]interface{}{
"iString": Must(NewString("foo")),
"iInteger": Must(NewInteger(42)),
"iBool": Must(NewBoolean(true)),
}.Vars()
})
)
req.NoError(vars.Decode(dst))
@@ -63,12 +62,12 @@ func TestVars_Decode(t *testing.T) {
req = require.New(t)
dst = &struct {
Vars RVars `var:"vars"`
Vars *Vars `var:"vars"`
}{}
vars = RVars{
"vars": RVars{"foo": Must(NewString("bar"))}.Vars(),
}.Vars()
vars, _ = NewVars(map[string]interface{}{
"vars": map[string]interface{}{"foo": Must(NewString("bar"))},
})
)
req.NoError(vars.Decode(dst))
@@ -83,10 +82,10 @@ func TestVars_Decode(t *testing.T) {
Uint64 uint64
}{}
vars = RVars{
vars, _ = NewVars(map[string]TypedValue{
"uint64": Must(NewAny("42")),
"int": Must(NewAny("42")),
}.Vars()
})
)
dst.Uint64 = 0
@@ -96,52 +95,66 @@ func TestVars_Decode(t *testing.T) {
req.Equal(uint64(42), dst.Uint64)
req.Equal(int64(42), dst.Int)
})
}
func TestVars_Assign(t *testing.T) {
var (
req = require.New(t)
vars = &Vars{}
)
req.NoError(Assign(vars, "foo", &String{value: "foo"}))
req.NoError(Assign(vars, "vars", &Vars{}))
req.NoError(Assign(vars, "vars.foo", &String{value: "foo"}))
}
func TestVars_UnmarshalJSON(t *testing.T) {
cases := []struct {
name string
json string
vars *Vars
vars map[string]interface{}
}{
{"empty", "", &Vars{}},
{"empty", "{}", &Vars{}},
{"string", `{"a":{"@value":"b"}}`, RVars{"a": &Unresolved{value: "b"}}.Vars()},
{"empty", "", make(map[string]interface{})},
{"object", "{}", make(map[string]interface{})},
{"string", `{"a":{"@value":"b"}}`, map[string]interface{}{"a": &Unresolved{value: "b"}}},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var (
r = require.New(t)
v = &Vars{}
r = require.New(t)
unmarshaled = &Vars{}
aux, _ = NewVars(c.vars)
)
r.NoError(v.UnmarshalJSON([]byte(c.json)))
r.Equal(c.vars, v)
r.NoError(unmarshaled.UnmarshalJSON([]byte(c.json)))
r.Equal(aux, unmarshaled)
})
}
}
func TestVars_UMarshalJSON(t *testing.T) {
func TestVars_MarshalJSON(t *testing.T) {
cases := []struct {
name string
json string
vars *Vars
vars map[string]interface{}
}{
{"empty", "{}", &Vars{}},
{"string", `{"a":{"@value":"b","@type":"String"}}`, RVars{"a": &String{value: "b"}}.Vars()},
{"empty", "{}", nil},
{"string", `{"a":{"@value":"b","@type":"String"}}`, map[string]interface{}{"a": &String{value: "b"}}},
{"array",
`{"arr":{"@value":[{"@value":"foo","@type":"String"},{"@value":"bar","@type":"String"}],"@type":"Array"}}`,
RVars{"arr": &Array{value: []TypedValue{&String{value: "foo"}, &String{value: "bar"}}}}.Vars()},
map[string]interface{}{"arr": &Array{value: []TypedValue{&String{value: "foo"}, &String{value: "bar"}}}}},
}
for _, c := range cases {
t.Run(c.name, func(t *testing.T) {
var (
r = require.New(t)
r = require.New(t)
aux, _ = NewVars(c.vars)
)
j, err := json.Marshal(c.vars)
j, err := json.Marshal(aux)
r.NoError(err)
r.Equal(c.json, string(j))
})
+5 -2
View File
@@ -3,18 +3,21 @@ package wfexec
import (
"context"
"github.com/cortezaproject/corteza-server/pkg/expr"
"github.com/spf13/cast"
"github.com/stretchr/testify/require"
"testing"
)
func gt(key string, val int64) pathTester {
return func(ctx context.Context, variables *expr.Vars) (bool, error) {
return expr.Must(expr.Select(variables, key)).Get().(int64) > val, nil
return cast.ToInt64(expr.Must(expr.Select(variables, key)).Get()) > val, nil
}
}
func makeIntVars(k string, num int) *expr.Vars {
return expr.RVars{k: expr.Must(expr.NewInteger(num))}.Vars()
o := &expr.Vars{}
_ = o.Set(k, num)
return o
}
func TestJoinGateway(t *testing.T) {
+3 -4
View File
@@ -483,10 +483,9 @@ func (s *Session) queueScheduledSuspended() {
delete(s.delayed, id)
// Set state input when step is resumed
sus.state.input = expr.RVars{
"resumed": expr.Must(expr.NewBoolean(true)),
"resumeAt": expr.Must(expr.NewDateTime(sus.resumeAt)),
}.Vars()
sus.state.input = &expr.Vars{}
sus.state.input.Set("resumed", true)
sus.state.input.Set("resumeAt", sus.resumeAt)
s.qState <- sus.state
}
}
+28 -21
View File
@@ -33,20 +33,22 @@ func (s *sesTestStep) Exec(ctx context.Context, r *ExecRequest) (ExecResponse, e
return s.exec(ctx, r)
}
var args = &struct {
Path string
Counter int64
}{}
var (
args = &struct {
Path string
Counter int64
}{}
)
if err := r.Scope.Decode(args); err != nil {
return nil, err
}
return expr.RVars{
"counter": expr.Must(expr.NewInteger(args.Counter + 1)),
"path": expr.Must(expr.NewString(args.Path + "/" + s.name)),
s.name: expr.Must(expr.NewString("executed")),
}.Vars(), nil
return expr.NewVars(map[string]interface{}{
"counter": args.Counter + 1,
"path": args.Path + "/" + s.name,
s.name: "executed",
})
}
func (s *sesTestTemporal) Exec(ctx context.Context, r *ExecRequest) (ExecResponse, error) {
@@ -58,9 +60,9 @@ func (s *sesTestTemporal) Exec(ctx context.Context, r *ExecRequest) (ExecRespons
return Delay(s.until), nil
}
return expr.RVars{
"waitForMoment": expr.Must(expr.NewString("executed")),
}.Vars(), nil
return expr.NewVars(map[string]interface{}{
"waitForMoment": "executed",
})
}
func TestSession_TwoStepWorkflow(t *testing.T) {
@@ -72,11 +74,16 @@ func TestSession_TwoStepWorkflow(t *testing.T) {
s1 = &sesTestStep{name: "s1"}
s2 = &sesTestStep{name: "s2"}
scope = &expr.Vars{}
)
scope.Set("two", 1)
scope.Set("three", 1)
wf.AddStep(s1, s2) // 1st execute s1 then s2
ses.Exec(ctx, s1, expr.RVars{"two": expr.Must(expr.NewInteger(1)), "three": expr.Must(expr.NewInteger(1))}.Vars())
ses.Wait(ctx)
req.NoError(ses.Exec(ctx, s1, scope))
req.NoError(ses.Wait(ctx))
req.NoError(ses.Error())
req.NotNil(ses.Result())
req.Equal("/s1/s2", expr.Must(expr.Select(ses.Result(), "path")).Get())
@@ -140,10 +147,8 @@ func TestSession_Delays(t *testing.T) {
return Prompt(0, "", nil), nil
}
out := expr.RVars{
"waitForInput": expr.Must(expr.NewString("executed")),
}.Vars()
out := &expr.Vars{}
_ = out.Set("waitForInput", "executed")
r.Input.Copy(out, "input")
return out, nil
@@ -159,7 +164,7 @@ func TestSession_Delays(t *testing.T) {
req.NoError(ses.Exec(ctx, start, nil))
// wait-for-moment step needs to be executed before we can resume wait-for-input
ses.Wait(ctx)
req.NoError(ses.Wait(ctx))
time.Sleep(delay + unit)
req.NotZero(waitForInputStateId.Load())
@@ -168,10 +173,12 @@ func TestSession_Delays(t *testing.T) {
req.True(ses.Suspended())
// push in the input
req.NoError(ses.Resume(ctx, waitForInputStateId.Load(), expr.RVars{"input": expr.Must(expr.NewString("foo"))}.Vars()))
input := &expr.Vars{}
input.Set("inout", "foo")
req.NoError(ses.Resume(ctx, waitForInputStateId.Load(), input))
req.False(ses.Suspended())
ses.Wait(ctx)
req.NoError(ses.Wait(ctx))
time.Sleep(2 * unit)
// should not be completed yet...
+4 -13
View File
@@ -3,6 +3,7 @@ package automation
import (
"fmt"
"io"
"io/ioutil"
"github.com/cortezaproject/corteza-server/pkg/expr"
"github.com/cortezaproject/corteza-server/system/types"
@@ -130,17 +131,7 @@ func CastToRenderOptions(val interface{}) (out map[string]string, err error) {
}
}
func CastToRenderVariables(val interface{}) (out map[string]interface{}, err error) {
switch val := expr.UntypedValue(val).(type) {
case map[string]interface{}:
return val, nil
case nil:
return make(map[string]interface{}), nil
default:
out, err = cast.ToStringMapE(val)
if err != nil {
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
}
return out, nil
}
func (doc renderedDocument) String() string {
aux, _ := ioutil.ReadAll(doc.Document)
return string(aux)
}
+5 -4
View File
@@ -23,16 +23,17 @@ func TestUser(t *testing.T) {
func TestUser_Expr(t *testing.T) {
var (
req = require.New(t)
u, err = NewUser(&types.User{Handle: "hendl"})
req = require.New(t)
u, _ = NewUser(&types.User{Handle: "hendl"})
scope = &expr.Vars{}
)
req.NoError(err)
req.NoError(scope.Set("user", u))
eval, err := expr.NewParser().Parse("user.handle")
req.NoError(err)
res, err := eval.Eval(context.Background(), expr.RVars{"user": u}.Vars())
res, err := eval.Eval(context.Background(), scope)
req.NoError(err)
req.Equal("hendl", res.(string))
+5 -5
View File
@@ -218,11 +218,11 @@ func (i *roleSetIterator) More(context.Context, *Vars) (bool, error) {
func (i *roleSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
func (i *roleSetIterator) Next(context.Context, *Vars) (*Vars, error) {
out := RVars{
"role": Must(NewRole(i.set[i.ptr])),
"total": Must(NewUnsignedInteger(i.filter.Total)),
}
out := &Vars{}
out.Set("role", Must(NewRole(i.set[i.ptr])))
out.Set("index", i.ptr)
out.Set("total", i.filter.Total)
i.ptr++
return out.Vars(), nil
return out, nil
}
+1 -1
View File
@@ -746,7 +746,7 @@ type (
DocumentType string
hasVariables bool
Variables expr.RVars
Variables *expr.Vars
hasOptions bool
Options map[string]string
+6 -11
View File
@@ -163,11 +163,6 @@ func (h templatesHandler) recover(ctx context.Context, args *templatesRecoverArg
func (h templatesHandler) render(ctx context.Context, args *templatesRenderArgs) (*templatesRenderResults, error) {
var err error
vars := args.Variables.Vars().Dict()
if vars == nil {
vars = make(map[string]interface{})
}
opts := make(map[string]string)
if args.hasOptions {
opts, err = cast.ToStringMapStringE(args.Options)
@@ -181,7 +176,7 @@ func (h templatesHandler) render(ctx context.Context, args *templatesRenderArgs)
return nil, err
}
doc, err := h.tSvc.Render(ctx, tplID, args.DocumentType, vars, opts)
doc, err := h.tSvc.Render(ctx, tplID, args.DocumentType, args.Variables.Dict(), opts)
if err != nil {
return nil, err
}
@@ -204,13 +199,13 @@ func (i *templateSetIterator) More(context.Context, *Vars) (bool, error) {
func (i *templateSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
func (i *templateSetIterator) Next(context.Context, *Vars) (*Vars, error) {
out := RVars{
"template": Must(NewTemplate(i.set[i.ptr])),
"total": Must(NewUnsignedInteger(i.filter.Total)),
}
out := &Vars{}
out.Set("template", Must(NewTemplate(i.set[i.ptr])))
out.Set("index", i.ptr)
out.Set("total", i.filter.Total)
i.ptr++
return out.Vars(), nil
return out, nil
}
func lookupTemplate(ctx context.Context, svc templateService, args templateLookup) (*types.Template, error) {
+1 -1
View File
@@ -153,7 +153,7 @@ functions:
- { wf: String }
variables:
types:
- { wf: Vars, go: 'expr.RVars' }
- { wf: Vars, go: '*expr.Vars' }
options:
types:
- { wf: RenderOptions }
+5 -5
View File
@@ -219,11 +219,11 @@ func (i *userSetIterator) More(context.Context, *Vars) (bool, error) {
func (i *userSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
func (i *userSetIterator) Next(context.Context, *Vars) (*Vars, error) {
out := RVars{
"user": Must(NewUser(i.set[i.ptr])),
"total": Must(NewUnsignedInteger(i.filter.Total)),
}
out := &Vars{}
out.Set("user", Must(NewUser(i.set[i.ptr])))
out.Set("index", i.ptr)
out.Set("total", i.filter.Total)
i.ptr++
return out.Vars(), nil
return out, nil
}
+87 -59
View File
@@ -536,14 +536,14 @@ func (res systemBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res systemBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res systemBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -921,10 +921,9 @@ func (res applicationBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res applicationBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res applicationBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
// Could not found expression-type counterpart for *types.Application
@@ -932,7 +931,8 @@ func (res applicationBase) EncodeVars() (vars *expr.Vars, err error) {
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -1214,20 +1214,24 @@ func (res authBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res authBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res authBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
if rvars["user"], err = automation.NewUser(res.user); err != nil {
return nil, err
if v, err = automation.NewUser(res.user); err == nil {
err = out.Set("user", v)
}
if err != nil {
return
}
// Could not found expression-type counterpart for *types.AuthProvider
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -1639,10 +1643,9 @@ func (res authClientBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res authClientBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res authClientBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
// Could not found expression-type counterpart for *types.AuthClient
@@ -1650,7 +1653,8 @@ func (res authClientBase) EncodeVars() (vars *expr.Vars, err error) {
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -1861,16 +1865,16 @@ func (res mailBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res mailBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res mailBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
// Could not found expression-type counterpart for *types.MailMessage
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -2265,22 +2269,30 @@ func (res roleBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res roleBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res roleBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
if rvars["role"], err = automation.NewRole(res.role); err != nil {
return nil, err
if v, err = automation.NewRole(res.role); err == nil {
err = out.Set("role", v)
}
if rvars["oldRole"], err = automation.NewRole(res.oldRole); err != nil {
return nil, err
if err != nil {
return
}
if v, err = automation.NewRole(res.oldRole); err == nil {
err = out.Set("oldRole", v)
}
if err != nil {
return
}
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -2570,22 +2582,30 @@ func (res roleMemberBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res roleMemberBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res roleMemberBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
if rvars["user"], err = automation.NewUser(res.user); err != nil {
return nil, err
if v, err = automation.NewUser(res.user); err == nil {
err = out.Set("user", v)
}
if rvars["role"], err = automation.NewRole(res.role); err != nil {
return nil, err
if err != nil {
return
}
if v, err = automation.NewRole(res.role); err == nil {
err = out.Set("role", v)
}
if err != nil {
return
}
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -2759,10 +2779,9 @@ func (res sinkBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res sinkBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res sinkBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
// Could not found expression-type counterpart for *types.SinkResponse
@@ -2770,7 +2789,8 @@ func (res sinkBase) EncodeVars() (vars *expr.Vars, err error) {
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
@@ -3168,22 +3188,30 @@ func (res userBase) Encode() (args map[string][]byte, err error) {
}
// Encode internal data to be passed as event params & arguments to workflow
func (res userBase) EncodeVars() (vars *expr.Vars, err error) {
var (
rvars = expr.RVars{}
)
func (res userBase) EncodeVars() (out *expr.Vars, err error) {
out = &expr.Vars{}
var v expr.TypedValue
if rvars["user"], err = automation.NewUser(res.user); err != nil {
return nil, err
if v, err = automation.NewUser(res.user); err == nil {
err = out.Set("user", v)
}
if rvars["oldUser"], err = automation.NewUser(res.oldUser); err != nil {
return nil, err
if err != nil {
return
}
if v, err = automation.NewUser(res.oldUser); err == nil {
err = out.Set("oldUser", v)
}
if err != nil {
return
}
// Could not found expression-type counterpart for auth.Identifiable
return rvars.Vars(), err
_ = v
return
}
// Decode return values from Corredor script into struct props
+1 -1
View File
@@ -151,7 +151,7 @@ func TestTriggerCreateFull(t *testing.T) {
WorkflowID: wf.ID,
ResourceType: "wf-full-test",
Enabled: true,
Input: &expr.Vars{},
Input: expr.EmptyVars(),
OwnedBy: 42,
}
)
+9 -5
View File
@@ -183,11 +183,15 @@ func TestWorkflowCreateFull(t *testing.T) {
Enabled: true,
Trace: true,
KeepSessions: 10000,
Scope: expr.RVars{"foo": expr.Must(expr.NewString("bar"))}.Vars(),
Steps: types.WorkflowStepSet{},
Paths: types.WorkflowPathSet{},
RunAs: 0,
OwnedBy: 42,
Scope: func() *expr.Vars {
o := &expr.Vars{}
_ = o.Set("foo", "bar")
return o
}(),
Steps: types.WorkflowStepSet{},
Paths: types.WorkflowPathSet{},
RunAs: 0,
OwnedBy: 42,
}
)