From a7e2eec9fd6424c2a1ef84632a080b34f425fe22 Mon Sep 17 00:00:00 2001 From: Denis Arh Date: Tue, 13 Jul 2021 16:39:12 +0200 Subject: [PATCH] Improve expr types, add r/w locking --- automation/automation/expr_types.gen.go | 26 +- automation/service/session.go | 5 +- automation/types/session.go | 13 + compose/automation/expr_types.gen.go | 133 +++++--- compose/automation/expr_types.go | 13 +- compose/automation/expr_types_test.go | 8 +- pkg/codegen/assets/expr_types.gen.go.tpl | 40 ++- pkg/expr/expr_types.gen.go | 376 ++++++++++++++++------- pkg/expr/expr_types.go | 4 +- pkg/expr/expr_types_test.go | 55 ++-- pkg/expr/parser.go | 2 +- pkg/expr/vars.go | 103 +++++-- pkg/wfexec/session.go | 8 +- pkg/wfexec/session_test.go | 2 +- system/automation/expr_types.gen.go | 311 ++++++++++++++----- 15 files changed, 795 insertions(+), 304 deletions(-) diff --git a/automation/automation/expr_types.gen.go b/automation/automation/expr_types.gen.go index 67b7c1e4d..b96cb0580 100644 --- a/automation/automation/expr_types.gen.go +++ b/automation/automation/expr_types.gen.go @@ -12,13 +12,17 @@ import ( "context" "fmt" . "github.com/cortezaproject/corteza-server/pkg/expr" + "sync" ) var _ = context.Background var _ = fmt.Errorf // EmailMessage is an expression type, wrapper for *emailMessage type -type EmailMessage struct{ value *emailMessage } +type EmailMessage struct { + value *emailMessage + mux sync.RWMutex +} // NewEmailMessage creates new instance of EmailMessage expression type func NewEmailMessage(val interface{}) (*EmailMessage, error) { @@ -29,16 +33,24 @@ func NewEmailMessage(val interface{}) (*EmailMessage, error) { } } -// Return underlying value on EmailMessage -func (t EmailMessage) Get() interface{} { return t.value } +// Get return underlying value on EmailMessage +func (t *EmailMessage) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on EmailMessage -func (t EmailMessage) GetValue() *emailMessage { return t.value } +// GetValue returns underlying value on EmailMessage +func (t *EmailMessage) GetValue() *emailMessage { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (EmailMessage) Type() string { return "EmailMessage" } -// Convert value to *emailMessage +// Cast converts value to *emailMessage func (EmailMessage) Cast(val interface{}) (TypedValue, error) { return NewEmailMessage(val) } diff --git a/automation/service/session.go b/automation/service/session.go index 83d7afd5d..5f0cf36c8 100644 --- a/automation/service/session.go +++ b/automation/service/session.go @@ -416,10 +416,7 @@ func (svc *session) stateChangeHandler(ctx context.Context) wfexec.StateChangeHa return } - if ses.Stacktrace != nil || ses.Error != "" { - // Save stacktrace when we know we're tracing workflows OR whenever there is an error... - ses.Stacktrace = ses.RuntimeStacktrace - } + ses.CopyRuntimeStacktrace() if err := svc.store.UpsertAutomationSession(ctx, ses); err != nil { log.Error("failed to update session", zap.Error(err)) diff --git a/automation/types/session.go b/automation/types/session.go index fa923d1d8..6ab3faf07 100644 --- a/automation/types/session.go +++ b/automation/types/session.go @@ -124,6 +124,9 @@ func (s *Session) GC() bool { // WaitResults wait blocks until workflow session is completed or fails (or context is canceled) and returns resuts func (s *Session) WaitResults(ctx context.Context) (*expr.Vars, wfexec.SessionStatus, Stacktrace, error) { + s.l.RLock() + defer s.l.RUnlock() + if err := s.session.WaitUntil(ctx, wfexec.SessionFailed, wfexec.SessionCompleted); err != nil { return nil, -1, s.Stacktrace, err } @@ -152,6 +155,16 @@ func (s *Session) Apply(ssp SessionStartParams) { } } +func (s *Session) CopyRuntimeStacktrace() { + s.l.Lock() + defer s.l.Unlock() + + if s.Stacktrace != nil || s.Error != "" { + // Save stacktrace when we know we're tracing workflows OR whenever there is an error... + s.Stacktrace = s.RuntimeStacktrace + } +} + func (set *Stacktrace) Scan(value interface{}) error { //lint:ignore S1034 This typecast is intentional, we need to get []byte out of a []uint8 switch value.(type) { diff --git a/compose/automation/expr_types.gen.go b/compose/automation/expr_types.gen.go index a9ad407ed..b8225d66a 100644 --- a/compose/automation/expr_types.gen.go +++ b/compose/automation/expr_types.gen.go @@ -13,13 +13,17 @@ import ( "fmt" "github.com/cortezaproject/corteza-server/compose/types" . "github.com/cortezaproject/corteza-server/pkg/expr" + "sync" ) var _ = context.Background var _ = fmt.Errorf // ComposeModule is an expression type, wrapper for *types.Module type -type ComposeModule struct{ value *types.Module } +type ComposeModule struct { + value *types.Module + mux sync.RWMutex +} // NewComposeModule creates new instance of ComposeModule expression type func NewComposeModule(val interface{}) (*ComposeModule, error) { @@ -30,16 +34,24 @@ func NewComposeModule(val interface{}) (*ComposeModule, error) { } } -// Return underlying value on ComposeModule -func (t ComposeModule) Get() interface{} { return t.value } +// Get return underlying value on ComposeModule +func (t *ComposeModule) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on ComposeModule -func (t ComposeModule) GetValue() *types.Module { return t.value } +// GetValue returns underlying value on ComposeModule +func (t *ComposeModule) GetValue() *types.Module { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (ComposeModule) Type() string { return "ComposeModule" } -// Convert value to *types.Module +// Cast converts value to *types.Module func (ComposeModule) Cast(val interface{}) (TypedValue, error) { return NewComposeModule(val) } @@ -57,6 +69,8 @@ func (t *ComposeModule) Assign(val interface{}) error { } func (t *ComposeModule) AssignFieldValue(key string, val TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() return assignToComposeModule(t.value, key, val) } @@ -65,18 +79,24 @@ func (t *ComposeModule) AssignFieldValue(key string, val TypedValue) error { // 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) { +func (t *ComposeModule) SelectGVal(ctx context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() 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) { +func (t *ComposeModule) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() return composeModuleTypedValueSelector(t.value, k) } -func (t ComposeModule) Has(k string) bool { +func (t *ComposeModule) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { case "ID", "moduleID": return true @@ -195,7 +215,10 @@ func assignToComposeModule(res *types.Module, k string, val interface{}) error { } // ComposeNamespace is an expression type, wrapper for *types.Namespace type -type ComposeNamespace struct{ value *types.Namespace } +type ComposeNamespace struct { + value *types.Namespace + mux sync.RWMutex +} // NewComposeNamespace creates new instance of ComposeNamespace expression type func NewComposeNamespace(val interface{}) (*ComposeNamespace, error) { @@ -206,16 +229,24 @@ func NewComposeNamespace(val interface{}) (*ComposeNamespace, error) { } } -// Return underlying value on ComposeNamespace -func (t ComposeNamespace) Get() interface{} { return t.value } +// Get return underlying value on ComposeNamespace +func (t *ComposeNamespace) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on ComposeNamespace -func (t ComposeNamespace) GetValue() *types.Namespace { return t.value } +// GetValue returns underlying value on ComposeNamespace +func (t *ComposeNamespace) GetValue() *types.Namespace { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (ComposeNamespace) Type() string { return "ComposeNamespace" } -// Convert value to *types.Namespace +// Cast converts value to *types.Namespace func (ComposeNamespace) Cast(val interface{}) (TypedValue, error) { return NewComposeNamespace(val) } @@ -233,6 +264,8 @@ func (t *ComposeNamespace) Assign(val interface{}) error { } func (t *ComposeNamespace) AssignFieldValue(key string, val TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() return assignToComposeNamespace(t.value, key, val) } @@ -241,18 +274,24 @@ func (t *ComposeNamespace) AssignFieldValue(key string, val TypedValue) error { // 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) { +func (t *ComposeNamespace) SelectGVal(ctx context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() 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) { +func (t *ComposeNamespace) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() return composeNamespaceTypedValueSelector(t.value, k) } -func (t ComposeNamespace) Has(k string) bool { +func (t *ComposeNamespace) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { case "ID", "namespaceID": return true @@ -363,7 +402,10 @@ func assignToComposeNamespace(res *types.Namespace, k string, val interface{}) e } // ComposeRecord is an expression type, wrapper for *types.Record type -type ComposeRecord struct{ value *types.Record } +type ComposeRecord struct { + value *types.Record + mux sync.RWMutex +} // NewComposeRecord creates new instance of ComposeRecord expression type func NewComposeRecord(val interface{}) (*ComposeRecord, error) { @@ -374,16 +416,24 @@ func NewComposeRecord(val interface{}) (*ComposeRecord, error) { } } -// Return underlying value on ComposeRecord -func (t ComposeRecord) Get() interface{} { return t.value } +// Get return underlying value on ComposeRecord +func (t *ComposeRecord) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on ComposeRecord -func (t ComposeRecord) GetValue() *types.Record { return t.value } +// GetValue returns underlying value on ComposeRecord +func (t *ComposeRecord) GetValue() *types.Record { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (ComposeRecord) Type() string { return "ComposeRecord" } -// Convert value to *types.Record +// Cast converts value to *types.Record func (ComposeRecord) Cast(val interface{}) (TypedValue, error) { return NewComposeRecord(val) } @@ -400,7 +450,9 @@ func (t *ComposeRecord) Assign(val interface{}) error { } } -func (t ComposeRecord) Has(k string) bool { +func (t *ComposeRecord) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { case "ID", "recordID": return true @@ -551,7 +603,10 @@ func assignToComposeRecord(res *types.Record, k string, val interface{}) error { } // ComposeRecordValueErrorSet is an expression type, wrapper for *types.RecordValueErrorSet type -type ComposeRecordValueErrorSet struct{ value *types.RecordValueErrorSet } +type ComposeRecordValueErrorSet struct { + value *types.RecordValueErrorSet + mux sync.RWMutex +} // NewComposeRecordValueErrorSet creates new instance of ComposeRecordValueErrorSet expression type func NewComposeRecordValueErrorSet(val interface{}) (*ComposeRecordValueErrorSet, error) { @@ -562,16 +617,24 @@ func NewComposeRecordValueErrorSet(val interface{}) (*ComposeRecordValueErrorSet } } -// Return underlying value on ComposeRecordValueErrorSet -func (t ComposeRecordValueErrorSet) Get() interface{} { return t.value } +// Get return underlying value on ComposeRecordValueErrorSet +func (t *ComposeRecordValueErrorSet) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on ComposeRecordValueErrorSet -func (t ComposeRecordValueErrorSet) GetValue() *types.RecordValueErrorSet { return t.value } +// GetValue returns underlying value on ComposeRecordValueErrorSet +func (t *ComposeRecordValueErrorSet) GetValue() *types.RecordValueErrorSet { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (ComposeRecordValueErrorSet) Type() string { return "ComposeRecordValueErrorSet" } -// Convert value to *types.RecordValueErrorSet +// Cast converts value to *types.RecordValueErrorSet func (ComposeRecordValueErrorSet) Cast(val interface{}) (TypedValue, error) { return NewComposeRecordValueErrorSet(val) } diff --git a/compose/automation/expr_types.go b/compose/automation/expr_types.go index ef8102b07..a93ba1634 100644 --- a/compose/automation/expr_types.go +++ b/compose/automation/expr_types.go @@ -98,6 +98,9 @@ var _ expr.DeepFieldAssigner = &ComposeRecord{} // We need to reroute value assigning for record-value-sets because // we loose the reference to record-value slice func (t *ComposeRecord) AssignFieldValue(kk []string, val expr.TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() + switch kk[0] { case "values": return assignToComposeRecordValues(t.value, kk[1:], val) @@ -115,7 +118,10 @@ var _ gval.Selector = &ComposeRecord{} // It allows gval lib to access Record's underlying value (*types.Record) // and it's fields // -func (t ComposeRecord) SelectGVal(_ context.Context, k string) (interface{}, error) { +func (t *ComposeRecord) SelectGVal(_ context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() + if t.value != nil && k == "values" { if t.value.Values == nil { t.value.Values = types.RecordValueSet{} @@ -134,7 +140,10 @@ func (t ComposeRecord) SelectGVal(_ context.Context, k string) (interface{}, err // Select is field accessor for *types.ComposeRecord // // Similar to SelectGVal but returns typed values -func (t ComposeRecord) Select(k string) (expr.TypedValue, error) { +func (t *ComposeRecord) Select(k string) (expr.TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() + if t.value != nil && k == "values" { if t.value.Values == nil { t.value.Values = types.RecordValueSet{} diff --git a/compose/automation/expr_types_test.go b/compose/automation/expr_types_test.go index 1f2b5c997..0c7f62e4b 100644 --- a/compose/automation/expr_types_test.go +++ b/compose/automation/expr_types_test.go @@ -69,13 +69,13 @@ func TestRecordFieldValuesAccess(t *testing.T) { // same as &ComposeRecord{value: &types.Record{}}, "validRecZero": &ComposeRecord{value: &types.Record{ID: 0, Values: types.RecordValueSet{}}}, - "validRecValidID": &ComposeRecord{&types.Record{ID: 42, Values: types.RecordValueSet{}}}, + "validRecValidID": &ComposeRecord{value: &types.Record{ID: 42, Values: types.RecordValueSet{}}}, // "record" (not really) set to nil "fooRec": nil, // record with id and value set (this was) fixme - "record": &ComposeRecord{&types.Record{ID: 99, Values: rawValues}}, + "record": &ComposeRecord{value: &types.Record{ID: 99, Values: rawValues}}, }) ) @@ -139,8 +139,8 @@ func TestRecordFieldValuesAccess(t *testing.T) { e.SetEval(evaluable) input, _ := expr.NewVars(map[string]expr.TypedValue{ - "record": &ComposeRecord{inputRec}, - "recordClone": &ComposeRecord{cloneRec}, + "record": &ComposeRecord{value: inputRec}, + "recordClone": &ComposeRecord{value: cloneRec}, }) output, err = (aTypes.ExprSet{e}).Eval(context.Background(), input) diff --git a/pkg/codegen/assets/expr_types.gen.go.tpl b/pkg/codegen/assets/expr_types.gen.go.tpl index 6e3653355..f3dedee19 100644 --- a/pkg/codegen/assets/expr_types.gen.go.tpl +++ b/pkg/codegen/assets/expr_types.gen.go.tpl @@ -11,6 +11,7 @@ package {{ .Package }} import ( "context" "fmt" + "sync" {{- range .Imports }} {{ normalizeImport . }} {{- end }} @@ -25,7 +26,10 @@ var _ = fmt.Errorf {{ range $exprType, $def := .Types }} {{ if not $def.CustomType }} // {{ $exprType }} is an expression type, wrapper for {{ $def.As }} type -type {{ $exprType }} struct{ value {{ $def.As }} } +type {{ $exprType }} struct{ + value {{ $def.As }} + mux sync.RWMutex +} // New{{ $exprType }} creates new instance of {{ $exprType }} expression type func New{{ $exprType }}(val interface{}) (*{{ $exprType }}, error) { @@ -37,16 +41,24 @@ func New{{ $exprType }}(val interface{}) (*{{ $exprType }}, error) { } -// Return underlying value on {{ $exprType }} -func (t {{ $exprType }}) Get() interface{} { return t.value } +// Get return underlying value on {{ $exprType }} +func (t *{{ $exprType }}) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on {{ $exprType }} -func (t {{ $exprType }}) GetValue() {{ $def.As }} { return t.value } +// GetValue returns underlying value on {{ $exprType }} +func (t *{{ $exprType }}) GetValue() {{ $def.As }} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func ({{ $exprType }}) Type() string { return "{{ $exprType }}" } -// Convert value to {{ $def.As }} +// Cast converts value to {{ $def.As }} func ({{ $exprType }}) Cast(val interface{}) (TypedValue, error) { return New{{ $exprType }}(val) } @@ -67,6 +79,8 @@ func (t *{{ $exprType }}) Assign(val interface{}) (error) { {{ if $def.Struct }} {{ if not $def.CustomFieldAssigner }} func (t *{{ $exprType }}) AssignFieldValue(key string, val TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() return {{ $def.AssignerFn }}(t.value, key, val) } {{ end }} @@ -77,7 +91,9 @@ func (t *{{ $exprType }}) AssignFieldValue(key string, val TypedValue) error { // It allows gval lib to access {{ $exprType }}'s underlying value ({{ $def.As }}) // and it's fields // -func (t {{ $exprType }}) SelectGVal(ctx context.Context, k string) (interface{}, error) { +func (t *{{ $exprType }}) SelectGVal(ctx context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() return {{ unexport $exprType "GValSelector" }}(t.value, k) } {{ end }} @@ -86,12 +102,16 @@ func (t {{ $exprType }}) SelectGVal(ctx context.Context, k string) (interface{}, // Select is field accessor for {{ $def.As }} // // Similar to SelectGVal but returns typed values -func (t {{ $exprType }}) Select(k string) (TypedValue, error) { +func (t *{{ $exprType }}) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() return {{ unexport $exprType "TypedValueSelector" }}(t.value, k) } {{ end }} -func (t {{ $exprType }}) Has(k string) bool { +func (t *{{ $exprType }}) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { {{- range $def.Struct }} {{- if .ExprType }} diff --git a/pkg/expr/expr_types.gen.go b/pkg/expr/expr_types.gen.go index aa2cb2861..c10c9b86f 100644 --- a/pkg/expr/expr_types.gen.go +++ b/pkg/expr/expr_types.gen.go @@ -12,6 +12,7 @@ import ( "context" "fmt" "io" + "sync" "time" ) @@ -19,7 +20,10 @@ var _ = context.Background var _ = fmt.Errorf // Any is an expression type, wrapper for interface{} type -type Any struct{ value interface{} } +type Any struct { + value interface{} + mux sync.RWMutex +} // NewAny creates new instance of Any expression type func NewAny(val interface{}) (*Any, error) { @@ -30,16 +34,24 @@ func NewAny(val interface{}) (*Any, error) { } } -// Return underlying value on Any -func (t Any) Get() interface{} { return t.value } +// Get return underlying value on Any +func (t *Any) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Any -func (t Any) GetValue() interface{} { return t.value } +// GetValue returns underlying value on Any +func (t *Any) GetValue() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Any) Type() string { return "Any" } -// Convert value to interface{} +// Cast converts value to interface{} func (Any) Cast(val interface{}) (TypedValue, error) { return NewAny(val) } @@ -57,7 +69,10 @@ func (t *Any) Assign(val interface{}) error { } // Array is an expression type, wrapper for []TypedValue type -type Array struct{ value []TypedValue } +type Array struct { + value []TypedValue + mux sync.RWMutex +} // NewArray creates new instance of Array expression type func NewArray(val interface{}) (*Array, error) { @@ -68,16 +83,24 @@ func NewArray(val interface{}) (*Array, error) { } } -// Return underlying value on Array -func (t Array) Get() interface{} { return t.value } +// Get return underlying value on Array +func (t *Array) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Array -func (t Array) GetValue() []TypedValue { return t.value } +// GetValue returns underlying value on Array +func (t *Array) GetValue() []TypedValue { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Array) Type() string { return "Array" } -// Convert value to []TypedValue +// Cast converts value to []TypedValue func (Array) Cast(val interface{}) (TypedValue, error) { return NewArray(val) } @@ -95,7 +118,10 @@ func (t *Array) Assign(val interface{}) error { } // Boolean is an expression type, wrapper for bool type -type Boolean struct{ value bool } +type Boolean struct { + value bool + mux sync.RWMutex +} // NewBoolean creates new instance of Boolean expression type func NewBoolean(val interface{}) (*Boolean, error) { @@ -106,16 +132,24 @@ func NewBoolean(val interface{}) (*Boolean, error) { } } -// Return underlying value on Boolean -func (t Boolean) Get() interface{} { return t.value } +// Get return underlying value on Boolean +func (t *Boolean) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Boolean -func (t Boolean) GetValue() bool { return t.value } +// GetValue returns underlying value on Boolean +func (t *Boolean) GetValue() bool { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Boolean) Type() string { return "Boolean" } -// Convert value to bool +// Cast converts value to bool func (Boolean) Cast(val interface{}) (TypedValue, error) { return NewBoolean(val) } @@ -133,7 +167,10 @@ func (t *Boolean) Assign(val interface{}) error { } // DateTime is an expression type, wrapper for *time.Time type -type DateTime struct{ value *time.Time } +type DateTime struct { + value *time.Time + mux sync.RWMutex +} // NewDateTime creates new instance of DateTime expression type func NewDateTime(val interface{}) (*DateTime, error) { @@ -144,16 +181,24 @@ func NewDateTime(val interface{}) (*DateTime, error) { } } -// Return underlying value on DateTime -func (t DateTime) Get() interface{} { return t.value } +// Get return underlying value on DateTime +func (t *DateTime) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on DateTime -func (t DateTime) GetValue() *time.Time { return t.value } +// GetValue returns underlying value on DateTime +func (t *DateTime) GetValue() *time.Time { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (DateTime) Type() string { return "DateTime" } -// Convert value to *time.Time +// Cast converts value to *time.Time func (DateTime) Cast(val interface{}) (TypedValue, error) { return NewDateTime(val) } @@ -171,7 +216,10 @@ func (t *DateTime) Assign(val interface{}) error { } // Duration is an expression type, wrapper for time.Duration type -type Duration struct{ value time.Duration } +type Duration struct { + value time.Duration + mux sync.RWMutex +} // NewDuration creates new instance of Duration expression type func NewDuration(val interface{}) (*Duration, error) { @@ -182,16 +230,24 @@ func NewDuration(val interface{}) (*Duration, error) { } } -// Return underlying value on Duration -func (t Duration) Get() interface{} { return t.value } +// Get return underlying value on Duration +func (t *Duration) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Duration -func (t Duration) GetValue() time.Duration { return t.value } +// GetValue returns underlying value on Duration +func (t *Duration) GetValue() time.Duration { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Duration) Type() string { return "Duration" } -// Convert value to time.Duration +// Cast converts value to time.Duration func (Duration) Cast(val interface{}) (TypedValue, error) { return NewDuration(val) } @@ -209,7 +265,10 @@ func (t *Duration) Assign(val interface{}) error { } // Float is an expression type, wrapper for float64 type -type Float struct{ value float64 } +type Float struct { + value float64 + mux sync.RWMutex +} // NewFloat creates new instance of Float expression type func NewFloat(val interface{}) (*Float, error) { @@ -220,16 +279,24 @@ func NewFloat(val interface{}) (*Float, error) { } } -// Return underlying value on Float -func (t Float) Get() interface{} { return t.value } +// Get return underlying value on Float +func (t *Float) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Float -func (t Float) GetValue() float64 { return t.value } +// GetValue returns underlying value on Float +func (t *Float) GetValue() float64 { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Float) Type() string { return "Float" } -// Convert value to float64 +// Cast converts value to float64 func (Float) Cast(val interface{}) (TypedValue, error) { return NewFloat(val) } @@ -247,7 +314,10 @@ func (t *Float) Assign(val interface{}) error { } // Handle is an expression type, wrapper for string type -type Handle struct{ value string } +type Handle struct { + value string + mux sync.RWMutex +} // NewHandle creates new instance of Handle expression type func NewHandle(val interface{}) (*Handle, error) { @@ -258,16 +328,24 @@ func NewHandle(val interface{}) (*Handle, error) { } } -// Return underlying value on Handle -func (t Handle) Get() interface{} { return t.value } +// Get return underlying value on Handle +func (t *Handle) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Handle -func (t Handle) GetValue() string { return t.value } +// GetValue returns underlying value on Handle +func (t *Handle) GetValue() string { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Handle) Type() string { return "Handle" } -// Convert value to string +// Cast converts value to string func (Handle) Cast(val interface{}) (TypedValue, error) { return NewHandle(val) } @@ -285,7 +363,10 @@ func (t *Handle) Assign(val interface{}) error { } // ID is an expression type, wrapper for uint64 type -type ID struct{ value uint64 } +type ID struct { + value uint64 + mux sync.RWMutex +} // NewID creates new instance of ID expression type func NewID(val interface{}) (*ID, error) { @@ -296,16 +377,24 @@ func NewID(val interface{}) (*ID, error) { } } -// Return underlying value on ID -func (t ID) Get() interface{} { return t.value } +// Get return underlying value on ID +func (t *ID) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on ID -func (t ID) GetValue() uint64 { return t.value } +// GetValue returns underlying value on ID +func (t *ID) GetValue() uint64 { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (ID) Type() string { return "ID" } -// Convert value to uint64 +// Cast converts value to uint64 func (ID) Cast(val interface{}) (TypedValue, error) { return NewID(val) } @@ -323,7 +412,10 @@ func (t *ID) Assign(val interface{}) error { } // Integer is an expression type, wrapper for int64 type -type Integer struct{ value int64 } +type Integer struct { + value int64 + mux sync.RWMutex +} // NewInteger creates new instance of Integer expression type func NewInteger(val interface{}) (*Integer, error) { @@ -334,16 +426,24 @@ func NewInteger(val interface{}) (*Integer, error) { } } -// Return underlying value on Integer -func (t Integer) Get() interface{} { return t.value } +// Get return underlying value on Integer +func (t *Integer) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Integer -func (t Integer) GetValue() int64 { return t.value } +// GetValue returns underlying value on Integer +func (t *Integer) GetValue() int64 { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Integer) Type() string { return "Integer" } -// Convert value to int64 +// Cast converts value to int64 func (Integer) Cast(val interface{}) (TypedValue, error) { return NewInteger(val) } @@ -361,7 +461,10 @@ func (t *Integer) Assign(val interface{}) error { } // KV is an expression type, wrapper for map[string]string type -type KV struct{ value map[string]string } +type KV struct { + value map[string]string + mux sync.RWMutex +} // NewKV creates new instance of KV expression type func NewKV(val interface{}) (*KV, error) { @@ -372,16 +475,24 @@ func NewKV(val interface{}) (*KV, error) { } } -// Return underlying value on KV -func (t KV) Get() interface{} { return t.value } +// Get return underlying value on KV +func (t *KV) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on KV -func (t KV) GetValue() map[string]string { return t.value } +// GetValue returns underlying value on KV +func (t *KV) GetValue() map[string]string { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (KV) Type() string { return "KV" } -// Convert value to map[string]string +// Cast converts value to map[string]string func (KV) Cast(val interface{}) (TypedValue, error) { return NewKV(val) } @@ -399,7 +510,10 @@ func (t *KV) Assign(val interface{}) error { } // KVV is an expression type, wrapper for map[string][]string type -type KVV struct{ value map[string][]string } +type KVV struct { + value map[string][]string + mux sync.RWMutex +} // NewKVV creates new instance of KVV expression type func NewKVV(val interface{}) (*KVV, error) { @@ -410,16 +524,24 @@ func NewKVV(val interface{}) (*KVV, error) { } } -// Return underlying value on KVV -func (t KVV) Get() interface{} { return t.value } +// Get return underlying value on KVV +func (t *KVV) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on KVV -func (t KVV) GetValue() map[string][]string { return t.value } +// GetValue returns underlying value on KVV +func (t *KVV) GetValue() map[string][]string { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (KVV) Type() string { return "KVV" } -// Convert value to map[string][]string +// Cast converts value to map[string][]string func (KVV) Cast(val interface{}) (TypedValue, error) { return NewKVV(val) } @@ -437,7 +559,10 @@ func (t *KVV) Assign(val interface{}) error { } // Reader is an expression type, wrapper for io.Reader type -type Reader struct{ value io.Reader } +type Reader struct { + value io.Reader + mux sync.RWMutex +} // NewReader creates new instance of Reader expression type func NewReader(val interface{}) (*Reader, error) { @@ -448,16 +573,24 @@ func NewReader(val interface{}) (*Reader, error) { } } -// Return underlying value on Reader -func (t Reader) Get() interface{} { return t.value } +// Get return underlying value on Reader +func (t *Reader) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Reader -func (t Reader) GetValue() io.Reader { return t.value } +// GetValue returns underlying value on Reader +func (t *Reader) GetValue() io.Reader { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Reader) Type() string { return "Reader" } -// Convert value to io.Reader +// Cast converts value to io.Reader func (Reader) Cast(val interface{}) (TypedValue, error) { return NewReader(val) } @@ -475,7 +608,10 @@ func (t *Reader) Assign(val interface{}) error { } // String is an expression type, wrapper for string type -type String struct{ value string } +type String struct { + value string + mux sync.RWMutex +} // NewString creates new instance of String expression type func NewString(val interface{}) (*String, error) { @@ -486,16 +622,24 @@ func NewString(val interface{}) (*String, error) { } } -// Return underlying value on String -func (t String) Get() interface{} { return t.value } +// Get return underlying value on String +func (t *String) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on String -func (t String) GetValue() string { return t.value } +// GetValue returns underlying value on String +func (t *String) GetValue() string { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (String) Type() string { return "String" } -// Convert value to string +// Cast converts value to string func (String) Cast(val interface{}) (TypedValue, error) { return NewString(val) } @@ -513,7 +657,10 @@ func (t *String) Assign(val interface{}) error { } // UnsignedInteger is an expression type, wrapper for uint64 type -type UnsignedInteger struct{ value uint64 } +type UnsignedInteger struct { + value uint64 + mux sync.RWMutex +} // NewUnsignedInteger creates new instance of UnsignedInteger expression type func NewUnsignedInteger(val interface{}) (*UnsignedInteger, error) { @@ -524,16 +671,24 @@ func NewUnsignedInteger(val interface{}) (*UnsignedInteger, error) { } } -// Return underlying value on UnsignedInteger -func (t UnsignedInteger) Get() interface{} { return t.value } +// Get return underlying value on UnsignedInteger +func (t *UnsignedInteger) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on UnsignedInteger -func (t UnsignedInteger) GetValue() uint64 { return t.value } +// GetValue returns underlying value on UnsignedInteger +func (t *UnsignedInteger) GetValue() uint64 { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (UnsignedInteger) Type() string { return "UnsignedInteger" } -// Convert value to uint64 +// Cast converts value to uint64 func (UnsignedInteger) Cast(val interface{}) (TypedValue, error) { return NewUnsignedInteger(val) } @@ -551,7 +706,10 @@ func (t *UnsignedInteger) Assign(val interface{}) error { } // Vars is an expression type, wrapper for map[string]TypedValue type -type Vars struct{ value map[string]TypedValue } +type Vars struct { + value map[string]TypedValue + mux sync.RWMutex +} // NewVars creates new instance of Vars expression type func NewVars(val interface{}) (*Vars, error) { @@ -562,16 +720,24 @@ func NewVars(val interface{}) (*Vars, error) { } } -// Return underlying value on Vars -func (t Vars) Get() interface{} { return t.value } +// Get return underlying value on Vars +func (t *Vars) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Vars -func (t Vars) GetValue() map[string]TypedValue { return t.value } +// GetValue returns underlying value on Vars +func (t *Vars) GetValue() map[string]TypedValue { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Vars) Type() string { return "Vars" } -// Convert value to map[string]TypedValue +// Cast converts value to map[string]TypedValue func (Vars) Cast(val interface{}) (TypedValue, error) { return NewVars(val) } diff --git a/pkg/expr/expr_types.go b/pkg/expr/expr_types.go index cf09a4a03..e896bd7e2 100644 --- a/pkg/expr/expr_types.go +++ b/pkg/expr/expr_types.go @@ -137,10 +137,10 @@ func Typify(in interface{}) (tv TypedValue, err error) { if v, err := CastToVars(c); err != nil { return nil, err } else { - return &Vars{v}, nil + return &Vars{value: v}, nil } case map[string]TypedValue: - return &Vars{c}, nil + return &Vars{value: c}, nil case map[string]string: return &KV{value: c}, nil case map[string][]string: diff --git a/pkg/expr/expr_types_test.go b/pkg/expr/expr_types_test.go index b92a0d03c..539da9219 100644 --- a/pkg/expr/expr_types_test.go +++ b/pkg/expr/expr_types_test.go @@ -3,10 +3,11 @@ package expr import ( "context" "fmt" - "github.com/stretchr/testify/require" "net/http" "net/url" "testing" + + "github.com/stretchr/testify/require" ) func Example_set_kv() { @@ -20,11 +21,11 @@ func Example_set_kv() { ) eval(`set(kv, key, value)`, p) - fmt.Printf("\nOriginal KV should be %v", kv) + fmt.Printf("\nOriginal KV should be %v", kv.value) // output: - // &{value:map[k1:v11]} - // Original KV should be &{map[]} + // map[k1:v11] + // Original KV should be map[] } func Example_merge_kv() { @@ -42,11 +43,11 @@ func Example_merge_kv() { ) eval(`merge(kv, foo, bar)`, p) - fmt.Printf("\nOriginal KV should be %v", kv) + fmt.Printf("\nOriginal KV should be %v", kv.value) // output: - // &{value:map[k1:v1 k2:v2]} - // Original KV should be &{map[]} + // map[k1:v1 k2:v2] + // Original KV should be map[] } func Example_filter_kv() { @@ -63,11 +64,11 @@ func Example_filter_kv() { ) eval(`filter(kv, key1, key2)`, p) - fmt.Printf("\nOriginal KV should be %v", kv) + fmt.Printf("\nOriginal KV should be %v", kv.value) // output: - // &{value:map[k1:v1]} - // Original KV should be &{map[k1:v1 k2:v2]} + // map[k1:v1] + // Original KV should be map[k1:v1 k2:v2] } func Example_omit_kv() { @@ -85,11 +86,11 @@ func Example_omit_kv() { ) eval(`omit(kv, key1, key2)`, p) - fmt.Printf("\nOriginal KV should be %v", kv) + fmt.Printf("\nOriginal KV should be %v", kv.value) // output: - // &{value:map[k2:v2]} - // Original KV should be &{map[k1:v1 k2:v2 k3:v3]} + // map[k2:v2] + // Original KV should be map[k1:v1 k2:v2 k3:v3] } func Example_set_kvv() { @@ -103,11 +104,11 @@ func Example_set_kvv() { ) eval(`set(kvv, key, value)`, p) - fmt.Printf("\nOriginal KVV should be %v", kvv) + fmt.Printf("\nOriginal KVV should be %v", kvv.value) // output: - // &{value:map[foo:[bar]]} - // Original KVV should be &{map[]} + // map[foo:[bar]] + // Original KVV should be map[] } func Example_merge_kvv() { @@ -126,11 +127,11 @@ func Example_merge_kvv() { ) eval(`merge(kvv, foo, bar)`, p) - fmt.Printf("\nOriginal KVV should be %v", kvv) + fmt.Printf("\nOriginal KVV should be %v", kvv.value) // output: - // &{value:map[k1:[v1 v11] k2:[v2]]} - // Original KVV should be &{map[]} + // map[k1:[v1 v11] k2:[v2]] + // Original KVV should be map[] } func Example_filter_kvv() { @@ -147,11 +148,11 @@ func Example_filter_kvv() { ) eval(`filter(kv, key1, key2)`, p) - fmt.Printf("\nOriginal KVV should be %v", kvv) + fmt.Printf("\nOriginal KVV should be %v", kvv.value) // output: - // &{value:map[k1:[v1]]} - // Original KVV should be &{map[k1:[v1] k2:[v2]]} + // map[k1:[v1]] + // Original KVV should be map[k1:[v1] k2:[v2]] } func Example_omit_kvv() { @@ -169,11 +170,11 @@ func Example_omit_kvv() { ) eval(`omit(kvv, key1, key2)`, p) - fmt.Printf("\nOriginal KVV should be %v", kvv) + fmt.Printf("\nOriginal KVV should be %v", kvv.value) // output: - // &{value:map[k2:[v2]]} - // Original KVV should be &{map[k1:[v1] k2:[v2] k3:[v3]]} + // map[k2:[v2]] + // Original KVV should be map[k1:[v1] k2:[v2] k3:[v3]] } func TestTypedValueOperations(t *testing.T) { @@ -527,10 +528,10 @@ func TestArrayDecode(t *testing.T) { req.NoError(err) vars, err := NewVars(map[string]interface{}{ - "strings": &Array{arr}, + "strings": &Array{value: arr}, "iface": Must(NewString("typed")), "typed": Must(NewString("typed")), - "values": &Array{arr}, + "values": &Array{value: arr}, }) req.NoError(err) diff --git a/pkg/expr/parser.go b/pkg/expr/parser.go index ee91a103c..e9b4a9762 100644 --- a/pkg/expr/parser.go +++ b/pkg/expr/parser.go @@ -103,6 +103,6 @@ func eval(e string, p interface{}) { if err != nil { fmt.Printf("error: %v", err) } else { - fmt.Printf("%+v", result) + fmt.Printf("%+v", UntypedValue(result)) } } diff --git a/pkg/expr/vars.go b/pkg/expr/vars.go index 69073099a..a6c13f6a9 100644 --- a/pkg/expr/vars.go +++ b/pkg/expr/vars.go @@ -13,11 +13,17 @@ import ( "github.com/spf13/cast" ) -func (t Vars) Len() int { +func (t *Vars) Len() int { + t.mux.RLock() + defer t.mux.RUnlock() + return len(t.value) } -func (t Vars) Select(k string) (TypedValue, error) { +func (t *Vars) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() + if v, is := t.value[k]; is { return v, nil } else { @@ -26,6 +32,9 @@ func (t Vars) Select(k string) (TypedValue, error) { } func (t *Vars) AssignFieldValue(key string, val TypedValue) (err error) { + t.mux.Lock() + defer t.mux.Unlock() + if t.value == nil { t.value = make(map[string]TypedValue) } @@ -35,7 +44,14 @@ func (t *Vars) AssignFieldValue(key string, val TypedValue) (err error) { return err } -func (t Vars) ResolveTypes(res func(typ string) Type) (err error) { +func (t *Vars) ResolveTypes(res func(typ string) Type) (err error) { + if t == nil { + return nil + } + + t.mux.RLock() + defer t.mux.RUnlock() + for k, v := range t.value { if u, is := v.(*Unresolved); is { if res(u.Type()) == nil { @@ -61,28 +77,22 @@ func (t Vars) ResolveTypes(res func(typ string) Type) (err error) { // Merge combines the given Vars(es) into Vars // NOTE: It will return CLONE of the original Vars, if its called without any parameters func (t *Vars) Merge(nn ...Iterator) (out TypedValue, err error) { - vars := EmptyVars() - - nn = append([]Iterator{t}, nn...) - - for _, i := range nn { - _ = i.Each(func(k string, v TypedValue) error { - vars.value[k] = v - return nil - }) - } - - return vars, nil + return t.MustMerge(nn...), nil } // MustMerge returns Vars after merging the given Vars(es) into it func (t *Vars) MustMerge(nn ...Iterator) *Vars { + if t != nil { + t.mux.RLock() + defer t.mux.RUnlock() + + nn = append([]Iterator{t}, nn...) + } + var ( out = &Vars{value: make(map[string]TypedValue)} ) - nn = append([]Iterator{t}, nn...) - for _, i := range nn { _ = i.Each(func(k string, v TypedValue) error { out.value[k] = v @@ -99,6 +109,13 @@ func (t *Vars) Copy(dst *Vars, kk ...string) { return } + t.mux.RLock() + defer t.mux.RUnlock() + + if t == nil { + return + } + if dst.value == nil { dst.value = make(map[string]TypedValue) } @@ -119,6 +136,13 @@ func (t *Vars) HasAll(key string, kk ...string) bool { return false } + t.mux.RLock() + defer t.mux.RUnlock() + + if t == nil { + return false + } + for _, key = range append([]string{key}, kk...) { if _, has := t.value[key]; !has { return false @@ -146,6 +170,9 @@ func (t *Vars) HasAny(key string, kk ...string) bool { var _ gval.Selector = &Vars{} func (t *Vars) Dict() map[string]interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + dict := make(map[string]interface{}) for k, v := range t.value { switch v := v.(type) { @@ -177,6 +204,9 @@ func (t *Vars) Decode(dst interface{}) (err error) { return nil } + t.mux.RLock() + defer t.mux.RUnlock() + dstRef := reflect.ValueOf(dst) if dstRef.Kind() != reflect.Ptr { @@ -234,10 +264,18 @@ func (t *Vars) Scan(value interface{}) error { } func (t *Vars) Value() (driver.Value, error) { + if t != nil { + t.mux.RLock() + defer t.mux.RUnlock() + } + return json.Marshal(t) } -func (t Vars) SelectGVal(_ context.Context, k string) (interface{}, error) { +func (t *Vars) SelectGVal(_ context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() + val, err := t.Select(k) switch c := val.(type) { case gval.Selector: @@ -249,6 +287,9 @@ func (t Vars) SelectGVal(_ context.Context, k string) (interface{}, error) { // UnmarshalJSON unmarshal JSON value into Vars func (t *Vars) UnmarshalJSON(in []byte) (err error) { + t.mux.Lock() + defer t.mux.Unlock() + var ( aux = make(map[string]*typedValueWrap) ) @@ -275,7 +316,14 @@ func (t *Vars) UnmarshalJSON(in []byte) (err error) { } func (t *Vars) Each(fn func(k string, v TypedValue) error) (err error) { - if t == nil || t.value == nil { + if t == nil { + return nil + } + + t.mux.RLock() + defer t.mux.RUnlock() + + if t.value == nil { return } @@ -290,6 +338,9 @@ func (t *Vars) Each(fn func(k string, v TypedValue) error) (err error) { // Set set/update the specific key value in KV func (t *Vars) Set(k string, v interface{}) (err error) { + t.mux.RLock() + defer t.mux.RUnlock() + if t.value == nil { t.value = make(map[string]TypedValue) } @@ -299,7 +350,10 @@ func (t *Vars) Set(k string, v interface{}) (err error) { } // MarshalJSON returns JSON encoding of expression -func (t Vars) MarshalJSON() ([]byte, error) { +func (t *Vars) MarshalJSON() ([]byte, error) { + t.mux.RLock() + defer t.mux.RUnlock() + aux := make(map[string]*typedValueWrap) for k, v := range t.value { if v == nil { @@ -410,6 +464,9 @@ func CastToVars(val interface{}) (out map[string]TypedValue, err error) { switch c := val.(type) { case *Vars: + c.mux.RLock() + defer c.mux.RUnlock() + return c.value, nil case map[string]TypedValue: return c, nil @@ -430,6 +487,9 @@ func CastToVars(val interface{}) (out map[string]TypedValue, err error) { // Filter take keys returns KV with only those key value pair func (t *Vars) Filter(keys ...string) (out TypedValue, err error) { + t.mux.RLock() + defer t.mux.RUnlock() + if t.value == nil { return } @@ -447,6 +507,9 @@ func (t *Vars) Filter(keys ...string) (out TypedValue, err error) { // Delete take keys returns KV without those key value pair func (t *Vars) Delete(keys ...string) (out TypedValue, err error) { + t.mux.RLock() + defer t.mux.RUnlock() + if t.value == nil { return } diff --git a/pkg/wfexec/session.go b/pkg/wfexec/session.go index 8da6b0666..c3c671147 100644 --- a/pkg/wfexec/session.go +++ b/pkg/wfexec/session.go @@ -185,7 +185,7 @@ func NewSession(ctx context.Context, g *Graph, oo ...SessionOpt) *Session { return s } -func (s Session) Status() SessionStatus { +func (s *Session) Status() SessionStatus { s.mux.RLock() defer s.mux.RUnlock() @@ -207,14 +207,14 @@ func (s Session) Status() SessionStatus { } } -func (s Session) ID() uint64 { +func (s *Session) ID() uint64 { s.mux.RLock() defer s.mux.RUnlock() return s.id } -func (s Session) Idle() bool { +func (s *Session) Idle() bool { return s.Status() != SessionActive } @@ -514,7 +514,7 @@ func (s *Session) Stop() { s.qErr <- nil } -func (s Session) Suspended() bool { +func (s *Session) Suspended() bool { defer s.mux.RUnlock() s.mux.RLock() return len(s.delayed) > 0 diff --git a/pkg/wfexec/session_test.go b/pkg/wfexec/session_test.go index 7f2309cd1..f033e8fc5 100644 --- a/pkg/wfexec/session_test.go +++ b/pkg/wfexec/session_test.go @@ -96,7 +96,7 @@ func TestSession_SplitAndMerge(t *testing.T) { ctx = context.Background() req = require.New(t) wf = NewGraph() - ses = NewSession(ctx, wf) + ses = NewSession(ctx, wf, SetDumpStacktraceOnPanic(true)) start = &sesTestStep{name: "start"} split1 = &sesTestStep{name: "split1"} diff --git a/system/automation/expr_types.gen.go b/system/automation/expr_types.gen.go index e0986abf8..f0f1709aa 100644 --- a/system/automation/expr_types.gen.go +++ b/system/automation/expr_types.gen.go @@ -11,17 +11,20 @@ package automation import ( "context" "fmt" - . "github.com/cortezaproject/corteza-server/pkg/expr" "github.com/cortezaproject/corteza-server/pkg/rbac" "github.com/cortezaproject/corteza-server/system/types" + "sync" ) var _ = context.Background var _ = fmt.Errorf // DocumentType is an expression type, wrapper for types.DocumentType type -type DocumentType struct{ value types.DocumentType } +type DocumentType struct { + value types.DocumentType + mux sync.RWMutex +} // NewDocumentType creates new instance of DocumentType expression type func NewDocumentType(val interface{}) (*DocumentType, error) { @@ -32,16 +35,24 @@ func NewDocumentType(val interface{}) (*DocumentType, error) { } } -// Return underlying value on DocumentType -func (t DocumentType) Get() interface{} { return t.value } +// Get return underlying value on DocumentType +func (t *DocumentType) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on DocumentType -func (t DocumentType) GetValue() types.DocumentType { return t.value } +// GetValue returns underlying value on DocumentType +func (t *DocumentType) GetValue() types.DocumentType { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (DocumentType) Type() string { return "DocumentType" } -// Convert value to types.DocumentType +// Cast converts value to types.DocumentType func (DocumentType) Cast(val interface{}) (TypedValue, error) { return NewDocumentType(val) } @@ -59,7 +70,10 @@ func (t *DocumentType) Assign(val interface{}) error { } // QueueMessage is an expression type, wrapper for *types.QueueMessage type -type QueueMessage struct{ value *types.QueueMessage } +type QueueMessage struct { + value *types.QueueMessage + mux sync.RWMutex +} // NewQueueMessage creates new instance of QueueMessage expression type func NewQueueMessage(val interface{}) (*QueueMessage, error) { @@ -70,16 +84,24 @@ func NewQueueMessage(val interface{}) (*QueueMessage, error) { } } -// Return underlying value on QueueMessage -func (t QueueMessage) Get() interface{} { return t.value } +// Get return underlying value on QueueMessage +func (t *QueueMessage) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on QueueMessage -func (t QueueMessage) GetValue() *types.QueueMessage { return t.value } +// GetValue returns underlying value on QueueMessage +func (t *QueueMessage) GetValue() *types.QueueMessage { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (QueueMessage) Type() string { return "QueueMessage" } -// Convert value to *types.QueueMessage +// Cast converts value to *types.QueueMessage func (QueueMessage) Cast(val interface{}) (TypedValue, error) { return NewQueueMessage(val) } @@ -97,6 +119,8 @@ func (t *QueueMessage) Assign(val interface{}) error { } func (t *QueueMessage) AssignFieldValue(key string, val TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() return assignToQueueMessage(t.value, key, val) } @@ -105,18 +129,24 @@ func (t *QueueMessage) AssignFieldValue(key string, val TypedValue) error { // It allows gval lib to access QueueMessage's underlying value (*types.QueueMessage) // and it's fields // -func (t QueueMessage) SelectGVal(ctx context.Context, k string) (interface{}, error) { +func (t *QueueMessage) SelectGVal(ctx context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() return queueMessageGValSelector(t.value, k) } // Select is field accessor for *types.QueueMessage // // Similar to SelectGVal but returns typed values -func (t QueueMessage) Select(k string) (TypedValue, error) { +func (t *QueueMessage) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() return queueMessageTypedValueSelector(t.value, k) } -func (t QueueMessage) Has(k string) bool { +func (t *QueueMessage) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { case "Queue": return true @@ -181,7 +211,10 @@ func assignToQueueMessage(res *types.QueueMessage, k string, val interface{}) er } // RbacResource is an expression type, wrapper for rbac.Resource type -type RbacResource struct{ value rbac.Resource } +type RbacResource struct { + value rbac.Resource + mux sync.RWMutex +} // NewRbacResource creates new instance of RbacResource expression type func NewRbacResource(val interface{}) (*RbacResource, error) { @@ -192,16 +225,24 @@ func NewRbacResource(val interface{}) (*RbacResource, error) { } } -// Return underlying value on RbacResource -func (t RbacResource) Get() interface{} { return t.value } +// Get return underlying value on RbacResource +func (t *RbacResource) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on RbacResource -func (t RbacResource) GetValue() rbac.Resource { return t.value } +// GetValue returns underlying value on RbacResource +func (t *RbacResource) GetValue() rbac.Resource { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (RbacResource) Type() string { return "RbacResource" } -// Convert value to rbac.Resource +// Cast converts value to rbac.Resource func (RbacResource) Cast(val interface{}) (TypedValue, error) { return NewRbacResource(val) } @@ -219,7 +260,10 @@ func (t *RbacResource) Assign(val interface{}) error { } // RenderOptions is an expression type, wrapper for map[string]string type -type RenderOptions struct{ value map[string]string } +type RenderOptions struct { + value map[string]string + mux sync.RWMutex +} // NewRenderOptions creates new instance of RenderOptions expression type func NewRenderOptions(val interface{}) (*RenderOptions, error) { @@ -230,16 +274,24 @@ func NewRenderOptions(val interface{}) (*RenderOptions, error) { } } -// Return underlying value on RenderOptions -func (t RenderOptions) Get() interface{} { return t.value } +// Get return underlying value on RenderOptions +func (t *RenderOptions) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on RenderOptions -func (t RenderOptions) GetValue() map[string]string { return t.value } +// GetValue returns underlying value on RenderOptions +func (t *RenderOptions) GetValue() map[string]string { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (RenderOptions) Type() string { return "RenderOptions" } -// Convert value to map[string]string +// Cast converts value to map[string]string func (RenderOptions) Cast(val interface{}) (TypedValue, error) { return NewRenderOptions(val) } @@ -257,7 +309,10 @@ func (t *RenderOptions) Assign(val interface{}) error { } // RenderedDocument is an expression type, wrapper for *renderedDocument type -type RenderedDocument struct{ value *renderedDocument } +type RenderedDocument struct { + value *renderedDocument + mux sync.RWMutex +} // NewRenderedDocument creates new instance of RenderedDocument expression type func NewRenderedDocument(val interface{}) (*RenderedDocument, error) { @@ -268,16 +323,24 @@ func NewRenderedDocument(val interface{}) (*RenderedDocument, error) { } } -// Return underlying value on RenderedDocument -func (t RenderedDocument) Get() interface{} { return t.value } +// Get return underlying value on RenderedDocument +func (t *RenderedDocument) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on RenderedDocument -func (t RenderedDocument) GetValue() *renderedDocument { return t.value } +// GetValue returns underlying value on RenderedDocument +func (t *RenderedDocument) GetValue() *renderedDocument { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (RenderedDocument) Type() string { return "RenderedDocument" } -// Convert value to *renderedDocument +// Cast converts value to *renderedDocument func (RenderedDocument) Cast(val interface{}) (TypedValue, error) { return NewRenderedDocument(val) } @@ -295,6 +358,8 @@ func (t *RenderedDocument) Assign(val interface{}) error { } func (t *RenderedDocument) AssignFieldValue(key string, val TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() return assignToRenderedDocument(t.value, key, val) } @@ -303,18 +368,24 @@ func (t *RenderedDocument) AssignFieldValue(key string, val TypedValue) error { // It allows gval lib to access RenderedDocument's underlying value (*renderedDocument) // and it's fields // -func (t RenderedDocument) SelectGVal(ctx context.Context, k string) (interface{}, error) { +func (t *RenderedDocument) SelectGVal(ctx context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() return renderedDocumentGValSelector(t.value, k) } // Select is field accessor for *renderedDocument // // Similar to SelectGVal but returns typed values -func (t RenderedDocument) Select(k string) (TypedValue, error) { +func (t *RenderedDocument) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() return renderedDocumentTypedValueSelector(t.value, k) } -func (t RenderedDocument) Has(k string) bool { +func (t *RenderedDocument) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { case "document": return true @@ -393,7 +464,10 @@ func assignToRenderedDocument(res *renderedDocument, k string, val interface{}) } // Role is an expression type, wrapper for *types.Role type -type Role struct{ value *types.Role } +type Role struct { + value *types.Role + mux sync.RWMutex +} // NewRole creates new instance of Role expression type func NewRole(val interface{}) (*Role, error) { @@ -404,16 +478,24 @@ func NewRole(val interface{}) (*Role, error) { } } -// Return underlying value on Role -func (t Role) Get() interface{} { return t.value } +// Get return underlying value on Role +func (t *Role) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Role -func (t Role) GetValue() *types.Role { return t.value } +// GetValue returns underlying value on Role +func (t *Role) GetValue() *types.Role { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Role) Type() string { return "Role" } -// Convert value to *types.Role +// Cast converts value to *types.Role func (Role) Cast(val interface{}) (TypedValue, error) { return NewRole(val) } @@ -431,6 +513,8 @@ func (t *Role) Assign(val interface{}) error { } func (t *Role) AssignFieldValue(key string, val TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() return assignToRole(t.value, key, val) } @@ -439,18 +523,24 @@ func (t *Role) AssignFieldValue(key string, val TypedValue) error { // It allows gval lib to access Role's underlying value (*types.Role) // and it's fields // -func (t Role) SelectGVal(ctx context.Context, k string) (interface{}, error) { +func (t *Role) SelectGVal(ctx context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() return roleGValSelector(t.value, k) } // Select is field accessor for *types.Role // // Similar to SelectGVal but returns typed values -func (t Role) Select(k string) (TypedValue, error) { +func (t *Role) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() return roleTypedValueSelector(t.value, k) } -func (t Role) Has(k string) bool { +func (t *Role) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { case "ID", "roleID": return true @@ -569,7 +659,10 @@ func assignToRole(res *types.Role, k string, val interface{}) error { } // Template is an expression type, wrapper for *types.Template type -type Template struct{ value *types.Template } +type Template struct { + value *types.Template + mux sync.RWMutex +} // NewTemplate creates new instance of Template expression type func NewTemplate(val interface{}) (*Template, error) { @@ -580,16 +673,24 @@ func NewTemplate(val interface{}) (*Template, error) { } } -// Return underlying value on Template -func (t Template) Get() interface{} { return t.value } +// Get return underlying value on Template +func (t *Template) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on Template -func (t Template) GetValue() *types.Template { return t.value } +// GetValue returns underlying value on Template +func (t *Template) GetValue() *types.Template { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (Template) Type() string { return "Template" } -// Convert value to *types.Template +// Cast converts value to *types.Template func (Template) Cast(val interface{}) (TypedValue, error) { return NewTemplate(val) } @@ -607,6 +708,8 @@ func (t *Template) Assign(val interface{}) error { } func (t *Template) AssignFieldValue(key string, val TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() return assignToTemplate(t.value, key, val) } @@ -615,18 +718,24 @@ func (t *Template) AssignFieldValue(key string, val TypedValue) error { // It allows gval lib to access Template's underlying value (*types.Template) // and it's fields // -func (t Template) SelectGVal(ctx context.Context, k string) (interface{}, error) { +func (t *Template) SelectGVal(ctx context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() return templateGValSelector(t.value, k) } // Select is field accessor for *types.Template // // Similar to SelectGVal but returns typed values -func (t Template) Select(k string) (TypedValue, error) { +func (t *Template) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() return templateTypedValueSelector(t.value, k) } -func (t Template) Has(k string) bool { +func (t *Template) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { case "ID", "templateID": return true @@ -809,7 +918,10 @@ func assignToTemplate(res *types.Template, k string, val interface{}) error { } // TemplateMeta is an expression type, wrapper for types.TemplateMeta type -type TemplateMeta struct{ value types.TemplateMeta } +type TemplateMeta struct { + value types.TemplateMeta + mux sync.RWMutex +} // NewTemplateMeta creates new instance of TemplateMeta expression type func NewTemplateMeta(val interface{}) (*TemplateMeta, error) { @@ -820,16 +932,24 @@ func NewTemplateMeta(val interface{}) (*TemplateMeta, error) { } } -// Return underlying value on TemplateMeta -func (t TemplateMeta) Get() interface{} { return t.value } +// Get return underlying value on TemplateMeta +func (t *TemplateMeta) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on TemplateMeta -func (t TemplateMeta) GetValue() types.TemplateMeta { return t.value } +// GetValue returns underlying value on TemplateMeta +func (t *TemplateMeta) GetValue() types.TemplateMeta { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (TemplateMeta) Type() string { return "TemplateMeta" } -// Convert value to types.TemplateMeta +// Cast converts value to types.TemplateMeta func (TemplateMeta) Cast(val interface{}) (TypedValue, error) { return NewTemplateMeta(val) } @@ -847,6 +967,8 @@ func (t *TemplateMeta) Assign(val interface{}) error { } func (t *TemplateMeta) AssignFieldValue(key string, val TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() return assignToTemplateMeta(t.value, key, val) } @@ -855,18 +977,24 @@ func (t *TemplateMeta) AssignFieldValue(key string, val TypedValue) error { // It allows gval lib to access TemplateMeta's underlying value (types.TemplateMeta) // and it's fields // -func (t TemplateMeta) SelectGVal(ctx context.Context, k string) (interface{}, error) { +func (t *TemplateMeta) SelectGVal(ctx context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() return templateMetaGValSelector(t.value, k) } // Select is field accessor for types.TemplateMeta // // Similar to SelectGVal but returns typed values -func (t TemplateMeta) Select(k string) (TypedValue, error) { +func (t *TemplateMeta) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() return templateMetaTypedValueSelector(t.value, k) } -func (t TemplateMeta) Has(k string) bool { +func (t *TemplateMeta) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { case "short": return true @@ -925,7 +1053,10 @@ func assignToTemplateMeta(res types.TemplateMeta, k string, val interface{}) err } // User is an expression type, wrapper for *types.User type -type User struct{ value *types.User } +type User struct { + value *types.User + mux sync.RWMutex +} // NewUser creates new instance of User expression type func NewUser(val interface{}) (*User, error) { @@ -936,16 +1067,24 @@ func NewUser(val interface{}) (*User, error) { } } -// Return underlying value on User -func (t User) Get() interface{} { return t.value } +// Get return underlying value on User +func (t *User) Get() interface{} { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return underlying value on User -func (t User) GetValue() *types.User { return t.value } +// GetValue returns underlying value on User +func (t *User) GetValue() *types.User { + t.mux.RLock() + defer t.mux.RUnlock() + return t.value +} -// Return type name +// Type return type name func (User) Type() string { return "User" } -// Convert value to *types.User +// Cast converts value to *types.User func (User) Cast(val interface{}) (TypedValue, error) { return NewUser(val) } @@ -963,6 +1102,8 @@ func (t *User) Assign(val interface{}) error { } func (t *User) AssignFieldValue(key string, val TypedValue) error { + t.mux.Lock() + defer t.mux.Unlock() return assignToUser(t.value, key, val) } @@ -971,18 +1112,24 @@ func (t *User) AssignFieldValue(key string, val TypedValue) error { // It allows gval lib to access User's underlying value (*types.User) // and it's fields // -func (t User) SelectGVal(ctx context.Context, k string) (interface{}, error) { +func (t *User) SelectGVal(ctx context.Context, k string) (interface{}, error) { + t.mux.RLock() + defer t.mux.RUnlock() return userGValSelector(t.value, k) } // Select is field accessor for *types.User // // Similar to SelectGVal but returns typed values -func (t User) Select(k string) (TypedValue, error) { +func (t *User) Select(k string) (TypedValue, error) { + t.mux.RLock() + defer t.mux.RUnlock() return userTypedValueSelector(t.value, k) } -func (t User) Has(k string) bool { +func (t *User) Has(k string) bool { + t.mux.RLock() + defer t.mux.RUnlock() switch k { case "ID", "userID": return true