Pass ctx+store to validators, refactor record (script) triggering
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user