Add basic automation functions for compose
This commit is contained in:
@@ -0,0 +1,605 @@
|
||||
package automation
|
||||
|
||||
// This file is auto-generated.
|
||||
//
|
||||
// Changes to this file may cause incorrect behavior and will be lost if
|
||||
// the code is regenerated.
|
||||
//
|
||||
// Definitions file that controls how this file is generated:
|
||||
// compose/automation/expr_types.yaml
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
. "github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
)
|
||||
|
||||
var _ = context.Background
|
||||
var _ = fmt.Errorf
|
||||
|
||||
// ComposeModule is an expression type, wrapper for *types.Module type
|
||||
type ComposeModule struct{ value *types.Module }
|
||||
|
||||
// NewComposeModule creates new instance of ComposeModule expression type
|
||||
func NewComposeModule(val interface{}) (*ComposeModule, error) {
|
||||
if c, err := CastToComposeModule(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create ComposeModule: %w", err)
|
||||
} else {
|
||||
return &ComposeModule{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return underlying value on ComposeModule
|
||||
func (t ComposeModule) Get() interface{} { return t.value }
|
||||
|
||||
// Return type name
|
||||
func (ComposeModule) Type() string { return "ComposeModule" }
|
||||
|
||||
// Convert value to *types.Module
|
||||
func (ComposeModule) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewComposeModule(val)
|
||||
}
|
||||
|
||||
// Assign new value to ComposeModule
|
||||
//
|
||||
// value is first passed through CastToComposeModule
|
||||
func (t *ComposeModule) Assign(val interface{}) error {
|
||||
if c, err := CastToComposeModule(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ComposeModule) AssignFieldValue(key string, val interface{}) error {
|
||||
return assignToComposeModule(t.value, key, val)
|
||||
}
|
||||
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
//
|
||||
// It allows gval lib to access ComposeModule's underlying value (*types.Module)
|
||||
// and it's fields
|
||||
//
|
||||
func (t ComposeModule) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
return composeModuleGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.Module
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t ComposeModule) Select(k string) (TypedValue, error) {
|
||||
return composeModuleTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t ComposeModule) Has(k string) bool {
|
||||
switch k {
|
||||
case "ID":
|
||||
return true
|
||||
case "namespaceID":
|
||||
return true
|
||||
case "name":
|
||||
return true
|
||||
case "handle":
|
||||
return true
|
||||
case "labels":
|
||||
return true
|
||||
case "createdAt":
|
||||
return true
|
||||
case "updatedAt":
|
||||
return true
|
||||
case "deletedAt":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// composeModuleGValSelector is field accessor for *types.Module
|
||||
func composeModuleGValSelector(res *types.Module, k string) (interface{}, error) {
|
||||
switch k {
|
||||
case "ID":
|
||||
return res.ID, nil
|
||||
case "namespaceID":
|
||||
return res.NamespaceID, nil
|
||||
case "name":
|
||||
return res.Name, nil
|
||||
case "handle":
|
||||
return res.Handle, nil
|
||||
case "labels":
|
||||
return res.Labels, nil
|
||||
case "createdAt":
|
||||
return res.CreatedAt, nil
|
||||
case "updatedAt":
|
||||
return res.UpdatedAt, nil
|
||||
case "deletedAt":
|
||||
return res.DeletedAt, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// composeModuleTypedValueSelector is field accessor for *types.Module
|
||||
func composeModuleTypedValueSelector(res *types.Module, k string) (TypedValue, error) {
|
||||
switch k {
|
||||
case "ID":
|
||||
return NewID(res.ID)
|
||||
case "namespaceID":
|
||||
return NewID(res.NamespaceID)
|
||||
case "name":
|
||||
return NewString(res.Name)
|
||||
case "handle":
|
||||
return NewHandle(res.Handle)
|
||||
case "labels":
|
||||
return NewKV(res.Labels)
|
||||
case "createdAt":
|
||||
return NewDateTime(res.CreatedAt)
|
||||
case "updatedAt":
|
||||
return NewDateTime(res.UpdatedAt)
|
||||
case "deletedAt":
|
||||
return NewDateTime(res.DeletedAt)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// assignToComposeModule is field value setter for *types.Module
|
||||
func assignToComposeModule(res *types.Module, k string, val interface{}) error {
|
||||
switch k {
|
||||
case "ID":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "namespaceID":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "name":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Name = aux
|
||||
return nil
|
||||
case "handle":
|
||||
aux, err := CastToHandle(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Handle = aux
|
||||
return nil
|
||||
case "labels":
|
||||
aux, err := CastToKV(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Labels = aux
|
||||
return nil
|
||||
case "createdAt":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "updatedAt":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "deletedAt":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// ComposeNamespace is an expression type, wrapper for *types.Namespace type
|
||||
type ComposeNamespace struct{ value *types.Namespace }
|
||||
|
||||
// NewComposeNamespace creates new instance of ComposeNamespace expression type
|
||||
func NewComposeNamespace(val interface{}) (*ComposeNamespace, error) {
|
||||
if c, err := CastToComposeNamespace(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create ComposeNamespace: %w", err)
|
||||
} else {
|
||||
return &ComposeNamespace{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return underlying value on ComposeNamespace
|
||||
func (t ComposeNamespace) Get() interface{} { return t.value }
|
||||
|
||||
// Return type name
|
||||
func (ComposeNamespace) Type() string { return "ComposeNamespace" }
|
||||
|
||||
// Convert value to *types.Namespace
|
||||
func (ComposeNamespace) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewComposeNamespace(val)
|
||||
}
|
||||
|
||||
// Assign new value to ComposeNamespace
|
||||
//
|
||||
// value is first passed through CastToComposeNamespace
|
||||
func (t *ComposeNamespace) Assign(val interface{}) error {
|
||||
if c, err := CastToComposeNamespace(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ComposeNamespace) AssignFieldValue(key string, val interface{}) error {
|
||||
return assignToComposeNamespace(t.value, key, val)
|
||||
}
|
||||
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
//
|
||||
// It allows gval lib to access ComposeNamespace's underlying value (*types.Namespace)
|
||||
// and it's fields
|
||||
//
|
||||
func (t ComposeNamespace) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
return composeNamespaceGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.Namespace
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t ComposeNamespace) Select(k string) (TypedValue, error) {
|
||||
return composeNamespaceTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t ComposeNamespace) Has(k string) bool {
|
||||
switch k {
|
||||
case "ID":
|
||||
return true
|
||||
case "name":
|
||||
return true
|
||||
case "slug", "handle":
|
||||
return true
|
||||
case "labels":
|
||||
return true
|
||||
case "createdAt":
|
||||
return true
|
||||
case "updatedAt":
|
||||
return true
|
||||
case "deletedAt":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// composeNamespaceGValSelector is field accessor for *types.Namespace
|
||||
func composeNamespaceGValSelector(res *types.Namespace, k string) (interface{}, error) {
|
||||
switch k {
|
||||
case "ID":
|
||||
return res.ID, nil
|
||||
case "name":
|
||||
return res.Name, nil
|
||||
case "slug", "handle":
|
||||
return res.Slug, nil
|
||||
case "labels":
|
||||
return res.Labels, nil
|
||||
case "createdAt":
|
||||
return res.CreatedAt, nil
|
||||
case "updatedAt":
|
||||
return res.UpdatedAt, nil
|
||||
case "deletedAt":
|
||||
return res.DeletedAt, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// composeNamespaceTypedValueSelector is field accessor for *types.Namespace
|
||||
func composeNamespaceTypedValueSelector(res *types.Namespace, k string) (TypedValue, error) {
|
||||
switch k {
|
||||
case "ID":
|
||||
return NewID(res.ID)
|
||||
case "name":
|
||||
return NewString(res.Name)
|
||||
case "slug", "handle":
|
||||
return NewHandle(res.Slug)
|
||||
case "labels":
|
||||
return NewKV(res.Labels)
|
||||
case "createdAt":
|
||||
return NewDateTime(res.CreatedAt)
|
||||
case "updatedAt":
|
||||
return NewDateTime(res.UpdatedAt)
|
||||
case "deletedAt":
|
||||
return NewDateTime(res.DeletedAt)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// assignToComposeNamespace is field value setter for *types.Namespace
|
||||
func assignToComposeNamespace(res *types.Namespace, k string, val interface{}) error {
|
||||
switch k {
|
||||
case "ID":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "name":
|
||||
aux, err := CastToString(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Name = aux
|
||||
return nil
|
||||
case "slug", "handle":
|
||||
aux, err := CastToHandle(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Slug = aux
|
||||
return nil
|
||||
case "labels":
|
||||
aux, err := CastToKV(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Labels = aux
|
||||
return nil
|
||||
case "createdAt":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "updatedAt":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "deletedAt":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// ComposeRecord is an expression type, wrapper for *types.Record type
|
||||
type ComposeRecord struct{ value *types.Record }
|
||||
|
||||
// NewComposeRecord creates new instance of ComposeRecord expression type
|
||||
func NewComposeRecord(val interface{}) (*ComposeRecord, error) {
|
||||
if c, err := CastToComposeRecord(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create ComposeRecord: %w", err)
|
||||
} else {
|
||||
return &ComposeRecord{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return underlying value on ComposeRecord
|
||||
func (t ComposeRecord) Get() interface{} { return t.value }
|
||||
|
||||
// Return type name
|
||||
func (ComposeRecord) Type() string { return "ComposeRecord" }
|
||||
|
||||
// Convert value to *types.Record
|
||||
func (ComposeRecord) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewComposeRecord(val)
|
||||
}
|
||||
|
||||
// Assign new value to ComposeRecord
|
||||
//
|
||||
// value is first passed through CastToComposeRecord
|
||||
func (t *ComposeRecord) Assign(val interface{}) error {
|
||||
if c, err := CastToComposeRecord(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ComposeRecord) AssignFieldValue(key string, val interface{}) error {
|
||||
return assignToComposeRecord(t.value, key, val)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.Record
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t ComposeRecord) Select(k string) (TypedValue, error) {
|
||||
return composeRecordTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t ComposeRecord) Has(k string) bool {
|
||||
switch k {
|
||||
case "ID":
|
||||
return true
|
||||
case "moduleID":
|
||||
return true
|
||||
case "namespaceID":
|
||||
return true
|
||||
case "values":
|
||||
return true
|
||||
case "labels":
|
||||
return true
|
||||
case "ownedBy":
|
||||
return true
|
||||
case "createdAt":
|
||||
return true
|
||||
case "createdBy":
|
||||
return true
|
||||
case "updatedAt":
|
||||
return true
|
||||
case "updatedBy":
|
||||
return true
|
||||
case "deletedAt":
|
||||
return true
|
||||
case "deletedBy":
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// composeRecordGValSelector is field accessor for *types.Record
|
||||
func composeRecordGValSelector(res *types.Record, k string) (interface{}, error) {
|
||||
switch k {
|
||||
case "ID":
|
||||
return res.ID, nil
|
||||
case "moduleID":
|
||||
return res.ModuleID, nil
|
||||
case "namespaceID":
|
||||
return res.NamespaceID, nil
|
||||
case "values":
|
||||
return res.Values, nil
|
||||
case "labels":
|
||||
return res.Labels, nil
|
||||
case "ownedBy":
|
||||
return res.OwnedBy, nil
|
||||
case "createdAt":
|
||||
return res.CreatedAt, nil
|
||||
case "createdBy":
|
||||
return res.CreatedBy, nil
|
||||
case "updatedAt":
|
||||
return res.UpdatedAt, nil
|
||||
case "updatedBy":
|
||||
return res.UpdatedBy, nil
|
||||
case "deletedAt":
|
||||
return res.DeletedAt, nil
|
||||
case "deletedBy":
|
||||
return res.DeletedBy, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// composeRecordTypedValueSelector is field accessor for *types.Record
|
||||
func composeRecordTypedValueSelector(res *types.Record, k string) (TypedValue, error) {
|
||||
switch k {
|
||||
case "ID":
|
||||
return NewID(res.ID)
|
||||
case "moduleID":
|
||||
return NewID(res.ModuleID)
|
||||
case "namespaceID":
|
||||
return NewID(res.NamespaceID)
|
||||
case "values":
|
||||
return NewComposeRecordValues(res.Values)
|
||||
case "labels":
|
||||
return NewKV(res.Labels)
|
||||
case "ownedBy":
|
||||
return NewID(res.OwnedBy)
|
||||
case "createdAt":
|
||||
return NewDateTime(res.CreatedAt)
|
||||
case "createdBy":
|
||||
return NewID(res.CreatedBy)
|
||||
case "updatedAt":
|
||||
return NewDateTime(res.UpdatedAt)
|
||||
case "updatedBy":
|
||||
return NewID(res.UpdatedBy)
|
||||
case "deletedAt":
|
||||
return NewDateTime(res.DeletedAt)
|
||||
case "deletedBy":
|
||||
return NewID(res.DeletedBy)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// assignToComposeRecord is field value setter for *types.Record
|
||||
func assignToComposeRecord(res *types.Record, k string, val interface{}) error {
|
||||
switch k {
|
||||
case "ID":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "moduleID":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "namespaceID":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "values":
|
||||
aux, err := CastToComposeRecordValues(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Values = aux
|
||||
return nil
|
||||
case "labels":
|
||||
aux, err := CastToKV(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.Labels = aux
|
||||
return nil
|
||||
case "ownedBy":
|
||||
aux, err := CastToID(val)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res.OwnedBy = aux
|
||||
return nil
|
||||
case "createdAt":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "createdBy":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "updatedAt":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "updatedBy":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "deletedAt":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
case "deletedBy":
|
||||
return fmt.Errorf("field '%s' is read-only", k)
|
||||
}
|
||||
|
||||
return fmt.Errorf("unknown field '%s'", k)
|
||||
}
|
||||
|
||||
// ComposeRecordValueErrorSet is an expression type, wrapper for *types.RecordValueErrorSet type
|
||||
type ComposeRecordValueErrorSet struct{ value *types.RecordValueErrorSet }
|
||||
|
||||
// NewComposeRecordValueErrorSet creates new instance of ComposeRecordValueErrorSet expression type
|
||||
func NewComposeRecordValueErrorSet(val interface{}) (*ComposeRecordValueErrorSet, error) {
|
||||
if c, err := CastToComposeRecordValueErrorSet(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create ComposeRecordValueErrorSet: %w", err)
|
||||
} else {
|
||||
return &ComposeRecordValueErrorSet{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return underlying value on ComposeRecordValueErrorSet
|
||||
func (t ComposeRecordValueErrorSet) Get() interface{} { return t.value }
|
||||
|
||||
// Return type name
|
||||
func (ComposeRecordValueErrorSet) Type() string { return "ComposeRecordValueErrorSet" }
|
||||
|
||||
// Convert value to *types.RecordValueErrorSet
|
||||
func (ComposeRecordValueErrorSet) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewComposeRecordValueErrorSet(val)
|
||||
}
|
||||
|
||||
// Assign new value to ComposeRecordValueErrorSet
|
||||
//
|
||||
// value is first passed through CastToComposeRecordValueErrorSet
|
||||
func (t *ComposeRecordValueErrorSet) Assign(val interface{}) error {
|
||||
if c, err := CastToComposeRecordValueErrorSet(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// ComposeRecordValues is an expression type, wrapper for types.RecordValueSet type
|
||||
type ComposeRecordValues struct{ value types.RecordValueSet }
|
||||
|
||||
// NewComposeRecordValues creates new instance of ComposeRecordValues expression type
|
||||
func NewComposeRecordValues(val interface{}) (*ComposeRecordValues, error) {
|
||||
if c, err := CastToComposeRecordValues(val); err != nil {
|
||||
return nil, fmt.Errorf("unable to create ComposeRecordValues: %w", err)
|
||||
} else {
|
||||
return &ComposeRecordValues{value: c}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Return underlying value on ComposeRecordValues
|
||||
func (t ComposeRecordValues) Get() interface{} { return t.value }
|
||||
|
||||
// Return type name
|
||||
func (ComposeRecordValues) Type() string { return "ComposeRecordValues" }
|
||||
|
||||
// Convert value to types.RecordValueSet
|
||||
func (ComposeRecordValues) Cast(val interface{}) (TypedValue, error) {
|
||||
return NewComposeRecordValues(val)
|
||||
}
|
||||
|
||||
// Assign new value to ComposeRecordValues
|
||||
//
|
||||
// value is first passed through CastToComposeRecordValues
|
||||
func (t *ComposeRecordValues) Assign(val interface{}) error {
|
||||
if c, err := CastToComposeRecordValues(val); err != nil {
|
||||
return err
|
||||
} else {
|
||||
t.value = c
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/spf13/cast"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func CastToComposeNamespace(val interface{}) (out *types.Namespace, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &types.Namespace{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToComposeNamespace(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.Namespace:
|
||||
return val, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToComposeModule(val interface{}) (out *types.Module, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &types.Module{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToComposeModule(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.Module:
|
||||
return val, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func CastToComposeRecord(val interface{}) (out *types.Record, err error) {
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
out = &types.Record{}
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
return assignToComposeRecord(out, k, v)
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.Record:
|
||||
return val, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
//
|
||||
// It allows gval lib to access Record's underlying value (*types.Record)
|
||||
// and it's fields
|
||||
//
|
||||
func (t ComposeRecord) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
if k == "values" {
|
||||
return t.value.Values.Dict(t.value.GetModule().Fields), nil
|
||||
}
|
||||
|
||||
return composeRecordGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
func CastToComposeRecordValues(val interface{}) (out types.RecordValueSet, err error) {
|
||||
out = types.RecordValueSet{}
|
||||
|
||||
switch val := val.(type) {
|
||||
case expr.Iterator:
|
||||
return out, val.Each(func(k string, v expr.TypedValue) error {
|
||||
// try with slice of strings first:
|
||||
if ss, err := cast.ToStringSliceE(expr.UntypedValue(v)); err == nil {
|
||||
for i, v := range ss {
|
||||
out = out.Set(&types.RecordValue{Name: k, Value: v, Place: uint(i)})
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if str, err := expr.CastToString(v); err != nil {
|
||||
return err
|
||||
} else {
|
||||
out = out.Set(&types.RecordValue{Name: k, Value: str})
|
||||
return nil
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.Record:
|
||||
return val.Values, nil
|
||||
case types.RecordValueSet:
|
||||
return val, nil
|
||||
case map[string]string:
|
||||
out = types.RecordValueSet{}
|
||||
for k, v := range val {
|
||||
out = out.Set(&types.RecordValue{Name: k, Value: v})
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
case map[string][]string:
|
||||
out = types.RecordValueSet{}
|
||||
for k, vv := range val {
|
||||
for i, v := range vv {
|
||||
out = out.Set(&types.RecordValue{Name: k, Value: v, Place: uint(i)})
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ComposeRecordValues) AssignFieldValue(pp []string, val interface{}) error {
|
||||
return assignToComposeRecordValues(&t.value, pp, val)
|
||||
}
|
||||
|
||||
// SelectGVal implements gval.Selector requirements
|
||||
//
|
||||
// It allows gval lib to access Record's underlying value (*types.RecordValues)
|
||||
// and it's fields
|
||||
//
|
||||
func (t ComposeRecordValues) SelectGVal(ctx context.Context, k string) (interface{}, error) {
|
||||
return composeRecordValuesGValSelector(t.value, k)
|
||||
}
|
||||
|
||||
// Select is field accessor for *types.Record
|
||||
//
|
||||
// Similar to SelectGVal but returns typed values
|
||||
func (t ComposeRecordValues) Select(k string) (expr.TypedValue, error) {
|
||||
return composeRecordValuesTypedValueSelector(t.value, k)
|
||||
}
|
||||
|
||||
func (t ComposeRecordValues) Has(k string) bool {
|
||||
return t.value.Get(k, 0) != nil
|
||||
}
|
||||
|
||||
// recordGValSelector is field accessor for *types.RecordValueSet
|
||||
func composeRecordValuesGValSelector(res types.RecordValueSet, k string) (interface{}, error) {
|
||||
vv := res.FilterByName(k)
|
||||
|
||||
switch len(vv) {
|
||||
case 0:
|
||||
return nil, nil
|
||||
case 1:
|
||||
return vv[0].Value, nil
|
||||
default:
|
||||
out := make([]string, 0, len(vv))
|
||||
return out, vv.Walk(func(v *types.RecordValue) error {
|
||||
out = append(out, v.Value)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// recordValuesTypedValueSelector is field accessor for *types.RecordValueSet
|
||||
//
|
||||
// @todo return appropriate types (atm all values are returned as String)
|
||||
func composeRecordValuesTypedValueSelector(res types.RecordValueSet, k string) (expr.TypedValue, error) {
|
||||
vv := res.FilterByName(k)
|
||||
|
||||
switch {
|
||||
case len(vv) == 0:
|
||||
return nil, nil
|
||||
case len(vv) == 1:
|
||||
return expr.NewString(vv[0].Value)
|
||||
default:
|
||||
mval := make([]expr.TypedValue, 0, len(vv))
|
||||
_ = vv.Walk(func(v *types.RecordValue) error {
|
||||
mval = append(mval, expr.Must(expr.NewString(v.Value)))
|
||||
return nil
|
||||
})
|
||||
|
||||
return expr.NewArray(mval)
|
||||
}
|
||||
}
|
||||
|
||||
// assignToRecordValuesSet is field value setter for *types.Record
|
||||
func assignToComposeRecordValues(res *types.RecordValueSet, pp []string, val interface{}) (err error) {
|
||||
if len(pp) < 1 {
|
||||
return fmt.Errorf("empty path used for assigning record values")
|
||||
}
|
||||
|
||||
k := pp[0]
|
||||
rv := &types.RecordValue{Name: k}
|
||||
if rv.Value, err = cast.ToStringE(expr.UntypedValue(val)); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if len(pp) == 2 {
|
||||
if rv.Place, err = cast.ToUintE(expr.UntypedValue(pp[1])); err != nil {
|
||||
return fmt.Errorf("failed to decode record value place from '%s': %w", strings.Join(pp, "."), err)
|
||||
}
|
||||
}
|
||||
|
||||
*res = res.Set(rv)
|
||||
//return fmt.Errorf("unknown field '%s'", k)
|
||||
return nil
|
||||
}
|
||||
|
||||
func CastToComposeRecordValueErrorSet(val interface{}) (out *types.RecordValueErrorSet, err error) {
|
||||
switch val := expr.UntypedValue(val).(type) {
|
||||
case *types.RecordValueErrorSet:
|
||||
return val, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unable to cast type %T to %T", val, out)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package: automation
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza-server/compose/types
|
||||
|
||||
types:
|
||||
ComposeNamespace:
|
||||
as: '*types.Namespace'
|
||||
struct:
|
||||
- { name: 'ID', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'name', exprType: 'String', goType: 'string' }
|
||||
- { name: 'slug', exprType: 'Handle', goType: 'string', alias: 'handle'}
|
||||
- { name: 'labels', exprType: 'KV', goType: 'map[string]string' }
|
||||
- { name: 'createdAt', exprType: 'DateTime', goType: 'time.Time', mode: ro }
|
||||
- { name: 'updatedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'deletedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
ComposeModule:
|
||||
as: '*types.Module'
|
||||
struct:
|
||||
- { name: 'ID', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'namespaceID', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'name', exprType: 'String', goType: 'string' }
|
||||
- { name: 'handle', exprType: 'Handle', goType: 'string' }
|
||||
- { name: 'labels', exprType: 'KV', goType: 'map[string]string' }
|
||||
- { name: 'createdAt', exprType: 'DateTime', goType: 'time.Time', mode: ro }
|
||||
- { name: 'updatedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'deletedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
|
||||
ComposeRecord:
|
||||
as: '*types.Record'
|
||||
struct:
|
||||
- { name: 'ID', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'moduleID', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'namespaceID', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'values', exprType: 'ComposeRecordValues', goType: 'map[string]interface{}' }
|
||||
- { name: 'labels', exprType: 'KV', goType: 'map[string]string' }
|
||||
- { name: 'ownedBy', exprType: 'ID', goType: 'uint64' }
|
||||
- { name: 'createdAt', exprType: 'DateTime', goType: 'time.Time', mode: ro }
|
||||
- { name: 'createdBy', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'updatedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'updatedBy', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
- { name: 'deletedAt', exprType: 'DateTime', goType: '*time.Time', mode: ro }
|
||||
- { name: 'deletedBy', exprType: 'ID', goType: 'uint64', mode: ro }
|
||||
customGValSelector: true
|
||||
|
||||
ComposeRecordValues:
|
||||
as: 'types.RecordValueSet'
|
||||
castFn: 'castComposeRecordValueSet'
|
||||
|
||||
ComposeRecordValueErrorSet:
|
||||
as: '*types.RecordValueErrorSet'
|
||||
|
||||
#
|
||||
# Page:
|
||||
# as: '*types.Page'
|
||||
# Chart:
|
||||
# as: '*types.Chart'
|
||||
@@ -0,0 +1,135 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/stretchr/testify/require"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSetRecordValuesWithPath(t *testing.T) {
|
||||
t.Run("some basic aerobics", func(t *testing.T) {
|
||||
|
||||
var (
|
||||
r = require.New(t)
|
||||
rvs = &RecordValues{types.RecordValueSet{}}
|
||||
)
|
||||
|
||||
r.NoError(expr.Assign(rvs, "field1", "a"))
|
||||
r.NoError(expr.Assign(rvs, "field1.1", "a"))
|
||||
r.True(rvs.value.Has("field1", 0))
|
||||
r.True(rvs.value.Has("field1", 1))
|
||||
})
|
||||
|
||||
t.Run("cast string map", func(t *testing.T) {
|
||||
|
||||
var (
|
||||
r = require.New(t)
|
||||
rvs, err = CastToRecordValues(map[string]string{"field2": "b"})
|
||||
)
|
||||
|
||||
r.NoError(err)
|
||||
r.True(rvs.Has("field2", 0))
|
||||
})
|
||||
|
||||
t.Run("cast string slice map", func(t *testing.T) {
|
||||
|
||||
var (
|
||||
r = require.New(t)
|
||||
rvs, err = CastToRecordValues(map[string][]string{"field2": []string{"a", "b"}})
|
||||
)
|
||||
|
||||
r.NoError(err)
|
||||
r.True(rvs.Has("field2", 0))
|
||||
r.True(rvs.Has("field2", 1))
|
||||
})
|
||||
}
|
||||
|
||||
func TestRecordFieldValuesAccess(t *testing.T) {
|
||||
var (
|
||||
err error
|
||||
v expr.TypedValue
|
||||
|
||||
mod = &types.Module{Fields: types.ModuleFieldSet{
|
||||
&types.ModuleField{Name: "s1", Multi: false},
|
||||
&types.ModuleField{Name: "m1", Multi: true},
|
||||
&types.ModuleField{Name: "m2", Multi: true},
|
||||
&types.ModuleField{Name: "s2", Multi: false},
|
||||
}}
|
||||
|
||||
raw = &types.Record{Values: types.RecordValueSet{
|
||||
&types.RecordValue{Name: "s1", Value: "sVal1"},
|
||||
&types.RecordValue{Name: "m1", Value: "mVal1.0"},
|
||||
&types.RecordValue{Name: "m1", Value: "mVal1.1", Place: 1},
|
||||
&types.RecordValue{Name: "m1", Value: "mVal1.2", Place: 2},
|
||||
&types.RecordValue{Name: "m2", Value: "mVal2.0"},
|
||||
}}
|
||||
|
||||
tval = &Record{value: raw}
|
||||
scope = expr.RVars{"rec": tval}.Vars()
|
||||
)
|
||||
|
||||
// @todo see not above re. back-ref to record
|
||||
raw.SetModule(mod)
|
||||
|
||||
t.Run("via typed value", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
)
|
||||
|
||||
v, err = expr.Select(scope, "rec.values.s1")
|
||||
req.NoError(err)
|
||||
req.NotEmpty(v)
|
||||
req.Equal("sVal1", v.Get())
|
||||
|
||||
v, err = expr.Select(scope, "rec.values.m1.0")
|
||||
req.NoError(err)
|
||||
req.NotEmpty(v)
|
||||
req.Equal("mVal1.0", v.Get())
|
||||
|
||||
v, err = expr.Select(scope, "rec.values.m1.1")
|
||||
req.NoError(err)
|
||||
req.NotEmpty(v)
|
||||
req.Equal("mVal1.1", v.Get())
|
||||
|
||||
// @todo when RecordValueSet supports back-ref to record,
|
||||
// we can employ better field access:
|
||||
// - no error on missing values when field exists
|
||||
// - proper handling of multi-value field values
|
||||
// - proper value-types that corelate to field types
|
||||
//v, err = expr.Select(scope, "rec.values.m2.0")
|
||||
//req.NoError(err)
|
||||
//req.NotEmpty(v)
|
||||
//req.Equal("mVal2.0", v.Get())
|
||||
})
|
||||
|
||||
t.Run("via gval selector", func(t *testing.T) {
|
||||
var (
|
||||
req = require.New(t)
|
||||
parser = expr.NewParser()
|
||||
)
|
||||
|
||||
eval, err := parser.Parse(`rec.values.s1 == "sVal1"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
|
||||
eval, err = parser.Parse(`rec.values.s1 != "foo"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
|
||||
eval, err = parser.Parse(`rec.values.m1[0] == "mVal1.0"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
|
||||
eval, err = parser.Parse(`rec.values.m1[1] == "mVal1.1"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
|
||||
eval, err = parser.Parse(`rec.values.m2[0] == "mVal2.0"`)
|
||||
req.NoError(err)
|
||||
req.True(eval.Test(context.Background(), scope))
|
||||
|
||||
})
|
||||
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
atypes "github.com/cortezaproject/corteza-server/automation/types"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza-server/pkg/label"
|
||||
"github.com/cortezaproject/corteza-server/pkg/wfexec"
|
||||
)
|
||||
|
||||
@@ -28,19 +27,12 @@ type (
|
||||
|
||||
func (h modulesHandler) register() {
|
||||
h.reg.AddFunctions(
|
||||
h.LookupByID(),
|
||||
h.Save(),
|
||||
h.Create(),
|
||||
h.Update(),
|
||||
h.Delete(),
|
||||
h.Lookup(),
|
||||
)
|
||||
}
|
||||
|
||||
type (
|
||||
modulesLookupByIDArgs struct {
|
||||
hasRecordID bool
|
||||
RecordID uint64
|
||||
|
||||
modulesLookupArgs struct {
|
||||
hasModule bool
|
||||
Module interface{}
|
||||
moduleID uint64
|
||||
@@ -54,64 +46,55 @@ type (
|
||||
namespaceRes *types.Namespace
|
||||
}
|
||||
|
||||
modulesLookupByIDResults struct {
|
||||
Record *types.Record
|
||||
modulesLookupResults struct {
|
||||
Module *types.Module
|
||||
}
|
||||
)
|
||||
|
||||
func (a modulesLookupByIDArgs) GetModule() (bool, uint64, string, *types.Module) {
|
||||
func (a modulesLookupArgs) GetModule() (bool, uint64, string, *types.Module) {
|
||||
return a.hasModule, a.moduleID, a.moduleHandle, a.moduleRes
|
||||
}
|
||||
|
||||
func (a modulesLookupByIDArgs) GetNamespace() (bool, uint64, string, *types.Namespace) {
|
||||
func (a modulesLookupArgs) GetNamespace() (bool, uint64, string, *types.Namespace) {
|
||||
return a.hasNamespace, a.namespaceID, a.namespaceHandle, a.namespaceRes
|
||||
}
|
||||
|
||||
// LookupByID function Lookup for compose record by ID
|
||||
// Lookup function Lookup for compose Lookup by ID
|
||||
//
|
||||
// expects implementation of lookupByID function:
|
||||
// func (h modulesHandler) lookupByID(ctx context.Context, args *modulesLookupByIDArgs) (results *modulesLookupByIDResults, err error) {
|
||||
// expects implementation of lookup function:
|
||||
// func (h modulesHandler) lookup(ctx context.Context, args *modulesLookupArgs) (results *modulesLookupResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h modulesHandler) LookupByID() *atypes.Function {
|
||||
func (h modulesHandler) Lookup() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "composeModulesLookupByID",
|
||||
Ref: "composeModulesLookup",
|
||||
Kind: "function",
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Lookup for compose record by ID",
|
||||
Short: "Lookup for compose Lookup by ID",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "recordID",
|
||||
Types: []string{"ID"}, Required: true,
|
||||
},
|
||||
{
|
||||
Name: "module",
|
||||
Types: []string{"ID", "String", "ComposeModule"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Module to set record type",
|
||||
Description: "Even with unique record ID across all modules, module needs to be known\nbefore doing any record operations. Mainly because records of different\nmodules can be located in different stores.",
|
||||
},
|
||||
Types: []string{"ID", "Handle", "ComposeModule"}, Required: true,
|
||||
},
|
||||
{
|
||||
Name: "namespace",
|
||||
Types: []string{"ID", "String", "ComposeNamespace"}, Required: true,
|
||||
Types: []string{"ID", "Handle", "ComposeNamespace"}, Required: true,
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "record",
|
||||
Types: []string{"ComposeRecord"},
|
||||
Name: "module",
|
||||
Types: []string{"ComposeModule"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &modulesLookupByIDArgs{
|
||||
hasRecordID: in.Has("recordID"),
|
||||
args = &modulesLookupArgs{
|
||||
hasModule: in.Has("module"),
|
||||
hasNamespace: in.Has("namespace"),
|
||||
}
|
||||
@@ -127,7 +110,7 @@ func (h modulesHandler) LookupByID() *atypes.Function {
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.moduleID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
case h.reg.Type("Handle").Type():
|
||||
args.moduleHandle = aux.Get().(string)
|
||||
case h.reg.Type("ComposeModule").Type():
|
||||
args.moduleRes = aux.Get().(*types.Module)
|
||||
@@ -140,29 +123,29 @@ func (h modulesHandler) LookupByID() *atypes.Function {
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.namespaceID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
case h.reg.Type("Handle").Type():
|
||||
args.namespaceHandle = aux.Get().(string)
|
||||
case h.reg.Type("ComposeNamespace").Type():
|
||||
args.namespaceRes = aux.Get().(*types.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
var results *modulesLookupByIDResults
|
||||
if results, err = h.lookupByID(ctx, args); err != nil {
|
||||
var results *modulesLookupResults
|
||||
if results, err = h.lookup(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Record (*types.Record) to ComposeRecord
|
||||
// converting results.Module (*types.Module) to ComposeModule
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("ComposeRecord").Cast(results.Record); err != nil {
|
||||
if tval, err = h.reg.Type("ComposeModule").Cast(results.Module); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "record", tval); err != nil {
|
||||
} else if err = expr.Assign(out, "module", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -171,495 +154,3 @@ func (h modulesHandler) LookupByID() *atypes.Function {
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
modulesSaveArgs struct {
|
||||
hasRecord bool
|
||||
Record *types.Record
|
||||
}
|
||||
|
||||
modulesSaveResults struct {
|
||||
Record *types.Record
|
||||
}
|
||||
)
|
||||
|
||||
// Save function Save record
|
||||
//
|
||||
// expects implementation of save function:
|
||||
// func (h modulesHandler) save(ctx context.Context, args *modulesSaveArgs) (results *modulesSaveResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h modulesHandler) Save() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "composeModulesSave",
|
||||
Kind: "function",
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Save record",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "record",
|
||||
Types: []string{"ComposeRecord"}, Required: true,
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "record",
|
||||
Types: []string{"ComposeRecord"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &modulesSaveArgs{
|
||||
hasRecord: in.Has("record"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
var results *modulesSaveResults
|
||||
if results, err = h.save(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Record (*types.Record) to ComposeRecord
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("ComposeRecord").Cast(results.Record); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "record", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
modulesCreateArgs struct {
|
||||
hasModule bool
|
||||
Module interface{}
|
||||
moduleID uint64
|
||||
moduleHandle string
|
||||
moduleRes *types.Module
|
||||
|
||||
hasNamespace bool
|
||||
Namespace interface{}
|
||||
namespaceID uint64
|
||||
namespaceHandle string
|
||||
namespaceRes *types.Namespace
|
||||
|
||||
hasValues bool
|
||||
Values types.RecordValueSet
|
||||
|
||||
hasLabels bool
|
||||
Labels label.Labels
|
||||
|
||||
hasOwnedBy bool
|
||||
OwnedBy uint64
|
||||
}
|
||||
|
||||
modulesCreateResults struct {
|
||||
Record *types.Record
|
||||
}
|
||||
)
|
||||
|
||||
func (a modulesCreateArgs) GetModule() (bool, uint64, string, *types.Module) {
|
||||
return a.hasModule, a.moduleID, a.moduleHandle, a.moduleRes
|
||||
}
|
||||
|
||||
func (a modulesCreateArgs) GetNamespace() (bool, uint64, string, *types.Namespace) {
|
||||
return a.hasNamespace, a.namespaceID, a.namespaceHandle, a.namespaceRes
|
||||
}
|
||||
|
||||
// Create function Creates and stores a new record
|
||||
//
|
||||
// expects implementation of create function:
|
||||
// func (h modulesHandler) create(ctx context.Context, args *modulesCreateArgs) (results *modulesCreateResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h modulesHandler) Create() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "composeModulesCreate",
|
||||
Kind: "function",
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Creates and stores a new record",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "module",
|
||||
Types: []string{"ID", "String", "ComposeModule"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Module to set record type",
|
||||
Description: "Even with unique record ID across all modules, module needs to be known\nbefore doing any record operations. Mainly because records of different\nmodules can be located in different stores.",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "namespace",
|
||||
Types: []string{"ID", "String", "ComposeNamespace"}, Required: true,
|
||||
},
|
||||
{
|
||||
Name: "values",
|
||||
Types: []string{"KV"},
|
||||
},
|
||||
{
|
||||
Name: "labels",
|
||||
Types: []string{"KV"},
|
||||
},
|
||||
{
|
||||
Name: "ownedBy",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Record owner",
|
||||
Visual: map[string]interface{}{"ref": "users"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "record",
|
||||
Types: []string{"ComposeRecord"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &modulesCreateArgs{
|
||||
hasModule: in.Has("module"),
|
||||
hasNamespace: in.Has("namespace"),
|
||||
hasValues: in.Has("values"),
|
||||
hasLabels: in.Has("labels"),
|
||||
hasOwnedBy: in.Has("ownedBy"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Module argument
|
||||
if args.hasModule {
|
||||
aux := expr.Must(expr.Select(in, "module"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.moduleID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
args.moduleHandle = aux.Get().(string)
|
||||
case h.reg.Type("ComposeModule").Type():
|
||||
args.moduleRes = aux.Get().(*types.Module)
|
||||
}
|
||||
}
|
||||
|
||||
// Converting Namespace argument
|
||||
if args.hasNamespace {
|
||||
aux := expr.Must(expr.Select(in, "namespace"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.namespaceID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
args.namespaceHandle = aux.Get().(string)
|
||||
case h.reg.Type("ComposeNamespace").Type():
|
||||
args.namespaceRes = aux.Get().(*types.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
var results *modulesCreateResults
|
||||
if results, err = h.create(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Record (*types.Record) to ComposeRecord
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("ComposeRecord").Cast(results.Record); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "record", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
modulesUpdateArgs struct {
|
||||
hasModule bool
|
||||
Module interface{}
|
||||
moduleID uint64
|
||||
moduleHandle string
|
||||
moduleRes *types.Module
|
||||
|
||||
hasNamespace bool
|
||||
Namespace interface{}
|
||||
namespaceID uint64
|
||||
namespaceHandle string
|
||||
namespaceRes *types.Namespace
|
||||
|
||||
hasValues bool
|
||||
Values types.RecordValueSet
|
||||
|
||||
hasLabels bool
|
||||
Labels label.Labels
|
||||
|
||||
hasOwnedBy bool
|
||||
OwnedBy uint64
|
||||
}
|
||||
|
||||
modulesUpdateResults struct {
|
||||
Record *types.Record
|
||||
}
|
||||
)
|
||||
|
||||
func (a modulesUpdateArgs) GetModule() (bool, uint64, string, *types.Module) {
|
||||
return a.hasModule, a.moduleID, a.moduleHandle, a.moduleRes
|
||||
}
|
||||
|
||||
func (a modulesUpdateArgs) GetNamespace() (bool, uint64, string, *types.Namespace) {
|
||||
return a.hasNamespace, a.namespaceID, a.namespaceHandle, a.namespaceRes
|
||||
}
|
||||
|
||||
// Update function Updates an existing record
|
||||
//
|
||||
// expects implementation of update function:
|
||||
// func (h modulesHandler) update(ctx context.Context, args *modulesUpdateArgs) (results *modulesUpdateResults, err error) {
|
||||
// return
|
||||
// }
|
||||
func (h modulesHandler) Update() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "composeModulesUpdate",
|
||||
Kind: "function",
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Updates an existing record",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "module",
|
||||
Types: []string{"ID", "String", "ComposeModule"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Module to set record type",
|
||||
Description: "Even with unique record ID across all modules, module needs to be known\nbefore doing any record operations. Mainly because records of different\nmodules can be located in different stores.",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "namespace",
|
||||
Types: []string{"ID", "String", "ComposeNamespace"}, Required: true,
|
||||
},
|
||||
{
|
||||
Name: "values",
|
||||
Types: []string{"KV"},
|
||||
},
|
||||
{
|
||||
Name: "labels",
|
||||
Types: []string{"KV"},
|
||||
},
|
||||
{
|
||||
Name: "ownedBy",
|
||||
Types: []string{"ID"},
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Record owner",
|
||||
Visual: map[string]interface{}{"ref": "users"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "record",
|
||||
Types: []string{"ComposeRecord"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &modulesUpdateArgs{
|
||||
hasModule: in.Has("module"),
|
||||
hasNamespace: in.Has("namespace"),
|
||||
hasValues: in.Has("values"),
|
||||
hasLabels: in.Has("labels"),
|
||||
hasOwnedBy: in.Has("ownedBy"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Module argument
|
||||
if args.hasModule {
|
||||
aux := expr.Must(expr.Select(in, "module"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.moduleID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
args.moduleHandle = aux.Get().(string)
|
||||
case h.reg.Type("ComposeModule").Type():
|
||||
args.moduleRes = aux.Get().(*types.Module)
|
||||
}
|
||||
}
|
||||
|
||||
// Converting Namespace argument
|
||||
if args.hasNamespace {
|
||||
aux := expr.Must(expr.Select(in, "namespace"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.namespaceID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
args.namespaceHandle = aux.Get().(string)
|
||||
case h.reg.Type("ComposeNamespace").Type():
|
||||
args.namespaceRes = aux.Get().(*types.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
var results *modulesUpdateResults
|
||||
if results, err = h.update(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Record (*types.Record) to ComposeRecord
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("ComposeRecord").Cast(results.Record); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "record", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
type (
|
||||
modulesDeleteArgs struct {
|
||||
hasRecordID bool
|
||||
RecordID uint64
|
||||
|
||||
hasModule bool
|
||||
Module interface{}
|
||||
moduleID uint64
|
||||
moduleHandle string
|
||||
moduleRes *types.Module
|
||||
|
||||
hasNamespace bool
|
||||
Namespace interface{}
|
||||
namespaceID uint64
|
||||
namespaceHandle string
|
||||
namespaceRes *types.Namespace
|
||||
}
|
||||
)
|
||||
|
||||
func (a modulesDeleteArgs) GetModule() (bool, uint64, string, *types.Module) {
|
||||
return a.hasModule, a.moduleID, a.moduleHandle, a.moduleRes
|
||||
}
|
||||
|
||||
func (a modulesDeleteArgs) GetNamespace() (bool, uint64, string, *types.Namespace) {
|
||||
return a.hasNamespace, a.namespaceID, a.namespaceHandle, a.namespaceRes
|
||||
}
|
||||
|
||||
// Delete function Soft deletes compose record by ID
|
||||
//
|
||||
// expects implementation of delete function:
|
||||
// func (h modulesHandler) delete(ctx context.Context, args *modulesDeleteArgs) (err error) {
|
||||
// return
|
||||
// }
|
||||
func (h modulesHandler) Delete() *atypes.Function {
|
||||
return &atypes.Function{
|
||||
Ref: "composeModulesDelete",
|
||||
Kind: "function",
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Soft deletes compose record by ID",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "recordID",
|
||||
Types: []string{"ID"}, Required: true,
|
||||
},
|
||||
{
|
||||
Name: "module",
|
||||
Types: []string{"ID", "String", "ComposeModule"}, Required: true,
|
||||
Meta: &atypes.ParamMeta{
|
||||
Label: "Module to set record type",
|
||||
Description: "Even with unique record ID across all modules, module needs to be known\nbefore doing any record operations. Mainly because records of different\nmodules can be located in different stores.",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "namespace",
|
||||
Types: []string{"ID", "String", "ComposeNamespace"}, Required: true,
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &modulesDeleteArgs{
|
||||
hasRecordID: in.Has("recordID"),
|
||||
hasModule: in.Has("module"),
|
||||
hasNamespace: in.Has("namespace"),
|
||||
}
|
||||
)
|
||||
|
||||
if err = in.Decode(args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Module argument
|
||||
if args.hasModule {
|
||||
aux := expr.Must(expr.Select(in, "module"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.moduleID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
args.moduleHandle = aux.Get().(string)
|
||||
case h.reg.Type("ComposeModule").Type():
|
||||
args.moduleRes = aux.Get().(*types.Module)
|
||||
}
|
||||
}
|
||||
|
||||
// Converting Namespace argument
|
||||
if args.hasNamespace {
|
||||
aux := expr.Must(expr.Select(in, "namespace"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.namespaceID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
args.namespaceHandle = aux.Get().(string)
|
||||
case h.reg.Type("ComposeNamespace").Type():
|
||||
args.namespaceRes = aux.Get().(*types.Namespace)
|
||||
}
|
||||
}
|
||||
|
||||
return out, h.delete(ctx, args)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,157 +2,82 @@ package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
)
|
||||
|
||||
type (
|
||||
recordService interface {
|
||||
FindByID(ctx context.Context, namespaceID, moduleID, recordID uint64) (*types.Record, error)
|
||||
Find(ctx context.Context, filter types.RecordFilter) (set types.RecordSet, f types.RecordFilter, err error)
|
||||
|
||||
Create(ctx context.Context, record *types.Record) (*types.Record, error)
|
||||
Update(ctx context.Context, record *types.Record) (*types.Record, error)
|
||||
Bulk(ctx context.Context, oo ...*types.RecordBulkOperation) (types.RecordSet, error)
|
||||
|
||||
DeleteByID(ctx context.Context, namespaceID, moduleID uint64, recordID ...uint64) error
|
||||
}
|
||||
|
||||
moduleService interface {
|
||||
FindByID(ctx context.Context, namespaceID, moduleID uint64) (*types.Module, error)
|
||||
FindByHandle(ctx context.Context, namespaceID uint64, handle string) (*types.Module, error)
|
||||
Find(ctx context.Context, filter types.ModuleFilter) (set types.ModuleSet, f types.ModuleFilter, err error)
|
||||
|
||||
Create(ctx context.Context, module *types.Module) (*types.Module, error)
|
||||
Update(ctx context.Context, module *types.Module) (*types.Module, error)
|
||||
|
||||
DeleteByID(ctx context.Context, namespaceID uint64, moduleID uint64) error
|
||||
}
|
||||
|
||||
namespaceService interface {
|
||||
FindByID(ctx context.Context, namespaceID uint64) (*types.Namespace, error)
|
||||
FindByHandle(ctx context.Context, handle string) (*types.Namespace, error)
|
||||
}
|
||||
|
||||
recordsHandlers struct {
|
||||
reg recordsHandlerRegistry
|
||||
modulesHandler struct {
|
||||
reg modulesHandlerRegistry
|
||||
ns namespaceService
|
||||
mod moduleService
|
||||
rec recordService
|
||||
}
|
||||
|
||||
moduleLookup interface {
|
||||
GetModule() (bool, uint64, string, *types.Module)
|
||||
}
|
||||
)
|
||||
|
||||
func RecordsHandlers(reg recordsHandlerRegistry, ns namespaceService, mod moduleService, rec recordService) *recordsHandlers {
|
||||
h := &recordsHandlers{
|
||||
func ModulesHandler(reg modulesHandlerRegistry, ns namespaceService, mod moduleService) *modulesHandler {
|
||||
h := &modulesHandler{
|
||||
reg: reg,
|
||||
ns: ns,
|
||||
mod: mod,
|
||||
rec: rec,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func resolveNamespace(ctx context.Context, svc namespaceService, id *uint64, handle string, res *types.Namespace) (err error) {
|
||||
if *id == 0 {
|
||||
if len(handle) > 0 {
|
||||
if res, err = svc.FindByHandle(ctx, handle); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if res != nil {
|
||||
*id = res.ID
|
||||
}
|
||||
}
|
||||
|
||||
func (h modulesHandler) lookup(ctx context.Context, args *modulesLookupArgs) (results *modulesLookupResults, err error) {
|
||||
results = &modulesLookupResults{}
|
||||
results.Module, err = lookupModule(ctx, h.ns, h.mod, args)
|
||||
return
|
||||
}
|
||||
|
||||
func resolveModule(ctx context.Context, svc moduleService, namespaceID uint64, id *uint64, handle string, res *types.Module) (err error) {
|
||||
if *id == 0 {
|
||||
if len(handle) > 0 {
|
||||
if res, err = svc.FindByHandle(ctx, namespaceID, handle); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if res != nil {
|
||||
*id = res.ID
|
||||
}
|
||||
func getModuleID(ctx context.Context, nsSvc namespaceService, modSvc moduleService, args moduleLookup) (namespaceID uint64, moduleID uint64, err error) {
|
||||
namespaceID, err = getNamespaceID(ctx, nsSvc, args.(namespaceLookup))
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
var mod *types.Module
|
||||
if _, moduleID, _, _ = args.GetModule(); moduleID > 0 {
|
||||
return
|
||||
} else if mod, err = lookupModule(ctx, nsSvc, modSvc, args); err != nil {
|
||||
return
|
||||
} else {
|
||||
return namespaceID, mod.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (h recordsHandlers) lookupByID(ctx context.Context, args *recordsLookupByIDArgs) (results *recordsLookupByIDResults, err error) {
|
||||
results = &recordsLookupByIDResults{}
|
||||
|
||||
if err = resolveNamespace(ctx, h.ns, &args.namespaceID, args.namespaceHandle, args.namespaceRes); err != nil {
|
||||
return nil, err
|
||||
func lookupModule(ctx context.Context, nsSvc namespaceService, modSvc moduleService, args moduleLookup) (*types.Module, error) {
|
||||
namespaceID, err := getNamespaceID(ctx, nsSvc, args.(namespaceLookup))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not load namespace: %w", err)
|
||||
}
|
||||
|
||||
if err = resolveModule(ctx, h.mod, args.namespaceID, &args.moduleID, args.moduleHandle, args.moduleRes); err != nil {
|
||||
return nil, err
|
||||
_, ID, handle, module := args.GetModule()
|
||||
|
||||
switch {
|
||||
case module != nil:
|
||||
return module, nil
|
||||
case ID > 0:
|
||||
return modSvc.FindByID(ctx, namespaceID, ID)
|
||||
case len(handle) > 0:
|
||||
return modSvc.FindByHandle(ctx, namespaceID, handle)
|
||||
}
|
||||
|
||||
results.Record, err = h.rec.FindByID(ctx, args.namespaceID, args.moduleID, args.RecordID)
|
||||
return
|
||||
}
|
||||
|
||||
func (h recordsHandlers) create(ctx context.Context, args *recordsCreateArgs) (results *recordsCreateResults, err error) {
|
||||
results = &recordsCreateResults{}
|
||||
|
||||
if err = resolveNamespace(ctx, h.ns, &args.namespaceID, args.namespaceHandle, args.namespaceRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = resolveModule(ctx, h.mod, args.namespaceID, &args.moduleID, args.moduleHandle, args.moduleRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rec := &types.Record{
|
||||
ModuleID: args.moduleID,
|
||||
NamespaceID: args.namespaceID,
|
||||
Values: args.Values,
|
||||
Labels: args.Labels,
|
||||
OwnedBy: args.OwnedBy,
|
||||
}
|
||||
|
||||
results.Record, err = h.rec.Create(ctx, rec)
|
||||
return
|
||||
}
|
||||
|
||||
func (h recordsHandlers) save(ctx context.Context, args *recordsSaveArgs) (results *recordsSaveResults, err error) {
|
||||
results = &recordsSaveResults{}
|
||||
results.Record, err = h.rec.Update(ctx, args.Record)
|
||||
return
|
||||
}
|
||||
|
||||
func (h recordsHandlers) update(ctx context.Context, args *recordsUpdateArgs) (results *recordsUpdateResults, err error) {
|
||||
results = &recordsUpdateResults{}
|
||||
if err = resolveNamespace(ctx, h.ns, &args.namespaceID, args.namespaceHandle, args.namespaceRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err = resolveModule(ctx, h.mod, args.namespaceID, &args.moduleID, args.moduleHandle, args.moduleRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rec := &types.Record{
|
||||
ModuleID: args.moduleID,
|
||||
NamespaceID: args.namespaceID,
|
||||
Values: args.Values,
|
||||
Labels: args.Labels,
|
||||
OwnedBy: args.OwnedBy,
|
||||
}
|
||||
|
||||
results.Record, err = h.rec.Update(ctx, rec)
|
||||
return
|
||||
}
|
||||
|
||||
func (h recordsHandlers) delete(ctx context.Context, args *recordsDeleteArgs) (err error) {
|
||||
if err = resolveNamespace(ctx, h.ns, &args.namespaceID, args.namespaceHandle, args.namespaceRes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err = resolveModule(ctx, h.mod, args.namespaceID, &args.moduleID, args.moduleHandle, args.moduleRes); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return h.rec.DeleteByID(ctx, args.namespaceID, args.moduleID, args.RecordID)
|
||||
return nil, fmt.Errorf("empty module lookup params")
|
||||
}
|
||||
|
||||
@@ -2,123 +2,31 @@ prefix: compose
|
||||
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza-server/compose/types
|
||||
- github.com/cortezaproject/corteza-server/pkg/label
|
||||
# - time
|
||||
|
||||
params:
|
||||
module: &module
|
||||
moduleLookup: &moduleLookup
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, go: uint64, suffix: ID }
|
||||
- { wf: String, go: string, suffix: handle }
|
||||
- { wf: ComposeModule, go: "*types.Module", suffix: res }
|
||||
meta:
|
||||
label: Module to set record type
|
||||
description: |-
|
||||
Even with unique record ID across all modules, module needs to be known
|
||||
before doing any record operations. Mainly because records of different
|
||||
modules can be located in different stores.
|
||||
- { wf: ID, }
|
||||
- { wf: Handle, }
|
||||
- { wf: ComposeModule, suffix: res }
|
||||
|
||||
namespace: &namespace
|
||||
namespaceLookup: &namespaceLookup
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, go: uint64, suffix: ID }
|
||||
- { wf: String, go: string, suffix: handle }
|
||||
- { wf: ComposeNamespace, go: "*types.Namespace", suffix: res }
|
||||
- { wf: ID, }
|
||||
- { wf: Handle, }
|
||||
- { wf: ComposeNamespace, suffix: res }
|
||||
|
||||
record: &record
|
||||
required: true
|
||||
types:
|
||||
- { wf: ComposeRecord, go: "*types.Record" }
|
||||
|
||||
recordID: &recordID
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, go: uint64 }
|
||||
|
||||
values: &values
|
||||
types: [ { wf: KV, go: 'types.RecordValueSet' }]
|
||||
|
||||
labels: &labels
|
||||
types: [ { wf: KV, go: 'label.Labels' }]
|
||||
|
||||
ownedBy: &ownedBy
|
||||
types: [ { wf: ID, go: uint64 } ]
|
||||
meta:
|
||||
label: Record owner
|
||||
visual:
|
||||
ref: users
|
||||
|
||||
recordReturnValue: &recordReturnValue
|
||||
wf: ComposeRecord
|
||||
go: "*types.Record"
|
||||
rvModule: &rvModule
|
||||
wf: ComposeModule
|
||||
|
||||
functions:
|
||||
lookupByID:
|
||||
lookup:
|
||||
meta:
|
||||
short: Lookup for compose record by ID
|
||||
short: Lookup for compose Lookup by ID
|
||||
params:
|
||||
recordID: *recordID
|
||||
module: *module
|
||||
namespace: *namespace
|
||||
module: *moduleLookup
|
||||
namespace: *namespaceLookup
|
||||
results:
|
||||
record: *recordReturnValue
|
||||
|
||||
save:
|
||||
meta:
|
||||
short: Save record
|
||||
params:
|
||||
record: *record
|
||||
results:
|
||||
record: *recordReturnValue
|
||||
|
||||
# @todo validate
|
||||
# -> record
|
||||
# <- errors RVSE
|
||||
# <- valid (bool)
|
||||
|
||||
# @todo copy
|
||||
# -> source Record
|
||||
# -> target Record
|
||||
# -> include []string list of fields from source record to copy
|
||||
# -> exclude []string list of fields from source record to copy
|
||||
|
||||
create:
|
||||
meta:
|
||||
short: Creates and stores a new record
|
||||
params:
|
||||
module: *module
|
||||
namespace: *namespace
|
||||
values: *values
|
||||
labels: *labels
|
||||
ownedBy: *ownedBy
|
||||
results:
|
||||
record: *recordReturnValue
|
||||
|
||||
update:
|
||||
meta:
|
||||
short: Updates an existing record
|
||||
params:
|
||||
module: *module
|
||||
namespace: *namespace
|
||||
values: *values
|
||||
labels: *labels
|
||||
ownedBy: *ownedBy
|
||||
results:
|
||||
record: *recordReturnValue
|
||||
|
||||
delete:
|
||||
meta:
|
||||
short: Soft deletes compose record by ID
|
||||
params:
|
||||
recordID: *recordID
|
||||
module: *module
|
||||
namespace: *namespace
|
||||
|
||||
# restore:
|
||||
# meta:
|
||||
# short: Soft deletes compose record by ID
|
||||
# params:
|
||||
# recordID: *recordID
|
||||
# module: *module
|
||||
# namespace: *namespace
|
||||
module: *rvModule
|
||||
|
||||
@@ -33,11 +33,6 @@ func (h namespacesHandler) register() {
|
||||
|
||||
type (
|
||||
namespacesLookupArgs struct {
|
||||
hasModule bool
|
||||
Module interface{}
|
||||
moduleID uint64
|
||||
moduleHandle string
|
||||
|
||||
hasNamespace bool
|
||||
Namespace interface{}
|
||||
namespaceID uint64
|
||||
@@ -46,19 +41,15 @@ type (
|
||||
}
|
||||
|
||||
namespacesLookupResults struct {
|
||||
Module *types.Module
|
||||
Namespace *types.Namespace
|
||||
}
|
||||
)
|
||||
|
||||
func (a namespacesLookupArgs) GetModule() (bool, uint64, string) {
|
||||
return a.hasModule, a.moduleID, a.moduleHandle
|
||||
}
|
||||
|
||||
func (a namespacesLookupArgs) GetNamespace() (bool, uint64, string, *types.Namespace) {
|
||||
return a.hasNamespace, a.namespaceID, a.namespaceHandle, a.namespaceRes
|
||||
}
|
||||
|
||||
// Lookup function Lookup for compose module by ID
|
||||
// Lookup function Lookup for compose namespace by ID
|
||||
//
|
||||
// expects implementation of lookup function:
|
||||
// func (h namespacesHandler) lookup(ctx context.Context, args *namespacesLookupArgs) (results *namespacesLookupResults, err error) {
|
||||
@@ -69,32 +60,27 @@ func (h namespacesHandler) Lookup() *atypes.Function {
|
||||
Ref: "composeNamespacesLookup",
|
||||
Kind: "function",
|
||||
Meta: &atypes.FunctionMeta{
|
||||
Short: "Lookup for compose module by ID",
|
||||
Short: "Lookup for compose namespace by ID",
|
||||
},
|
||||
|
||||
Parameters: []*atypes.Param{
|
||||
{
|
||||
Name: "module",
|
||||
Types: []string{"ID", "String"}, Required: true,
|
||||
},
|
||||
{
|
||||
Name: "namespace",
|
||||
Types: []string{"ID", "String", "ComposeNamespace"}, Required: true,
|
||||
Types: []string{"ID", "Handle", "ComposeNamespace"}, Required: true,
|
||||
},
|
||||
},
|
||||
|
||||
Results: []*atypes.Param{
|
||||
|
||||
{
|
||||
Name: "module",
|
||||
Types: []string{"ComposeModule"},
|
||||
Name: "namespace",
|
||||
Types: []string{"ComposeNamespace"},
|
||||
},
|
||||
},
|
||||
|
||||
Handler: func(ctx context.Context, in *expr.Vars) (out *expr.Vars, err error) {
|
||||
var (
|
||||
args = &namespacesLookupArgs{
|
||||
hasModule: in.Has("module"),
|
||||
hasNamespace: in.Has("namespace"),
|
||||
}
|
||||
)
|
||||
@@ -103,24 +89,13 @@ func (h namespacesHandler) Lookup() *atypes.Function {
|
||||
return
|
||||
}
|
||||
|
||||
// Converting Module argument
|
||||
if args.hasModule {
|
||||
aux := expr.Must(expr.Select(in, "module"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.moduleID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
args.moduleHandle = aux.Get().(string)
|
||||
}
|
||||
}
|
||||
|
||||
// Converting Namespace argument
|
||||
if args.hasNamespace {
|
||||
aux := expr.Must(expr.Select(in, "namespace"))
|
||||
switch aux.Type() {
|
||||
case h.reg.Type("ID").Type():
|
||||
args.namespaceID = aux.Get().(uint64)
|
||||
case h.reg.Type("String").Type():
|
||||
case h.reg.Type("Handle").Type():
|
||||
args.namespaceHandle = aux.Get().(string)
|
||||
case h.reg.Type("ComposeNamespace").Type():
|
||||
args.namespaceRes = aux.Get().(*types.Namespace)
|
||||
@@ -135,14 +110,14 @@ func (h namespacesHandler) Lookup() *atypes.Function {
|
||||
out = &expr.Vars{}
|
||||
|
||||
{
|
||||
// converting results.Module (*types.Module) to ComposeModule
|
||||
// converting results.Namespace (*types.Namespace) to ComposeNamespace
|
||||
var (
|
||||
tval expr.TypedValue
|
||||
)
|
||||
|
||||
if tval, err = h.reg.Type("ComposeModule").Cast(results.Module); err != nil {
|
||||
if tval, err = h.reg.Type("ComposeNamespace").Cast(results.Namespace); err != nil {
|
||||
return
|
||||
} else if err = expr.Assign(out, "module", tval); err != nil {
|
||||
} else if err = expr.Assign(out, "namespace", tval); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,72 +2,71 @@ package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
)
|
||||
|
||||
type (
|
||||
moduleService interface {
|
||||
FindByID(ctx context.Context, namespaceID, moduleID uint64) (*types.Module, error)
|
||||
FindByHandle(ctx context.Context, namespaceID uint64, handle string) (*types.Module, error)
|
||||
Find(ctx context.Context, filter types.ModuleFilter) (set types.ModuleSet, f types.ModuleFilter, err error)
|
||||
|
||||
Create(ctx context.Context, module *types.Module) (*types.Module, error)
|
||||
Update(ctx context.Context, module *types.Module) (*types.Module, error)
|
||||
|
||||
DeleteByID(ctx context.Context, namespaceID uint64, moduleID ...uint64) error
|
||||
}
|
||||
|
||||
moduleNamespaceService interface {
|
||||
namespaceService interface {
|
||||
FindByID(ctx context.Context, namespaceID uint64) (*types.Namespace, error)
|
||||
FindByHandle(ctx context.Context, handle string) (*types.Namespace, error)
|
||||
Find(ctx context.Context, filter types.NamespaceFilter) (set types.NamespaceSet, f types.NamespaceFilter, err error)
|
||||
|
||||
Create(ctx context.Context, namespace *types.Namespace) (*types.Namespace, error)
|
||||
Update(ctx context.Context, namespace *types.Namespace) (*types.Namespace, error)
|
||||
|
||||
DeleteByID(ctx context.Context, namespaceID uint64) error
|
||||
}
|
||||
|
||||
modulesHandlers struct {
|
||||
reg modulesHandlerRegistry
|
||||
ns moduleNamespaceService
|
||||
mod moduleService
|
||||
namespacesHandler struct {
|
||||
reg namespacesHandlerRegistry
|
||||
ns namespaceService
|
||||
}
|
||||
|
||||
namespaceLookup interface {
|
||||
GetNamespace() (bool, uint64, string, *types.Namespace)
|
||||
}
|
||||
)
|
||||
|
||||
func ModulesHandlers(reg modulesHandlerRegistry, ns moduleNamespaceService, rec moduleService) *modulesHandlers {
|
||||
h := &modulesHandlers{
|
||||
func NamespacesHandler(reg namespacesHandlerRegistry, ns namespaceService) *namespacesHandler {
|
||||
h := &namespacesHandler{
|
||||
reg: reg,
|
||||
ns: ns,
|
||||
mod: rec,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h modulesHandlers) resolveNamespace(ctx context.Context, id *uint64, handle string, res *types.Namespace) (err error) {
|
||||
if *id == 0 {
|
||||
if len(handle) > 0 {
|
||||
if res, err = h.ns.FindByHandle(ctx, handle); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if res != nil {
|
||||
*id = res.ID
|
||||
}
|
||||
}
|
||||
func (h namespacesHandler) lookup(ctx context.Context, args *namespacesLookupArgs) (results *namespacesLookupResults, err error) {
|
||||
results = &namespacesLookupResults{}
|
||||
|
||||
results.Namespace, err = lookupNamespace(ctx, h.ns, args)
|
||||
return
|
||||
}
|
||||
|
||||
func (h modulesHandlers) lookup(ctx context.Context, args *modulesLookupArgs) (results *modulesLookupResults, err error) {
|
||||
results = &modulesLookupResults{}
|
||||
|
||||
if err = h.resolveNamespace(ctx, &args.namespaceID, args.namespaceHandle, args.namespaceRes); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if args.moduleID > 0 {
|
||||
results.Module, err = h.mod.FindByID(ctx, args.namespaceID, args.moduleID)
|
||||
func getNamespaceID(ctx context.Context, svc namespaceService, args namespaceLookup) (uint64, error) {
|
||||
_, ID, _, _ := args.GetNamespace()
|
||||
if ID > 0 {
|
||||
return ID, nil
|
||||
} else if ns, err := lookupNamespace(ctx, svc, args); err != nil {
|
||||
return 0, err
|
||||
} else {
|
||||
results.Module, err = h.mod.FindByHandle(ctx, args.namespaceID, args.moduleHandle)
|
||||
return ns.ID, nil
|
||||
}
|
||||
}
|
||||
|
||||
func lookupNamespace(ctx context.Context, svc namespaceService, args namespaceLookup) (*types.Namespace, error) {
|
||||
_, ID, handle, namespace := args.GetNamespace()
|
||||
|
||||
switch {
|
||||
case namespace != nil:
|
||||
return namespace, nil
|
||||
case ID > 0:
|
||||
return svc.FindByID(ctx, ID)
|
||||
case len(handle) > 0:
|
||||
return svc.FindByHandle(ctx, handle)
|
||||
}
|
||||
|
||||
return
|
||||
return nil, fmt.Errorf("empty namespace lookup params")
|
||||
}
|
||||
|
||||
@@ -4,29 +4,21 @@ imports:
|
||||
- github.com/cortezaproject/corteza-server/compose/types
|
||||
|
||||
params:
|
||||
module: &module
|
||||
namespaceLookup: &namespaceLookup
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, go: uint64, suffix: ID }
|
||||
- { wf: String, go: string, suffix: handle }
|
||||
- { wf: ID, }
|
||||
- { wf: Handle, }
|
||||
- { wf: ComposeNamespace, suffix: res }
|
||||
|
||||
namespace: &namespace
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, go: uint64, suffix: ID }
|
||||
- { wf: String, go: string, suffix: handle }
|
||||
- { wf: ComposeNamespace, go: "*types.Namespace", suffix: res }
|
||||
|
||||
moduleReturnValue: &moduleReturnValue
|
||||
wf: ComposeModule
|
||||
go: "*types.Module"
|
||||
rvNamespace: &rvNamespace
|
||||
wf: ComposeNamespace
|
||||
|
||||
functions:
|
||||
lookup:
|
||||
meta:
|
||||
short: Lookup for compose module by ID
|
||||
short: Lookup for compose namespace by ID
|
||||
params:
|
||||
module: *module
|
||||
namespace: *namespace
|
||||
namespace: *namespaceLookup
|
||||
results:
|
||||
module: *moduleReturnValue
|
||||
namespace: *rvNamespace
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,259 @@
|
||||
package automation
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
. "github.com/cortezaproject/corteza-server/pkg/expr"
|
||||
"github.com/cortezaproject/corteza-server/pkg/filter"
|
||||
"github.com/cortezaproject/corteza-server/pkg/wfexec"
|
||||
)
|
||||
|
||||
type (
|
||||
recordService interface {
|
||||
FindByID(ctx context.Context, namespaceID, moduleID, recordID uint64) (*types.Record, error)
|
||||
Find(ctx context.Context, filter types.RecordFilter) (set types.RecordSet, f types.RecordFilter, err error)
|
||||
|
||||
Create(ctx context.Context, record *types.Record) (*types.Record, error)
|
||||
Update(ctx context.Context, record *types.Record) (*types.Record, error)
|
||||
Bulk(ctx context.Context, oo ...*types.RecordBulkOperation) (types.RecordSet, error)
|
||||
|
||||
Validate(ctx context.Context, rec *types.Record) error
|
||||
|
||||
DeleteByID(ctx context.Context, namespaceID, moduleID uint64, recordID ...uint64) error
|
||||
}
|
||||
|
||||
recordsHandler struct {
|
||||
reg recordsHandlerRegistry
|
||||
ns namespaceService
|
||||
mod moduleService
|
||||
rec recordService
|
||||
}
|
||||
|
||||
recordSetIterator struct {
|
||||
ptr int
|
||||
set types.RecordSet
|
||||
filter types.RecordFilter
|
||||
}
|
||||
|
||||
recordLookup interface {
|
||||
GetRecord() (bool, uint64, *types.Record)
|
||||
}
|
||||
)
|
||||
|
||||
func RecordsHandler(reg recordsHandlerRegistry, ns namespaceService, mod moduleService, rec recordService) *recordsHandler {
|
||||
h := &recordsHandler{
|
||||
reg: reg,
|
||||
ns: ns,
|
||||
mod: mod,
|
||||
rec: rec,
|
||||
}
|
||||
|
||||
h.register()
|
||||
return h
|
||||
}
|
||||
|
||||
func (h recordsHandler) lookup(ctx context.Context, args *recordsLookupArgs) (results *recordsLookupResults, err error) {
|
||||
results = &recordsLookupResults{}
|
||||
results.Record, err = h.lookupRecord(ctx, args)
|
||||
return
|
||||
}
|
||||
|
||||
func (h recordsHandler) search(ctx context.Context, args *recordsSearchArgs) (results *recordsSearchResults, err error) {
|
||||
results = &recordsSearchResults{}
|
||||
|
||||
var (
|
||||
f = types.RecordFilter{
|
||||
Query: args.Query,
|
||||
Labels: args.Labels,
|
||||
Deleted: filter.State(args.Deleted),
|
||||
}
|
||||
)
|
||||
|
||||
if ns, mod, err := h.loadCombo(ctx, args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
f.ModuleID = mod.ID
|
||||
f.NamespaceID = ns.ID
|
||||
}
|
||||
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasPageCursor {
|
||||
if err = f.PageCursor.Decode(args.PageCursor); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
f.IncTotal = args.IncTotal
|
||||
f.IncPageNavigation = args.IncPageNavigation
|
||||
|
||||
if args.hasLabels {
|
||||
f.Labels = args.Labels
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
f.Limit = uint(args.Limit)
|
||||
}
|
||||
|
||||
results.Records, _, err = h.rec.Find(ctx, f)
|
||||
return
|
||||
}
|
||||
|
||||
func (h recordsHandler) each(ctx context.Context, args *recordsEachArgs) (out wfexec.IteratorHandler, err error) {
|
||||
var (
|
||||
i = &recordSetIterator{}
|
||||
f = types.RecordFilter{
|
||||
Query: args.Query,
|
||||
Labels: args.Labels,
|
||||
Deleted: filter.State(args.Deleted),
|
||||
}
|
||||
)
|
||||
|
||||
if ns, mod, err := h.loadCombo(ctx, args); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
f.ModuleID = mod.ID
|
||||
f.NamespaceID = ns.ID
|
||||
}
|
||||
|
||||
if args.hasSort {
|
||||
if err = f.Sort.Set(args.Sort); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if args.hasPageCursor {
|
||||
if err = f.PageCursor.Decode(args.PageCursor); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
f.IncTotal = args.IncTotal
|
||||
f.IncPageNavigation = args.IncPageNavigation
|
||||
|
||||
if args.hasLabels {
|
||||
f.Labels = args.Labels
|
||||
}
|
||||
|
||||
if args.hasLimit {
|
||||
f.Limit = uint(args.Limit)
|
||||
}
|
||||
|
||||
i.set, i.filter, err = h.rec.Find(ctx, f)
|
||||
return i, err
|
||||
}
|
||||
|
||||
func (h recordsHandler) validate(ctx context.Context, args *recordsValidateArgs) (*recordsValidateResults, error) {
|
||||
results := &recordsValidateResults{Valid: true}
|
||||
if err := h.rec.Validate(ctx, args.Record); err != nil {
|
||||
results.Valid = false
|
||||
|
||||
//if rves, is := err.(*types.RecordValueErrorSet); is {
|
||||
// results.Errors = rves
|
||||
//} else {
|
||||
// return nil, err
|
||||
//}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (h recordsHandler) new(ctx context.Context, args *recordsNewArgs) (*recordsNewResults, error) {
|
||||
results := &recordsNewResults{}
|
||||
namespace, module, err := h.loadCombo(ctx, args)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results.Record = &types.Record{
|
||||
ModuleID: module.ID,
|
||||
NamespaceID: namespace.ID,
|
||||
}
|
||||
|
||||
results.Record.SetModule(module)
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func (h recordsHandler) create(ctx context.Context, args *recordsCreateArgs) (results *recordsCreateResults, err error) {
|
||||
results = &recordsCreateResults{}
|
||||
results.Record, err = h.rec.Create(ctx, args.Record)
|
||||
return
|
||||
}
|
||||
|
||||
func (h recordsHandler) update(ctx context.Context, args *recordsUpdateArgs) (results *recordsUpdateResults, err error) {
|
||||
results = &recordsUpdateResults{}
|
||||
results.Record, err = h.rec.Update(ctx, args.Record)
|
||||
return
|
||||
}
|
||||
|
||||
func (h recordsHandler) delete(ctx context.Context, args *recordsDeleteArgs) error {
|
||||
if rec, err := h.lookupRecord(ctx, args); err != nil {
|
||||
return err
|
||||
} else {
|
||||
return h.rec.DeleteByID(ctx, rec.NamespaceID, rec.ModuleID, rec.ID)
|
||||
}
|
||||
}
|
||||
|
||||
func (h recordsHandler) lookupRecord(ctx context.Context, args recordLookup) (record *types.Record, err error) {
|
||||
var (
|
||||
namespace *types.Namespace
|
||||
module *types.Module
|
||||
recordID uint64
|
||||
)
|
||||
|
||||
if _, recordID, record = args.GetRecord(); record != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if namespace, module, err = h.loadCombo(ctx, args); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
return h.rec.FindByID(ctx, namespace.ID, module.ID, recordID)
|
||||
}
|
||||
|
||||
func (h recordsHandler) loadCombo(ctx context.Context, args interface{}) (namespace *types.Namespace, module *types.Module, err error) {
|
||||
if lkp, is := args.(namespaceLookup); is {
|
||||
if namespace, err = lookupNamespace(ctx, h.ns, lkp); err != nil {
|
||||
err = fmt.Errorf("could not load namespace: %w", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("could not extract namespace lookup arguments")
|
||||
return
|
||||
}
|
||||
|
||||
if lkp, is := args.(moduleLookup); is {
|
||||
if module, err = lookupModule(ctx, h.ns, h.mod, lkp); err != nil {
|
||||
err = fmt.Errorf("could not load module: %w", err)
|
||||
return
|
||||
}
|
||||
} else {
|
||||
err = fmt.Errorf("could not extract module lookup arguments")
|
||||
return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (i *recordSetIterator) More(context.Context, *Vars) (bool, error) {
|
||||
return i.ptr < len(i.set), nil
|
||||
}
|
||||
|
||||
func (i *recordSetIterator) Start(context.Context, *Vars) error { i.ptr = 0; return nil }
|
||||
|
||||
func (i *recordSetIterator) Next(context.Context, *Vars) (*Vars, error) {
|
||||
out := RVars{
|
||||
"record": Must(NewComposeRecord(i.set[i.ptr])),
|
||||
"index": Must(NewUnsignedInteger(i.ptr)),
|
||||
"total": Must(NewUnsignedInteger(i.filter.Total)),
|
||||
}
|
||||
|
||||
i.ptr++
|
||||
return out.Vars(), nil
|
||||
}
|
||||
@@ -0,0 +1,170 @@
|
||||
prefix: compose
|
||||
|
||||
imports:
|
||||
- github.com/cortezaproject/corteza-server/compose/types
|
||||
|
||||
params:
|
||||
moduleLookup: &moduleLookup
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, }
|
||||
- { wf: Handle, }
|
||||
- { wf: ComposeModule, suffix: res }
|
||||
meta:
|
||||
label: Module to set record type
|
||||
description: |-
|
||||
Even with unique record ID across all modules, module needs to be known
|
||||
before doing any record operations. Mainly because records of different
|
||||
modules can be located in different stores.
|
||||
|
||||
namespaceLookup: &namespaceLookup
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, }
|
||||
- { wf: Handle, }
|
||||
- { wf: ComposeNamespace, suffix: res }
|
||||
|
||||
recordLookup: &recordLookup
|
||||
required: true
|
||||
types:
|
||||
- { wf: ID, }
|
||||
- { wf: ComposeRecord, suffix: res }
|
||||
|
||||
record: &record
|
||||
required: true
|
||||
types:
|
||||
- { wf: ComposeRecord, }
|
||||
|
||||
rvRecord: &rvRecord
|
||||
wf: ComposeRecord
|
||||
|
||||
rvTotal: &rvTotal
|
||||
wf: UnsignedInteger
|
||||
meta:
|
||||
label: Total records found
|
||||
description: |-
|
||||
Total items that satisfy given conditions.
|
||||
|
||||
Needs to be explicitly requested with incTotal argument
|
||||
|
||||
rvIndex: &rvIndex
|
||||
wf: UnsignedInteger
|
||||
meta:
|
||||
label: Iteration counter
|
||||
description: |-
|
||||
Zero-based number iteration counter
|
||||
|
||||
rvPageCursor: &rvPageCursor
|
||||
wf: String
|
||||
|
||||
filterParams: &filterParams
|
||||
module: *moduleLookup
|
||||
namespace: *namespaceLookup
|
||||
query:
|
||||
types:
|
||||
- { wf: String }
|
||||
labels:
|
||||
types:
|
||||
- { wf: KV }
|
||||
deleted:
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
sort:
|
||||
types:
|
||||
- { wf: String }
|
||||
limit:
|
||||
types:
|
||||
- { wf: UnsignedInteger }
|
||||
incTotal:
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
incPageNavigation:
|
||||
types:
|
||||
- { wf: Boolean }
|
||||
pageCursor:
|
||||
types:
|
||||
- { wf: String }
|
||||
|
||||
functions:
|
||||
lookup:
|
||||
meta:
|
||||
short: Lookup for compose record by ID
|
||||
params:
|
||||
module: *moduleLookup
|
||||
namespace: *namespaceLookup
|
||||
record: *recordLookup
|
||||
results:
|
||||
record: *rvRecord
|
||||
|
||||
search:
|
||||
meta:
|
||||
short: Searches for records and returns them
|
||||
params: *filterParams
|
||||
results:
|
||||
records:
|
||||
<<: *rvRecord
|
||||
isArray: true
|
||||
total: *rvTotal
|
||||
pageCursor: *rvPageCursor
|
||||
|
||||
each:
|
||||
kind: iterator
|
||||
meta:
|
||||
short: Searches for records and iterates over results
|
||||
params: *filterParams
|
||||
results:
|
||||
record: *rvRecord
|
||||
index: *rvIndex
|
||||
total: *rvTotal
|
||||
|
||||
new:
|
||||
meta:
|
||||
short: Make a new record
|
||||
params:
|
||||
module: *moduleLookup
|
||||
namespace: *namespaceLookup
|
||||
results:
|
||||
record: *rvRecord
|
||||
|
||||
validate:
|
||||
meta:
|
||||
short: Validate record
|
||||
params:
|
||||
record: *record
|
||||
results:
|
||||
valid:
|
||||
wf: Boolean
|
||||
meta:
|
||||
label: Set to true when record is valid
|
||||
|
||||
create:
|
||||
meta:
|
||||
short: Creates and stores a new record
|
||||
params:
|
||||
record:
|
||||
<<: *record
|
||||
required: true
|
||||
results:
|
||||
record: *rvRecord
|
||||
|
||||
update:
|
||||
meta:
|
||||
short: Updates an existing record
|
||||
params:
|
||||
record:
|
||||
<<: *record
|
||||
required: true
|
||||
results:
|
||||
record: *rvRecord
|
||||
|
||||
delete:
|
||||
meta:
|
||||
short: Soft deletes compose record by ID
|
||||
params:
|
||||
record: *recordLookup
|
||||
|
||||
# undelete:
|
||||
# meta:
|
||||
# short: Recovers deleted record by ID
|
||||
# params:
|
||||
# record: *recordLookup
|
||||
@@ -84,6 +84,8 @@ type (
|
||||
Update(ctx context.Context, record *types.Record) (*types.Record, error)
|
||||
Bulk(ctx context.Context, oo ...*types.RecordBulkOperation) (types.RecordSet, error)
|
||||
|
||||
Validate(ctx context.Context, rec *types.Record) error
|
||||
|
||||
DeleteByID(ctx context.Context, namespaceID, moduleID uint64, recordID ...uint64) error
|
||||
|
||||
Organize(ctx context.Context, namespaceID, moduleID, recordID uint64, sortingField, sortingValue, sortingFilter, valueField, value string) error
|
||||
@@ -1120,6 +1122,15 @@ func (svc record) Organize(ctx context.Context, namespaceID, moduleID, recordID
|
||||
return svc.recordAction(ctx, aProps, RecordActionOrganize, err)
|
||||
}
|
||||
|
||||
func (svc record) Validate(ctx context.Context, rec *types.Record) error {
|
||||
if m, err := loadModule(ctx, svc.store, rec.ModuleID); err != nil {
|
||||
return err
|
||||
} else {
|
||||
rec.Values = values.Sanitizer().Run(m, rec.Values)
|
||||
return values.Validator().Run(ctx, svc.store, m, rec)
|
||||
}
|
||||
}
|
||||
|
||||
// TriggerScript loads requested record sanitizes and validates values and passes all to the automation script
|
||||
//
|
||||
// For backward compatibility (of controllers), it returns module+record
|
||||
|
||||
@@ -3,6 +3,8 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
automationService "github.com/cortezaproject/corteza-server/automation/service"
|
||||
"github.com/cortezaproject/corteza-server/compose/automation"
|
||||
"github.com/cortezaproject/corteza-server/compose/types"
|
||||
"github.com/cortezaproject/corteza-server/pkg/actionlog"
|
||||
"github.com/cortezaproject/corteza-server/pkg/corredor"
|
||||
@@ -151,6 +153,31 @@ func Initialize(ctx context.Context, log *zap.Logger, s store.Storer, c Config)
|
||||
|
||||
RegisterIteratorProviders()
|
||||
|
||||
automationService.Registry().AddTypes(
|
||||
automation.ComposeNamespace{},
|
||||
automation.ComposeModule{},
|
||||
automation.ComposeRecord{},
|
||||
automation.ComposeRecordValues{},
|
||||
)
|
||||
|
||||
automation.RecordsHandler(
|
||||
automationService.Registry(),
|
||||
DefaultNamespace,
|
||||
DefaultModule,
|
||||
DefaultRecord,
|
||||
)
|
||||
|
||||
automation.ModulesHandler(
|
||||
automationService.Registry(),
|
||||
DefaultNamespace,
|
||||
DefaultModule,
|
||||
)
|
||||
|
||||
automation.NamespacesHandler(
|
||||
automationService.Registry(),
|
||||
DefaultNamespace,
|
||||
)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,10 @@ func (r *Record) SetModule(m *Module) {
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Record) GetModule() *Module {
|
||||
return r.module
|
||||
}
|
||||
|
||||
func (r Record) Clone() *Record {
|
||||
c := &r
|
||||
c.Values = r.Values.Clone()
|
||||
|
||||
Reference in New Issue
Block a user