Skip to content

Commit

Permalink
Extend set/omit expr function
Browse files Browse the repository at this point in the history
It updates 1st parameter of set expr function from merger type to interface, so It will accept constant values, also extend set/omit usage for ComposeRecordValues.
  • Loading branch information
vicpatel committed Feb 28, 2022
1 parent 5ff68c4 commit 86e2ef3
Show file tree
Hide file tree
Showing 7 changed files with 367 additions and 12 deletions.
102 changes: 102 additions & 0 deletions compose/automation/expr_types.go
Expand Up @@ -518,3 +518,105 @@ func CastToAttachment(val interface{}) (out *types.Attachment, err error) {
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
}
}

func EmptyComposeRecordValues() *ComposeRecordValues {
return &ComposeRecordValues{value: &types.Record{Values: types.RecordValueSet{}}}
}

func (t *ComposeRecordValues) Each(fn func(k string, v expr.TypedValue) error) (err error) {
if t.IsEmpty() {
return
}

for _, vv := range t.GetValue() {
key := vv.Name
if len(key) == 0 {
continue
}

var val expr.TypedValue
val, err = expr.Typify(vv)
if err = fn(key, val); err != nil {
return
}
}

return
}

// Merge combines the given ComposeRecordValues into ComposeRecordValues
// NOTE: It will return CLONE of the original ComposeRecordValues, if it's called without any parameters
func (t *ComposeRecordValues) Merge(nn ...expr.Iterator) (out expr.TypedValue, err error) {
rv := EmptyComposeRecordValues()

nn = append([]expr.Iterator{t}, nn...)

for _, i := range nn {
err = i.Each(func(k string, v expr.TypedValue) error {
var (
rVal types.RecordValue
bb []byte
)

// @todo implement casting of RecordValue from TypedValue
bb, err = json.Marshal(v.Get())
if err != nil {
return err
}

err = json.Unmarshal(bb, &rVal)
if err != nil {
return err
}

rr := rv.GetValue().Set(&rVal)
err = rv.Assign(rr)
if err != nil {
return err
}

return err
})
if err != nil {
return
}
}

return rv, nil
}

func (t *ComposeRecordValues) Delete(keys ...string) (out expr.TypedValue, err error) {
if t.IsEmpty() {
return
}

// get cloned ComposeRecordValues
out, err = t.Merge()
if err != nil {
return
}

rv := out.(*ComposeRecordValues)
if !rv.IsEmpty() {
rv.value.Values = types.RecordValueSet{}
}

keyMap := make(map[string]string)
for _, k := range keys {
keyMap[k] = k
}

// Push the only with values with non-matching Name
for _, val := range t.GetValue() {
_, ok := keyMap[val.Name]
if !ok {
rr := rv.GetValue().Set(val.Clone())
err = rv.Assign(rr)
if err != nil {
return out, err
}
}
}

return rv, nil
}
89 changes: 89 additions & 0 deletions compose/automation/expr_types_test.go
Expand Up @@ -580,3 +580,92 @@ func TestGvalStringFunctionParamsIntCasting(t *testing.T) {
})

}

func TestRecordValues_Merge(t *testing.T) {
var (
req = require.New(t)

rv = &ComposeRecordValues{}
foo = &ComposeRecordValues{
value: &types.Record{
Values: []*types.RecordValue{
{
Name: "k1",
Value: "testValue1",
},
{
Name: "k2",
Value: "testValue2",
},
},
},
}
bar = &ComposeRecordValues{
value: &types.Record{
Values: []*types.RecordValue{
{
Name: "k3",
Value: "testValue3",
},
},
},
}
expected = &types.Record{
Values: []*types.RecordValue{
{
Name: "k1",
Value: "testValue1",
},
{
Name: "k2",
Value: "testValue2",
},
{
Name: "k3",
Value: "testValue3",
},
},
}
)

out, err := rv.Merge(foo, bar)
req.NoError(err)
req.Equal(expected, out.Get())
}

func TestRecordValues_Omit(t *testing.T) {
var (
req = require.New(t)

rv = ComposeRecordValues{
value: &types.Record{
Values: []*types.RecordValue{
{
Name: "k1",
Value: "testValue1",
},
{
Name: "k2",
Value: "testValue2",
},
{
Name: "k3",
Value: "testValue3",
},
},
},
}
expected = &types.Record{
Values: []*types.RecordValue{
{
Name: "k2",
Value: "testValue2",
},
},
}
)

out, err := rv.Delete("k1", "k3")
req.NoError(err)
req.Equal(expected, out.Get())
}
4 changes: 2 additions & 2 deletions compose/types/record_value.go
Expand Up @@ -120,7 +120,7 @@ func (set RecordValueSet) FilterByRecordID(recordID uint64) (vv RecordValueSet)
return
}

