3
0

Pass ctx+store to validators, refactor record (script) triggering

This commit is contained in:
Denis Arh
2020-08-27 23:21:14 +02:00
parent 0c085d7b24
commit e8049fe7aa
5 changed files with 80 additions and 68 deletions

View File

@@ -5,6 +5,7 @@ import (
"encoding/csv"
"encoding/json"
"fmt"
"github.com/cortezaproject/corteza-server/store"
"net/http"
"path"
"strconv"
@@ -14,11 +15,8 @@ import (
"github.com/cortezaproject/corteza-server/compose/decoder"
"github.com/cortezaproject/corteza-server/compose/encoder"
"github.com/cortezaproject/corteza-server/compose/repository"
"github.com/cortezaproject/corteza-server/compose/rest/request"
"github.com/cortezaproject/corteza-server/compose/service"
"github.com/cortezaproject/corteza-server/compose/service/event"
"github.com/cortezaproject/corteza-server/compose/service/values"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/corredor"
"github.com/cortezaproject/corteza-server/pkg/mime"
@@ -127,7 +125,7 @@ func (ctrl *Record) Read(ctx context.Context, r *request.RecordRead) (interface{
// Temp workaround until we do proper by-module filtering for record findByID
if record != nil && record.ModuleID != r.ModuleID {
return nil, repository.ErrRecordNotFound
return nil, store.ErrNotFound
}
return ctrl.makePayload(ctx, m, record, err)
@@ -492,35 +490,8 @@ func (ctrl Record) Exec(ctx context.Context, r *request.RecordExec) (interface{}
return nil, nil
}
func (ctrl *Record) TriggerScript(ctx context.Context, r *request.RecordTriggerScript) (rsp interface{}, err error) {
var (
record *types.Record
oldRecord *types.Record
module *types.Module
namespace *types.Namespace
)
if oldRecord, err = ctrl.record.With(ctx).FindByID(r.NamespaceID, r.ModuleID, r.RecordID); err != nil {
return
}
if module, err = ctrl.module.With(ctx).FindByID(r.NamespaceID, r.ModuleID); err != nil {
return
}
if namespace, err = ctrl.namespace.With(ctx).FindByID(r.NamespaceID); err != nil {
return
}
record = oldRecord
record.Values = values.Sanitizer().Run(module, r.Values)
validated := values.Validator().Run(module, record)
err = corredor.Service().Exec(
ctx,
r.Script,
event.RecordOnManual(record, oldRecord, module, namespace, validated),
)
func (ctrl *Record) TriggerScript(ctx context.Context, r *request.RecordTriggerScript) (interface{}, error) {
module, record, err := ctrl.record.TriggerScript(ctx, r.NamespaceID, r.ModuleID, r.RecordID, r.Values, r.Script)
// Script can return modified record and we'll pass it on to the caller
return ctrl.makePayload(ctx, module, record, err)

View File

@@ -10,6 +10,7 @@ import (
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/actionlog"
"github.com/cortezaproject/corteza-server/pkg/auth"
"github.com/cortezaproject/corteza-server/pkg/corredor"
"github.com/cortezaproject/corteza-server/pkg/eventbus"
"github.com/cortezaproject/corteza-server/pkg/id"
"github.com/cortezaproject/corteza-server/store"
@@ -50,7 +51,7 @@ type (
}
recordValuesValidator interface {
Run(*types.Module, *types.Record) *types.RecordValueErrorSet
Run(context.Context, store.Storable, *types.Module, *types.Record) *types.RecordValueErrorSet
UniqueChecker(fn values.UniqueChecker)
RecordRefChecker(fn values.ReferenceChecker)
UserRefChecker(fn values.ReferenceChecker)
@@ -87,6 +88,8 @@ type (
Iterator(f types.RecordFilter, fn eventbus.HandlerFn, action string) (err error)
TriggerScript(ctx context.Context, namespaceID, moduleID, recordID uint64, rvs types.RecordValueSet, script string) (*types.Module, *types.Record, error)
EventEmitting(enable bool)
}
@@ -135,37 +138,35 @@ func (svc record) With(ctx context.Context) RecordService {
// Initialize validator and setup all checkers it needs
validator := values.Validator()
validator.UniqueChecker(func(v *types.RecordValue, f *types.ModuleField, m *types.Module) (uint64, error) {
validator.UniqueChecker(func(ctx context.Context, s store.Storable, v *types.RecordValue, f *types.ModuleField, m *types.Module) (uint64, error) {
if v.Ref == 0 {
return 0, nil
}
return store.ComposeRecordValueRefLookup(ctx, svc.store, m, f.Name, v.Ref)
return store.ComposeRecordValueRefLookup(ctx, s, m, f.Name, v.Ref)
})
validator.RecordRefChecker(func(v *types.RecordValue, f *types.ModuleField, m *types.Module) (bool, error) {
validator.RecordRefChecker(func(ctx context.Context, s store.Storable, v *types.RecordValue, f *types.ModuleField, m *types.Module) (bool, error) {
if v.Ref == 0 {
return false, nil
}
r, err := store.LookupComposeRecordByID(ctx, svc.store, m, v.Ref)
r, err := store.LookupComposeRecordByID(ctx, s, m, v.Ref)
return r != nil, err
})
validator.UserRefChecker(func(v *types.RecordValue, f *types.ModuleField, m *types.Module) (bool, error) {
r, err := svc.store.LookupUserByID(ctx, v.Ref)
validator.UserRefChecker(func(ctx context.Context, s store.Storable, v *types.RecordValue, f *types.ModuleField, m *types.Module) (bool, error) {
r, err := store.LookupUserByID(ctx, s, v.Ref)
return r != nil, err
})
validator.FileRefChecker(func(v *types.RecordValue, f *types.ModuleField, m *types.Module) (bool, error) {
validator.FileRefChecker(func(ctx context.Context, s store.Storable, v *types.RecordValue, f *types.ModuleField, m *types.Module) (bool, error) {
if v.Ref == 0 {
return false, nil
}
// @todo refactor this
panic("refactor this")
//r, err := repository.Attachment(ctx, nil).FindByID(m.NamespaceID, v.Ref)
//return r != nil, err
r, err := store.LookupComposeAttachmentByID(ctx, s, v.Ref)
return r != nil, err
})
return &record{
@@ -567,7 +568,7 @@ func (svc record) create(new *types.Record) (rec *types.Record, err error) {
if svc.optEmitEvents {
// Handle input payload
if rve = svc.procCreate(invokerID, m, new); !rve.IsValid() {
if rve = svc.procCreate(svc.ctx, svc.store, invokerID, m, new); !rve.IsValid() {
return nil, RecordErrValueInput().Wrap(rve)
}
@@ -583,7 +584,7 @@ func (svc record) create(new *types.Record) (rec *types.Record, err error) {
new.Values = svc.setDefaultValues(m, new.Values)
// Handle payload from automation scripts
if rve = svc.procCreate(invokerID, m, new); !rve.IsValid() {
if rve = svc.procCreate(svc.ctx, svc.store, invokerID, m, new); !rve.IsValid() {
return nil, RecordErrValueInput().Wrap(rve)
}
@@ -655,7 +656,7 @@ func (svc record) update(upd *types.Record) (rec *types.Record, err error) {
if svc.optEmitEvents {
// Handle input payload
if rve = svc.procUpdate(invokerID, m, upd, old); !rve.IsValid() {
if rve = svc.procUpdate(svc.ctx, svc.store, invokerID, m, upd, old); !rve.IsValid() {
return nil, RecordErrValueInput().Wrap(rve)
}
@@ -683,7 +684,7 @@ func (svc record) update(upd *types.Record) (rec *types.Record, err error) {
}
// Handle payload from automation scripts
if rve = svc.procUpdate(invokerID, m, upd, old); !rve.IsValid() {
if rve = svc.procUpdate(svc.ctx, svc.store, invokerID, m, upd, old); !rve.IsValid() {
return nil, RecordErrValueInput().Wrap(rve)
}
@@ -732,7 +733,7 @@ func (svc record) Create(new *types.Record) (rec *types.Record, err error) {
// of the creation procedure and after results are back from the automation scripts
//
// Both these points introduce external data that need to be checked fully in the same manner
func (svc record) procCreate(invokerID uint64, m *types.Module, new *types.Record) *types.RecordValueErrorSet {
func (svc record) procCreate(ctx context.Context, s store.Storable, invokerID uint64, m *types.Module, new *types.Record) *types.RecordValueErrorSet {
// Mark all values as updated (new)
new.Values.SetUpdatedFlag(true)
@@ -758,7 +759,7 @@ func (svc record) procCreate(invokerID uint64, m *types.Module, new *types.Recor
}
// Run validation of the updated records
return svc.validator.Run(m, new)
return svc.validator.Run(ctx, s, m, new)
}
func (svc record) Update(upd *types.Record) (rec *types.Record, err error) {
@@ -782,7 +783,7 @@ func (svc record) Update(upd *types.Record) (rec *types.Record, err error) {
// of the update procedure and after results are back from the automation scripts
//
// Both these points introduce external data that need to be checked fully in the same manner
func (svc record) procUpdate(invokerID uint64, m *types.Module, upd *types.Record, old *types.Record) *types.RecordValueErrorSet {
func (svc record) procUpdate(ctx context.Context, s store.Storable, invokerID uint64, m *types.Module, upd *types.Record, old *types.Record) *types.RecordValueErrorSet {
// Mark all values as updated (new)
upd.Values.SetUpdatedFlag(true)
@@ -821,7 +822,7 @@ func (svc record) procUpdate(invokerID uint64, m *types.Module, upd *types.Recor
}
// Run validation of the updated records
return svc.validator.Run(m, upd)
return svc.validator.Run(ctx, s, m, upd)
}
func (svc record) recordInfoUpdate(r *types.Record) {
@@ -1103,7 +1104,30 @@ func (svc record) Organize(namespaceID, moduleID, recordID uint64, posField, pos
}()
return svc.recordAction(svc.ctx, aProps, RecordActionOrganize, err)
}
// TriggerScript loads requested record sanitizes and validates values and passes all to the automation script
//
// For backward compatibility (of controllers), it returns module+record
func (svc record) TriggerScript(ctx context.Context, namespaceID, moduleID, recordID uint64, rvs types.RecordValueSet, script string) (*types.Module, *types.Record, error) {
var (
ns, m, r, err = loadRecordCombo(ctx, svc.store, namespaceID, moduleID, recordID)
)
if err != nil {
return nil, nil, err
}
original := r.Clone()
r.Values = values.Sanitizer().Run(m, rvs)
validated := values.Validator().Run(ctx, svc.store, m, r)
err = corredor.Service().Exec(ctx, script, event.RecordOnManual(r, original, m, ns, validated))
if err != nil {
return nil, nil, err
}
return m, r, nil
}
// Iterator loads and iterates through list of records
@@ -1213,7 +1237,7 @@ func (svc record) Iterator(f types.RecordFilter, fn eventbus.HandlerFn, action s
rec.Values = svc.setDefaultValues(m, rec.Values)
// Handle payload from automation scripts
if rve := svc.procCreate(invokerID, m, rec); !rve.IsValid() {
if rve := svc.procCreate(svc.ctx, svc.store, invokerID, m, rec); !rve.IsValid() {
return RecordErrValueInput().Wrap(rve)
}
@@ -1224,7 +1248,7 @@ func (svc record) Iterator(f types.RecordFilter, fn eventbus.HandlerFn, action s
recordableAction = RecordActionIteratorUpdate
// Handle input payload
if rve := svc.procUpdate(invokerID, m, rec, rec); !rve.IsValid() {
if rve := svc.procUpdate(svc.ctx, svc.store, invokerID, m, rec, rec); !rve.IsValid() {
return RecordErrValueInput().Wrap(rve)
}

View File

@@ -1,9 +1,11 @@
package values
import (
"context"
"errors"
"github.com/cortezaproject/corteza-server/compose/types"
"github.com/cortezaproject/corteza-server/pkg/slice"
"github.com/cortezaproject/corteza-server/store"
"math/big"
"net/mail"
"net/url"
@@ -19,8 +21,8 @@ import (
// is no need for such level of interaction and dynamic we require on the frontend
type (
UniqueChecker func(*types.RecordValue, *types.ModuleField, *types.Module) (uint64, error)
ReferenceChecker func(*types.RecordValue, *types.ModuleField, *types.Module) (bool, error)
UniqueChecker func(context.Context, store.Storable, *types.RecordValue, *types.ModuleField, *types.Module) (uint64, error)
ReferenceChecker func(context.Context, store.Storable, *types.RecordValue, *types.ModuleField, *types.Module) (bool, error)
validator struct {
uniqueCheckerFn UniqueChecker
@@ -91,7 +93,7 @@ func (vldtr *validator) FileRefChecker(fn ReferenceChecker) {
// - check for unique-multi-value in multi value fields
// - field-kind specific validation on all values
// - unique check on all all values
func (vldtr validator) Run(m *types.Module, r *types.Record) (out *types.RecordValueErrorSet) {
func (vldtr validator) Run(ctx context.Context, s store.Storable, m *types.Module, r *types.Record) (out *types.RecordValueErrorSet) {
var (
f *types.ModuleField
)
@@ -153,11 +155,11 @@ fields:
case "email":
out.Push(vldtr.vEmail(v, f, r, m)...)
case "file":
out.Push(vldtr.vFile(v, f, r, m)...)
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(v, f, r, m)...)
out.Push(vldtr.vRecord(ctx, s, v, f, r, m)...)
case "select":
out.Push(vldtr.vSelect(v, f, r, m)...)
//case "string":
@@ -165,7 +167,7 @@ fields:
case "url":
out.Push(vldtr.vUrl(v, f, r, m)...)
case "user":
out.Push(vldtr.vUser(v, f, r, m)...)
out.Push(vldtr.vUser(ctx, s, v, f, r, m)...)
}
}
@@ -181,7 +183,7 @@ fields:
continue
}
duplicateRecordID, err := vldtr.uniqueCheckerFn(v, f, m)
duplicateRecordID, err := vldtr.uniqueCheckerFn(ctx, s, v, f, m)
if err != nil {
out.Push(makeInternalErr(f, err))
} else if duplicateRecordID > 0 && duplicateRecordID != r.ID {
@@ -260,8 +262,8 @@ func (vldtr validator) vEmail(v *types.RecordValue, f *types.ModuleField, r *typ
return nil
}
func (vldtr validator) vFile(v *types.RecordValue, f *types.ModuleField, r *types.Record, m *types.Module) []types.RecordValueError {
if ok, err := vldtr.fileRefCheckerFn(v, f, m); err != nil {
func (vldtr validator) vFile(ctx context.Context, s store.Storable, v *types.RecordValue, f *types.ModuleField, r *types.Record, m *types.Module) []types.RecordValueError {
if ok, err := vldtr.fileRefCheckerFn(ctx, s, v, f, m); err != nil {
return e2s(makeInternalErr(f, err))
} else if !ok {
return e2s(makeInvalidRefErr(f, v.Ref))
@@ -282,8 +284,8 @@ func (vldtr validator) vNumber(v *types.RecordValue, f *types.ModuleField, r *ty
return nil
}
func (vldtr validator) vRecord(v *types.RecordValue, f *types.ModuleField, r *types.Record, m *types.Module) []types.RecordValueError {
if ok, err := vldtr.recordRefCheckerFn(v, f, m); err != nil {
func (vldtr validator) vRecord(ctx context.Context, s store.Storable, v *types.RecordValue, f *types.ModuleField, r *types.Record, m *types.Module) []types.RecordValueError {
if ok, err := vldtr.recordRefCheckerFn(ctx, s, v, f, m); err != nil {
return e2s(makeInternalErr(f, err))
} else if !ok {
return e2s(makeInvalidRefErr(f, v.Ref))
@@ -335,8 +337,8 @@ func (vldtr validator) vUrl(v *types.RecordValue, f *types.ModuleField, r *types
return nil
}
func (vldtr validator) vUser(v *types.RecordValue, f *types.ModuleField, r *types.Record, m *types.Module) []types.RecordValueError {
if ok, err := vldtr.userRefCheckerFn(v, f, m); err != nil {
func (vldtr validator) vUser(ctx context.Context, s store.Storable, v *types.RecordValue, f *types.ModuleField, r *types.Record, m *types.Module) []types.RecordValueError {
if ok, err := vldtr.userRefCheckerFn(ctx, s, v, f, m); err != nil {
return e2s(makeInternalErr(f, err))
} else if !ok {
return e2s(makeInvalidRefErr(f, v.Ref))

View File

@@ -95,6 +95,12 @@ loop:
return
}
func (r Record) Clone() *Record {
c := &r
c.Values = r.Values.Clone()
return c
}
// Resource returns a system resource ID for this type
func (r Record) PermissionResource() permissions.Resource {
return ModulePermissionResource.AppendID(r.ModuleID)

View File

@@ -49,6 +49,15 @@ func (v RecordValue) Clone() *RecordValue {
}
}
func (set RecordValueSet) Clone() (vv RecordValueSet) {
vv = make(RecordValueSet, len(vv))
for i := range set {
vv[i] = set[i].Clone()
}
return
}
func (set RecordValueSet) FilterByName(name string) (vv RecordValueSet) {
for i := range set {
if set[i].Name == name {