feat(eventstore): sdk (#39)

* sdk

* fix(sdk): return correct error type

* AppendEventError instead of Aggregater error

* fix(tests): tests

* fix(tests): wantErr to is error func
This commit is contained in:
Silvan
2020-04-07 18:36:37 +02:00
committed by GitHub
parent 970586dfc9
commit 191690d905
27 changed files with 524 additions and 234 deletions

View File

@@ -33,7 +33,7 @@ func (es *eventstore) PushAggregates(ctx context.Context, aggregates ...*models.
}
for _, event := range aggregate.Events {
if err = event.Validate(); err != nil {
return err
return errors.ThrowInvalidArgument(err, "EVENT-tzIhl", "validate event failed")
}
}
}
@@ -42,12 +42,6 @@ func (es *eventstore) PushAggregates(ctx context.Context, aggregates ...*models.
return err
}
for _, aggregate := range aggregates {
if aggregate.Appender != nil {
aggregate.Appender(aggregate.Events...)
}
}
return nil
}

View File

@@ -24,11 +24,8 @@ type Aggregate struct {
editorUser string
resourceOwner string
Events []*Event
Appender appender
}
type appender func(...*Event)
func (a *Aggregate) AppendEvent(typ EventType, payload interface{}) (*Aggregate, error) {
if string(typ) == "" {
return a, errors.ThrowInvalidArgument(nil, "MODEL-TGoCb", "no event type")
@@ -81,8 +78,3 @@ func (a *Aggregate) Validate() error {
return nil
}
func (a *Aggregate) SetAppender(appendFn appender) *Aggregate {
a.Appender = appendFn
return a
}

View File

@@ -0,0 +1,36 @@
package sdk
import (
"fmt"
"github.com/caos/zitadel/internal/errors"
)
var (
_ AppendEventError = (*appendEventError)(nil)
_ errors.Error = (*appendEventError)(nil)
)
type AppendEventError interface {
error
IsAppendEventError()
}
type appendEventError struct {
*errors.CaosError
}
func ThrowAppendEventError(parent error, id, message string) error {
return &appendEventError{errors.CreateCaosError(parent, id, message)}
}
func ThrowAggregaterf(parent error, id, format string, a ...interface{}) error {
return ThrowAppendEventError(parent, id, fmt.Sprintf(format, a...))
}
func (err *appendEventError) IsAppendEventError() {}
func IsAppendEventError(err error) bool {
_, ok := err.(AppendEventError)
return ok
}

View File

@@ -0,0 +1,31 @@
package sdk
import (
"errors"
"testing"
"github.com/stretchr/testify/assert"
)
func TestAppendEventError(t *testing.T) {
var err interface{}
err = new(appendEventError)
_, ok := err.(*appendEventError)
assert.True(t, ok)
}
func TestThrowAppendEventErrorf(t *testing.T) {
err := ThrowAggregaterf(nil, "id", "msg")
_, ok := err.(*appendEventError)
assert.True(t, ok)
}
func TestIsAppendEventError(t *testing.T) {
err := ThrowAppendEventError(nil, "id", "msg")
ok := IsAppendEventError(err)
assert.True(t, ok)
err = errors.New("i am found")
ok = IsAppendEventError(err)
assert.False(t, ok)
}

View File

@@ -0,0 +1,72 @@
package sdk
import (
"context"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
)
type filterFunc func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error)
type appendFunc func(...*es_models.Event) error
type aggregateFunc func(context.Context) (*es_models.Aggregate, error)
type pushFunc func(context.Context, ...*es_models.Aggregate) error
func Filter(ctx context.Context, filter filterFunc, appender appendFunc, query *es_models.SearchQuery) error {
events, err := filter(ctx, query)
if err != nil {
return err
}
if len(events) == 0 {
return errors.ThrowNotFound(nil, "EVENT-8due3", "no events found")
}
err = appender(events...)
if err != nil{
return ThrowAppendEventError(err, "SDK-awiWK", "appender failed")
}
return nil
}
// Push creates the aggregates from aggregater
// and pushes the aggregates to the given pushFunc
// the given events are appended by the appender
func Push(ctx context.Context, push pushFunc, appender appendFunc, aggregaters ...aggregateFunc) (err error) {
if len(aggregaters) < 1 {
return errors.ThrowPreconditionFailed(nil, "SDK-q9wjp", "no aggregaters passed")
}
aggregates, err := makeAggregates(ctx, aggregaters)
if err != nil {
return err
}
err = push(ctx, aggregates...)
if err != nil {
return err
}
return appendAggregates(appender, aggregates)
}
func appendAggregates(appender appendFunc, aggregates []*models.Aggregate) error {
for _, aggregate := range aggregates {
err := appender(aggregate.Events...)
if err != nil {
return ThrowAppendEventError(err, "SDK-o6kzK", "aggregator failed")
}
}
return nil
}
func makeAggregates(ctx context.Context, aggregaters []aggregateFunc) (aggregates []*models.Aggregate, err error) {
aggregates = make([]*models.Aggregate, len(aggregaters))
for i, aggregater := range aggregaters {
aggregates[i], err = aggregater(ctx)
if err != nil {
return nil, err
}
}
return aggregates, nil
}

View File

@@ -0,0 +1,193 @@
package sdk
import (
"context"
"testing"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
)
func TestFilter(t *testing.T) {
type args struct {
filter filterFunc
appender appendFunc
}
tests := []struct {
name string
args args
wantErr func(error) bool
}{
{
name: "filter error",
args: args{
filter: func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error) {
return nil, errors.ThrowInternal(nil, "test-46VX2", "test error")
},
appender: nil,
},
wantErr: errors.IsInternal,
},
{
name: "no events found",
args: args{
filter: func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error) {
return []*es_models.Event{}, nil
},
appender: nil,
},
wantErr: errors.IsNotFound,
},
{
name: "append fails",
args: args{
filter: func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error) {
return []*es_models.Event{&es_models.Event{}}, nil
},
appender: func(...*es_models.Event) error {
return errors.ThrowInvalidArgument(nil, "SDK-DhBzl", "test error")
},
},
wantErr: IsAppendEventError,
},
{
name: "filter correct",
args: args{
filter: func(context.Context, *es_models.SearchQuery) ([]*es_models.Event, error) {
return []*es_models.Event{&es_models.Event{}}, nil
},
appender: func(...*es_models.Event) error {
return nil
},
},
wantErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := Filter(context.Background(), tt.args.filter, tt.args.appender, nil)
if tt.wantErr == nil && err != nil {
t.Errorf("no error expected %v", err)
}
if tt.wantErr != nil && !tt.wantErr(err) {
t.Errorf("no error has wrong type %v", err)
}
})
}
}
func TestPush(t *testing.T) {
type args struct {
push pushFunc
appender appendFunc
aggregaters []aggregateFunc
}
tests := []struct {
name string
args args
wantErr func(error) bool
}{
{
name: "no aggregates",
args: args{
push: nil,
appender: nil,
aggregaters: nil,
},
wantErr: errors.IsPreconditionFailed,
},
{
name: "aggregater fails",
args: args{
push: nil,
appender: nil,
aggregaters: []aggregateFunc{
func(context.Context) (*es_models.Aggregate, error) {
return nil, errors.ThrowInternal(nil, "SDK-Ec5x2", "test err")
},
},
},
wantErr: errors.IsInternal,
},
{
name: "push fails",
args: args{
push: func(context.Context, ...*es_models.Aggregate) error {
return errors.ThrowInternal(nil, "SDK-0g4gW", "test error")
},
appender: nil,
aggregaters: []aggregateFunc{
func(context.Context) (*es_models.Aggregate, error) {
return &es_models.Aggregate{}, nil
},
},
},
wantErr: errors.IsInternal,
},
{
name: "append aggregates fails",
args: args{
push: func(context.Context, ...*es_models.Aggregate) error {
return nil
},
appender: func(...*es_models.Event) error {
return errors.ThrowInvalidArgument(nil, "SDK-BDhcT", "test err")
},
aggregaters: []aggregateFunc{
func(context.Context) (*es_models.Aggregate, error) {
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
},
},
},
wantErr: IsAppendEventError,
},
{
name: "correct one aggregate",
args: args{
push: func(context.Context, ...*es_models.Aggregate) error {
return nil
},
appender: func(...*es_models.Event) error {
return nil
},
aggregaters: []aggregateFunc{
func(context.Context) (*es_models.Aggregate, error) {
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
},
},
},
wantErr: nil,
},
{
name: "correct multiple aggregate",
args: args{
push: func(context.Context, ...*es_models.Aggregate) error {
return nil
},
appender: func(...*es_models.Event) error {
return nil
},
aggregaters: []aggregateFunc{
func(context.Context) (*es_models.Aggregate, error) {
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
},
func(context.Context) (*es_models.Aggregate, error) {
return &es_models.Aggregate{Events: []*es_models.Event{&es_models.Event{}}}, nil
},
},
},
wantErr: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := Push(context.Background(), tt.args.push, tt.args.appender, tt.args.aggregaters...)
if tt.wantErr == nil && err != nil {
t.Errorf("no error expected %v", err)
}
if tt.wantErr != nil && !tt.wantErr(err) {
t.Errorf("no error has wrong type %v", err)
}
})
}
}