// Replaces existing values, remove extra
// Replace existing values, remove extra
func (set RecordValueSet) Replace(name string, values ...string) (vv RecordValueSet) {
for i := range set {
if set[i].Name != name {
Expand Down Expand Up @@ -160,7 +160,7 @@ func (set RecordValueSet) Set(v *RecordValue) RecordValueSet {
return append(set, v)
}

// Has value set?
// Get value set?
func (set RecordValueSet) Get(name string, place uint) *RecordValue {
for i := range set {
if set[i].Name != name {
Expand Down
15 changes: 14 additions & 1 deletion pkg/expr/expr_types.go
Expand Up @@ -63,7 +63,20 @@ func ResolveTypes(rt resolvableType, resolver func(typ string) Type) error {
return rt.ResolveTypes(resolver)
}

func set(m merger, key string, val interface{}) (out TypedValue, err error) {
func set(i interface{}, key string, val interface{}) (out TypedValue, err error) {
m, ok := i.(merger)
if !ok {
i, err = Typify(i)
if err != nil {
return
}

m, ok = i.(merger)
if !ok {
return out, fmt.Errorf("cannot set on unexpected type: %T", i)
}
}

out, err = m.Merge()
if err != nil {
return
Expand Down
8 changes: 4 additions & 4 deletions pkg/expr/vars.go
Expand Up @@ -76,7 +76,7 @@ func (t *Vars) ResolveTypes(res func(typ string) Type) (err error) {
}

// Merge combines the given Vars(es) into Vars
// NOTE: It will return CLONE of the original Vars, if its called without any parameters
// NOTE: It will return CLONE of the original Vars, if it's called without any parameters
func (t *Vars) Merge(nn ...Iterator) (out TypedValue, err error) {
return t.MustMerge(nn...), nil
}
Expand Down Expand Up @@ -337,7 +337,7 @@ func (t *Vars) Each(fn func(k string, v TypedValue) error) (err error) {
return
}

// Set set/update the specific key value in KV
// Set or update the specific key value in Vars
func (t *Vars) Set(k string, v interface{}) (err error) {
t.mux.RLock()
defer t.mux.RUnlock()
Expand Down Expand Up @@ -513,7 +513,7 @@ func CastToVars(val interface{}) (out map[string]TypedValue, err error) {
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
}

// Filter take keys returns KV with only those key value pair
// Filter take keys returns Vars with only those key value pair
func (t *Vars) Filter(keys ...string) (out TypedValue, err error) {
t.mux.RLock()
defer t.mux.RUnlock()
Expand All @@ -533,7 +533,7 @@ func (t *Vars) Filter(keys ...string) (out TypedValue, err error) {
return vars, nil
}

// Delete take keys returns KV without those key value pair
// Delete take keys returns Vars without those key value pair
func (t *Vars) Delete(keys ...string) (out TypedValue, err error) {
t.mux.RLock()
defer t.mux.RUnlock()
Expand Down
68 changes: 65 additions & 3 deletions tests/workflows/0016_set_expression_issue_test.go
Expand Up @@ -2,6 +2,7 @@ package workflows

import (
"context"
cmpTypes "github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/expr"
"github.com/stretchr/testify/require"
"testing"
Expand All @@ -18,8 +19,16 @@ func Test0016_set_expression_issue(t *testing.T) {
loadScenario(ctx, t)

t.Run("set expressions", func(t *testing.T) {
type (
testInput struct {
Out map[string]expr.TypedValue
OutConstStr map[string]expr.TypedValue
OutConstInt map[string]expr.TypedValue
OutRv *cmpTypes.Record
}
)
var (
aux = struct{ Out map[string]expr.TypedValue }{}
aux = testInput{}
vars, _ = mustExecWorkflow(ctx, t, "set_expression", types.WorkflowExecParams{})

testString = expr.Must(expr.NewString("testing string"))
Expand All @@ -28,14 +37,67 @@ func Test0016_set_expression_issue(t *testing.T) {
"testString": testString,
"testFloat": expr.Must(expr.NewFloat(50)),
}))
expected = map[string]expr.TypedValue{

expectedVars = map[string]expr.TypedValue{
"testString": testString,
"testInt": testInt,
"testVar": testVar,
}
expected = testInput{
Out: expectedVars,
OutConstStr: map[string]expr.TypedValue{
"testConstKey": expr.Must(expr.NewString("testConstValue")),
},
OutConstInt: map[string]expr.TypedValue{
"testInt": testInt,
},

OutRv: &cmpTypes.Record{
Values: []*cmpTypes.RecordValue{
{
Name: "testRv",
Value: "testing string",
},
{
Name: "testFloat",
Value: "50",
},
},
},
}
)

req.NoError(vars.Decode(&aux))
req.Equal(expected, aux)
})

t.Run("omit expressions", func(t *testing.T) {
type (
testInput struct {
Out map[string]expr.TypedValue
OutConstInt map[string]expr.TypedValue
OutRv *cmpTypes.Record
}
)
var (
aux = testInput{}
vars, _ = mustExecWorkflow(ctx, t, "omit_expression", types.WorkflowExecParams{})

expected = testInput{
Out: map[string]expr.TypedValue{},
OutConstInt: map[string]expr.TypedValue{},
OutRv: &cmpTypes.Record{
Values: []*cmpTypes.RecordValue{
{
Name: "testFloat",
Value: "50",
},
},
},
}
)

req.NoError(vars.Decode(&aux))
req.Equal(expected, aux.Out)
req.Equal(expected, aux)
})
}

0 comments on commit 86e2ef3

Please sign in to comment.