3
0

Better handling empty/uninit values

This commit is contained in:
Denis Arh
2021-04-03 13:06:01 +02:00
parent 770f890d9e
commit 7383310d36
5 changed files with 118 additions and 8 deletions

View File

@@ -100,6 +100,9 @@ func (t ComposeModule) Has(k string) bool {
// composeModuleGValSelector is field accessor for *types.Module
func composeModuleGValSelector(res *types.Module, k string) (interface{}, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "moduleID":
return res.ID, nil
@@ -124,6 +127,9 @@ func composeModuleGValSelector(res *types.Module, k string) (interface{}, error)
// composeModuleTypedValueSelector is field accessor for *types.Module
func composeModuleTypedValueSelector(res *types.Module, k string) (TypedValue, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "moduleID":
return NewID(res.ID)
@@ -268,6 +274,9 @@ func (t ComposeNamespace) Has(k string) bool {
// composeNamespaceGValSelector is field accessor for *types.Namespace
func composeNamespaceGValSelector(res *types.Namespace, k string) (interface{}, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "namespaceID":
return res.ID, nil
@@ -290,6 +299,9 @@ func composeNamespaceGValSelector(res *types.Namespace, k string) (interface{},
// composeNamespaceTypedValueSelector is field accessor for *types.Namespace
func composeNamespaceTypedValueSelector(res *types.Namespace, k string) (TypedValue, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "namespaceID":
return NewID(res.ID)
@@ -420,6 +432,9 @@ func (t ComposeRecord) Has(k string) bool {
// composeRecordGValSelector is field accessor for *types.Record
func composeRecordGValSelector(res *types.Record, k string) (interface{}, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "recordID":
return res.ID, nil
@@ -452,6 +467,9 @@ func composeRecordGValSelector(res *types.Record, k string) (interface{}, error)
// composeRecordTypedValueSelector is field accessor for *types.Record
func composeRecordTypedValueSelector(res *types.Record, k string) (TypedValue, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "recordID":
return NewID(res.ID)

View File

@@ -113,12 +113,17 @@ var _ gval.Selector = &ComposeRecord{}
// and it's fields
//
func (t ComposeRecord) SelectGVal(_ context.Context, k string) (interface{}, error) {
if k == "values" {
if t.value != nil && k == "values" {
if t.value.Values == nil {
t.value.Values = types.RecordValueSet{}
}
return t.value.Values.Dict(t.value.GetModule().Fields), nil
var ff types.ModuleFieldSet
if t.value.GetModule() != nil {
ff = t.value.GetModule().Fields
}
return t.value.Values.Dict(ff), nil
}
return composeRecordGValSelector(t.value, k)
@@ -128,7 +133,7 @@ func (t ComposeRecord) SelectGVal(_ context.Context, k string) (interface{}, err
//
// Similar to SelectGVal but returns typed values
func (t ComposeRecord) Select(k string) (expr.TypedValue, error) {
if k == "values" {
if t.value != nil && k == "values" {
if t.value.Values == nil {
t.value.Values = types.RecordValueSet{}
}

View File

@@ -2,6 +2,7 @@ package automation
import (
"context"
"fmt"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/expr"
"github.com/stretchr/testify/require"
@@ -77,10 +78,21 @@ func TestRecordFieldValuesAccess(t *testing.T) {
&types.RecordValue{Name: "ref2", Value: "", Ref: 2, Place: 0},
}}
tval = &ComposeRecord{value: raw}
scope, _ = expr.NewVars(map[string]interface{}{
"rec": tval,
"rec": &ComposeRecord{value: raw},
// nis is only for testing, gval does not recognise nil (or null) as a keyword!
"nil": nil,
// typed value but empty (this happens when you assign next value in expression but do not set a value to it
"initRec": &ComposeRecord{},
// same as &ComposeRecord{value: &types.Record{}},
"validRecZero": &ComposeRecord{value: &types.Record{ID: 0, Values: types.RecordValueSet{}}},
"validRecValidID": &ComposeRecord{&types.Record{ID: 42, Values: types.RecordValueSet{}}},
// "record" (not really) set to nil
"fooRec": nil,
})
)
@@ -117,13 +129,42 @@ func TestRecordFieldValuesAccess(t *testing.T) {
t.Run("via gval selector", func(t *testing.T) {
tcc := []struct {
test bool
test interface{}
expr string
}{
{false, `nil`},
{true, `rec`},
{true, `rec.values`},
// not set, so false
{fmt.Errorf("failed to select 'notSetRec' on *expr.Vars: no such key 'notSetRec'"), `notSetRec`},
// set but nil
{false, `fooRec`},
// set, initialized
{true, `initRec`},
// set, initialized, but recordID is empty
{false, `initRec.recordID`},
// set, initialized, but recordID (ID is alias) is empty
{false, `initRec.ID`},
// set, initialized, but values are empty
{false, `initRec.values`},
{true, `validRecZero`},
{false, `validRecZero.recordID`},
{false, `validRecZero.values`},
{true, `validRecValidID`},
{true, `validRecValidID.recordID`},
{false, `validRecValidID.values`},
// but we can not access the values below...
{fmt.Errorf("unknown parameter initRec.values.foo"), `initRec.values.foo`},
// interaction with set values
{true, `rec.values.s1 == "sVal1"`},
{false, `rec.values.s1 == "sVal2"`},
@@ -184,8 +225,18 @@ func TestRecordFieldValuesAccess(t *testing.T) {
req.NoError(err)
test, err := eval.Test(context.Background(), scope)
req.NoError(err)
req.Equal(tc.test, test)
switch tct := tc.test.(type) {
case error:
req.EqualError(err, tct.Error())
case bool:
req.NoError(err)
req.Equal(tc.test, test)
default:
panic("unexpected test case type")
}
})
}
})

View File

@@ -105,6 +105,12 @@ func (t {{ $exprType }}) Has(k string) bool {
// {{ unexport $exprType "GValSelector" }} is field accessor for {{ $def.As }}
func {{ unexport $exprType "GValSelector" }}(res {{ $def.As }}, k string) (interface{}, error) {
{{- if hasPtr $def.As }}
if res == nil {
return nil, nil
}
{{- end }}
switch k {
{{- range $def.Struct }}
{{- if .ExprType }}
@@ -118,6 +124,12 @@ func {{ unexport $exprType "GValSelector" }}(res {{ $def.As }}, k string) (inter
// {{ unexport $exprType "TypedValueSelector" }} is field accessor for {{ $def.As }}
func {{ unexport $exprType "TypedValueSelector" }}(res {{ $def.As }}, k string) (TypedValue, error) {
{{- if hasPtr $def.As }}
if res == nil {
return nil, nil
}
{{- end }}
switch k {
{{- range $def.Struct }}
{{- if .ExprType }}

View File

@@ -166,6 +166,9 @@ func (t RenderedDocument) Has(k string) bool {
// renderedDocumentGValSelector is field accessor for *renderedDocument
func renderedDocumentGValSelector(res *renderedDocument, k string) (interface{}, error) {
if res == nil {
return nil, nil
}
switch k {
case "document":
return res.Document, nil
@@ -180,6 +183,9 @@ func renderedDocumentGValSelector(res *renderedDocument, k string) (interface{},
// renderedDocumentTypedValueSelector is field accessor for *renderedDocument
func renderedDocumentTypedValueSelector(res *renderedDocument, k string) (TypedValue, error) {
if res == nil {
return nil, nil
}
switch k {
case "document":
return NewReader(res.Document)
@@ -306,6 +312,9 @@ func (t Role) Has(k string) bool {
// roleGValSelector is field accessor for *types.Role
func roleGValSelector(res *types.Role, k string) (interface{}, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "roleID":
return res.ID, nil
@@ -330,6 +339,9 @@ func roleGValSelector(res *types.Role, k string) (interface{}, error) {
// roleTypedValueSelector is field accessor for *types.Role
func roleTypedValueSelector(res *types.Role, k string) (TypedValue, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "roleID":
return NewID(res.ID)
@@ -486,6 +498,9 @@ func (t Template) Has(k string) bool {
// templateGValSelector is field accessor for *types.Template
func templateGValSelector(res *types.Template, k string) (interface{}, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "templateID":
return res.ID, nil
@@ -520,6 +535,9 @@ func templateGValSelector(res *types.Template, k string) (interface{}, error) {
// templateTypedValueSelector is field accessor for *types.Template
func templateTypedValueSelector(res *types.Template, k string) (TypedValue, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "templateID":
return NewID(res.ID)
@@ -832,6 +850,9 @@ func (t User) Has(k string) bool {
// userGValSelector is field accessor for *types.User
func userGValSelector(res *types.User, k string) (interface{}, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "userID":
return res.ID, nil
@@ -862,6 +883,9 @@ func userGValSelector(res *types.User, k string) (interface{}, error) {
// userTypedValueSelector is field accessor for *types.User
func userTypedValueSelector(res *types.User, k string) (TypedValue, error) {
if res == nil {
return nil, nil
}
switch k {
case "ID", "userID":
return NewID(res.ID)