diff --git a/compose/service/values/validator.go b/compose/service/values/validator.go index e6787b307..a6cb15d0b 100644 --- a/compose/service/values/validator.go +++ b/compose/service/values/validator.go @@ -96,6 +96,9 @@ func (vldtr *validator) FileRefChecker(fn ReferenceChecker) { func (vldtr validator) Run(ctx context.Context, s store.Storer, m *types.Module, r *types.Record) (out *types.RecordValueErrorSet) { var ( f *types.ModuleField + + valParser = parser() + valDict = r.Values.Dict(m.Fields) ) out = &types.RecordValueErrorSet{} @@ -132,7 +135,7 @@ fields: } for _, v := range r.Values { - if !v.IsUpdated() || v.IsUpdated() { + if !v.IsUpdated() || v.IsDeleted() { // We'll validate only updated (and non-deleted) values continue } @@ -141,33 +144,67 @@ fields: continue } - if v.Value == "" { - // Nothing to do with empty value - return nil + if !(f.Expressions.DisableDefaultValidators && len(f.Expressions.Validators) > 0) { + if v.Value == "" { + // Nothing to do with empty value + return nil + } + + // Per field type validators + switch strings.ToLower(f.Kind) { + case "bool": + out.Push(vldtr.vBool(v, f, r, m)...) + case "datetime": + out.Push(vldtr.vDatetime(v, f, r, m)...) + case "email": + out.Push(vldtr.vEmail(v, f, r, m)...) + case "file": + out.Push(vldtr.vFile(ctx, s, v, f, r, m)...) + case "number": + out.Push(vldtr.vNumber(v, f, r, m)...) + case "record": + out.Push(vldtr.vRecord(ctx, s, v, f, r, m)...) + case "select": + out.Push(vldtr.vSelect(v, f, r, m)...) + //case "string": + // out.Push(vldtr.vString(v, f, r, m)...) + case "url": + out.Push(vldtr.vUrl(v, f, r, m)...) + case "user": + out.Push(vldtr.vUser(ctx, s, v, f, r, m)...) + } } - // Per field type validators - switch strings.ToLower(f.Kind) { - case "bool": - out.Push(vldtr.vBool(v, f, r, m)...) - case "datetime": - out.Push(vldtr.vDatetime(v, f, r, m)...) - case "email": - out.Push(vldtr.vEmail(v, f, r, m)...) - case "file": - out.Push(vldtr.vFile(ctx, s, v, f, r, m)...) - case "number": - out.Push(vldtr.vNumber(v, f, r, m)...) - case "record": - out.Push(vldtr.vRecord(ctx, s, v, f, r, m)...) - case "select": - out.Push(vldtr.vSelect(v, f, r, m)...) - //case "string": - // out.Push(vldtr.vString(v, f, r, m)...) - case "url": - out.Push(vldtr.vUrl(v, f, r, m)...) - case "user": - out.Push(vldtr.vUser(ctx, s, v, f, r, m)...) + if len(f.Expressions.Validators) > 0 { + for _, cv := range f.Expressions.Validators { + eval, err := valParser.NewEvaluable(cv.Test) + if err != nil { + out.Push(makeInternalErr(f, err)) + break + } + + invalid, err := eval.EvalBool(ctx, map[string]interface{}{ + "value": v.Value, + "oldValue": v.OldValue, + "values": valDict, + }) + + if err != nil { + out.Push(makeInternalErr(f, err)) + break + } + + if invalid { + out.Push(types.RecordValueError{ + Kind: "error", + Message: cv.Error, + Meta: map[string]interface{}{"field": f.Name}}, + ) + + // break at first failed test + break + } + } } } diff --git a/compose/service/values/validator_test.go b/compose/service/values/validator_test.go index b3c1bdae0..90becc78d 100644 --- a/compose/service/values/validator_test.go +++ b/compose/service/values/validator_test.go @@ -1,7 +1,9 @@ package values import ( + "context" "github.com/cortezaproject/corteza-server/compose/types" + "github.com/stretchr/testify/require" "reflect" "testing" "time" @@ -218,3 +220,25 @@ func Test_validator_vSelect(t *testing.T) { }) } } + +func Test_validator_customExpr(t *testing.T) { + var ( + vldtr = validator{} + m = &types.Module{} + f = &types.ModuleField{Name: "num", Kind: "Number"} + r = &types.Record{} + ) + + f.Expressions.Validators = []types.ModuleFieldValidator{ + {Test: "value > 5", Error: "value is lower than 5"}, + } + m.Fields = append(m.Fields, f) + r.Values = r.Values.Replace("num", "1") + + rve := vldtr.Run(context.Background(), nil, m, r) + require.False(t, rve.IsValid()) + + r.Values = r.Values.Replace("num", "10") + rve = vldtr.Run(context.Background(), nil, m, r) + require.True(t, rve.IsValid()) +} diff --git a/compose/types/module_field.go b/compose/types/module_field.go index 76fdd6cc4..316b397dd 100644 --- a/compose/types/module_field.go +++ b/compose/types/module_field.go @@ -45,14 +45,18 @@ type ( ModuleFieldExpr struct { Value string `json:"value,omitempty"` Sanitizers []string `json:"sanitizers,omitempty"` - //Validators []struct { - // Test string `json:"test,omitempty"` - // Error string `json:"error,omitempty"` - //} `json:"validators,omitempty"` + + Validators []ModuleFieldValidator `json:"validators,omitempty"` + DisableDefaultValidators bool `json:"disableDefaultValidators,omitempty"` Formatters []string `json:"formatters,omitempty"` DisableDefaultFormatters bool `json:"disableDefaultFormatters,omitempty"` } + + ModuleFieldValidator struct { + Test string `json:"test,omitempty"` + Error string `json:"error,omitempty"` + } ) var ( diff --git a/compose/types/record_value.go b/compose/types/record_value.go index 90b0011bc..c003e3afc 100644 --- a/compose/types/record_value.go +++ b/compose/types/record_value.go @@ -96,9 +96,10 @@ func (set RecordValueSet) Replace(name string, values ...string) (vv RecordValue for p, v := range values { vv = append(vv, &RecordValue{ - Name: name, - Value: v, - Place: uint(p), + Name: name, + Value: v, + Place: uint(p), + Updated: true, }) }