mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-04 15:35:10 +00:00
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:
parent
970586dfc9
commit
191690d905
@ -17,11 +17,11 @@ type AlreadyExistsError struct {
|
||||
}
|
||||
|
||||
func ThrowAlreadyExists(parent error, id, message string) error {
|
||||
return &AlreadyExistsError{createCaosError(parent, id, message)}
|
||||
return &AlreadyExistsError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowAlreadyExistsf(parent error, id, format string, a ...interface{}) error {
|
||||
return &AlreadyExistsError{createCaosError(parent, id, fmt.Sprintf(format, a...))}
|
||||
return &AlreadyExistsError{CreateCaosError(parent, id, fmt.Sprintf(format, a...))}
|
||||
}
|
||||
|
||||
func (err *AlreadyExistsError) IsAlreadyExists() {}
|
||||
|
@ -13,10 +13,10 @@ type CaosError struct {
|
||||
}
|
||||
|
||||
func ThrowError(parent error, id, message string) error {
|
||||
return createCaosError(parent, id, message)
|
||||
return CreateCaosError(parent, id, message)
|
||||
}
|
||||
|
||||
func createCaosError(parent error, id, message string) *CaosError {
|
||||
func CreateCaosError(parent error, id, message string) *CaosError {
|
||||
return &CaosError{
|
||||
Parent: parent,
|
||||
ID: id,
|
||||
|
@ -19,7 +19,7 @@ type DeadlineExceededError struct {
|
||||
}
|
||||
|
||||
func ThrowDeadlineExceeded(parent error, id, message string) error {
|
||||
return &DeadlineExceededError{createCaosError(parent, id, message)}
|
||||
return &DeadlineExceededError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowDeadlineExceededf(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -19,7 +19,7 @@ type {{.ErrorName}}Error struct {
|
||||
}
|
||||
|
||||
func Throw{{.ErrorName}}(parent error, id, message string) error {
|
||||
return &{{.ErrorName}}Error{createCaosError(parent, id, message)}
|
||||
return &{{.ErrorName}}Error{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func Throw{{.ErrorName}}f(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
func Test{{.ErrorName}}Error(t *testing.T) {
|
||||
var err interface{}
|
||||
err = new(caos_errs.{{.ErrorName}}Error)
|
||||
_, ok := err.(caos_errs.{{.ErrorName}})
|
||||
_, ok := err.(*caos_errs.{{.ErrorName}})
|
||||
assert.True(t, ok)
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ type InternalError struct {
|
||||
}
|
||||
|
||||
func ThrowInternal(parent error, id, message string) error {
|
||||
return &InternalError{createCaosError(parent, id, message)}
|
||||
return &InternalError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowInternalf(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -17,7 +17,7 @@ type InvalidArgumentError struct {
|
||||
}
|
||||
|
||||
func ThrowInvalidArgument(parent error, id, message string) error {
|
||||
return &InvalidArgumentError{createCaosError(parent, id, message)}
|
||||
return &InvalidArgumentError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowInvalidArgumentf(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -12,7 +12,7 @@ type NotFoundError struct {
|
||||
}
|
||||
|
||||
func ThrowNotFound(parent error, id, message string) error {
|
||||
return &NotFoundError{createCaosError(parent, id, message)}
|
||||
return &NotFoundError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowNotFoundf(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -19,7 +19,7 @@ type PermissionDeniedError struct {
|
||||
}
|
||||
|
||||
func ThrowPermissionDenied(parent error, id, message string) error {
|
||||
return &PermissionDeniedError{createCaosError(parent, id, message)}
|
||||
return &PermissionDeniedError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowPermissionDeniedf(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -19,7 +19,7 @@ type PreconditionFailedError struct {
|
||||
}
|
||||
|
||||
func ThrowPreconditionFailed(parent error, id, message string) error {
|
||||
return &PreconditionFailedError{createCaosError(parent, id, message)}
|
||||
return &PreconditionFailedError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowPreconditionFailedf(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -19,7 +19,7 @@ type UnauthenticatedError struct {
|
||||
}
|
||||
|
||||
func ThrowUnauthenticated(parent error, id, message string) error {
|
||||
return &UnauthenticatedError{createCaosError(parent, id, message)}
|
||||
return &UnauthenticatedError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowUnauthenticatedf(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -19,7 +19,7 @@ type UnavailableError struct {
|
||||
}
|
||||
|
||||
func ThrowUnavailable(parent error, id, message string) error {
|
||||
return &UnavailableError{createCaosError(parent, id, message)}
|
||||
return &UnavailableError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowUnavailablef(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -19,7 +19,7 @@ type UnimplementedError struct {
|
||||
}
|
||||
|
||||
func ThrowUnimplemented(parent error, id, message string) error {
|
||||
return &UnimplementedError{createCaosError(parent, id, message)}
|
||||
return &UnimplementedError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowUnimplementedf(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -19,7 +19,7 @@ type UnknownError struct {
|
||||
}
|
||||
|
||||
func ThrowUnknown(parent error, id, message string) error {
|
||||
return &UnknownError{createCaosError(parent, id, message)}
|
||||
return &UnknownError{CreateCaosError(parent, id, message)}
|
||||
}
|
||||
|
||||
func ThrowUnknownf(parent error, id, format string, a ...interface{}) error {
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
36
internal/eventstore/sdk/append_event_error.go
Normal file
36
internal/eventstore/sdk/append_event_error.go
Normal 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
|
||||
}
|
31
internal/eventstore/sdk/append_event_error_test.go
Normal file
31
internal/eventstore/sdk/append_event_error_test.go
Normal 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)
|
||||
}
|
72
internal/eventstore/sdk/sdk.go
Normal file
72
internal/eventstore/sdk/sdk.go
Normal 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
|
||||
}
|
193
internal/eventstore/sdk/sdk_test.go
Normal file
193
internal/eventstore/sdk/sdk_test.go
Normal 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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
proj_model "github.com/caos/zitadel/internal/project/model"
|
||||
proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing"
|
||||
)
|
||||
@ -26,11 +27,7 @@ func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (project *p
|
||||
|
||||
func (repo *ProjectRepo) CreateProject(ctx context.Context, name string) (*proj_model.Project, error) {
|
||||
project := &proj_model.Project{Name: name}
|
||||
project, err := repo.ProjectEvents.CreateProject(ctx, project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return project, nil
|
||||
return repo.ProjectEvents.CreateProject(ctx, project)
|
||||
}
|
||||
|
||||
func (repo *ProjectRepo) UpdateProject(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) {
|
||||
@ -39,11 +36,7 @@ func (repo *ProjectRepo) UpdateProject(ctx context.Context, project *proj_model.
|
||||
return nil, err
|
||||
}
|
||||
|
||||
project, err = repo.ProjectEvents.UpdateProject(ctx, existingProject, project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return project, err
|
||||
return repo.ProjectEvents.UpdateProject(ctx, existingProject, project)
|
||||
}
|
||||
|
||||
func (repo *ProjectRepo) DeactivateProject(ctx context.Context, id string) (*proj_model.Project, error) {
|
||||
@ -52,11 +45,7 @@ func (repo *ProjectRepo) DeactivateProject(ctx context.Context, id string) (*pro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
project, err = repo.ProjectEvents.DeactivateProject(ctx, project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return project, err
|
||||
return repo.ProjectEvents.DeactivateProject(ctx, project)
|
||||
}
|
||||
|
||||
func (repo *ProjectRepo) ReactivateProject(ctx context.Context, id string) (*proj_model.Project, error) {
|
||||
@ -65,9 +54,5 @@ func (repo *ProjectRepo) ReactivateProject(ctx context.Context, id string) (*pro
|
||||
return nil, err
|
||||
}
|
||||
|
||||
project, err = repo.ProjectEvents.ReactivateProject(ctx, project)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return project, err
|
||||
return repo.ProjectEvents.ReactivateProject(ctx, project)
|
||||
}
|
||||
|
@ -2,8 +2,10 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
es_int "github.com/caos/zitadel/internal/eventstore"
|
||||
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
|
||||
proj_model "github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
|
||||
@ -20,22 +22,17 @@ func StartProject(conf ProjectConfig) (*ProjectEventstore, error) {
|
||||
}
|
||||
|
||||
func (es *ProjectEventstore) ProjectByID(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) {
|
||||
filter, err := ProjectByIDQuery(project.ID, project.Sequence)
|
||||
query, err := ProjectByIDQuery(project.ID, project.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
events, err := es.Eventstore.FilterEvents(ctx, filter)
|
||||
|
||||
p := ProjectFromModel(project)
|
||||
err = es_sdk.Filter(ctx, es.FilterEvents, p.AppendEvents, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(events) == 0 {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-8due3", "Could not find project events")
|
||||
}
|
||||
foundProject, err := ProjectFromEvents(nil, events...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ProjectToModel(foundProject), nil
|
||||
return ProjectToModel(p), nil
|
||||
}
|
||||
|
||||
func (es *ProjectEventstore) CreateProject(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) {
|
||||
@ -44,34 +41,29 @@ func (es *ProjectEventstore) CreateProject(ctx context.Context, project *proj_mo
|
||||
}
|
||||
project.State = proj_model.Active
|
||||
repoProject := ProjectFromModel(project)
|
||||
projectAggregate, err := ProjectCreateAggregate(ctx, es.Eventstore.AggregateCreator(), repoProject)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = es.PushAggregates(ctx, projectAggregate)
|
||||
|
||||
createAggregate := ProjectCreateAggregate(es.AggregateCreator(), repoProject)
|
||||
err := es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, createAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repoProject.AppendEvents(projectAggregate.Events...)
|
||||
return ProjectToModel(repoProject), nil
|
||||
}
|
||||
|
||||
func (es *ProjectEventstore) UpdateProject(ctx context.Context, existing *proj_model.Project, new *proj_model.Project) (*proj_model.Project, error) {
|
||||
if !new.IsValid() {
|
||||
func (es *ProjectEventstore) UpdateProject(ctx context.Context, existingProject *proj_model.Project, project *proj_model.Project) (*proj_model.Project, error) {
|
||||
if !project.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Name is required")
|
||||
}
|
||||
repoExisting := ProjectFromModel(existing)
|
||||
repoNew := ProjectFromModel(new)
|
||||
projectAggregate, err := ProjectUpdateAggregate(ctx, es.AggregateCreator(), repoExisting, repoNew)
|
||||
repoExisting := ProjectFromModel(existingProject)
|
||||
repoNew := ProjectFromModel(project)
|
||||
|
||||
updateAggregate := ProjectUpdateAggregate(es.AggregateCreator(), repoExisting, repoNew)
|
||||
err := es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = es.PushAggregates(ctx, projectAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoExisting.AppendEvents(projectAggregate.Events...)
|
||||
|
||||
return ProjectToModel(repoExisting), nil
|
||||
}
|
||||
|
||||
@ -79,16 +71,10 @@ func (es *ProjectEventstore) DeactivateProject(ctx context.Context, existing *pr
|
||||
if !existing.IsActive() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die45", "project must be active")
|
||||
}
|
||||
|
||||
repoExisting := ProjectFromModel(existing)
|
||||
projectAggregate, err := ProjectDeactivateAggregate(ctx, es.AggregateCreator(), repoExisting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = es.PushAggregates(ctx, projectAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoExisting.AppendEvents(projectAggregate.Events...)
|
||||
aggregate := ProjectDeactivateAggregate(es.AggregateCreator(), repoExisting)
|
||||
es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, aggregate)
|
||||
return ProjectToModel(repoExisting), nil
|
||||
}
|
||||
|
||||
@ -96,15 +82,9 @@ func (es *ProjectEventstore) ReactivateProject(ctx context.Context, existing *pr
|
||||
if existing.IsActive() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die45", "project must be inactive")
|
||||
}
|
||||
|
||||
repoExisting := ProjectFromModel(existing)
|
||||
projectAggregate, err := ProjectReactivateAggregate(ctx, es.AggregateCreator(), repoExisting)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = es.PushAggregates(ctx, projectAggregate)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
repoExisting.AppendEvents(projectAggregate.Events...)
|
||||
aggregate := ProjectReactivateAggregate(es.AggregateCreator(), repoExisting)
|
||||
es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, aggregate)
|
||||
return ProjectToModel(repoExisting), nil
|
||||
}
|
||||
|
@ -2,11 +2,48 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/caos/logging"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
|
||||
const (
|
||||
projectVersion = "v1"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
es_models.ObjectRoot
|
||||
Name string `json:"name,omitempty"`
|
||||
State int32 `json:"-"`
|
||||
}
|
||||
|
||||
func ProjectFromModel(project *model.Project) *Project {
|
||||
return &Project{
|
||||
ObjectRoot: es_models.ObjectRoot{
|
||||
ID: project.ObjectRoot.ID,
|
||||
Sequence: project.Sequence,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
},
|
||||
Name: project.Name,
|
||||
State: model.ProjectStateToInt(project.State),
|
||||
}
|
||||
}
|
||||
|
||||
func ProjectToModel(project *Project) *model.Project {
|
||||
return &model.Project{
|
||||
ObjectRoot: es_models.ObjectRoot{
|
||||
ID: project.ID,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
Sequence: project.Sequence,
|
||||
},
|
||||
Name: project.Name,
|
||||
State: model.ProjectStateFromInt(project.State),
|
||||
}
|
||||
}
|
||||
|
||||
func ProjectFromEvents(project *Project, events ...*es_models.Event) (*Project, error) {
|
||||
if project == nil {
|
||||
project = &Project{}
|
||||
@ -15,6 +52,14 @@ func ProjectFromEvents(project *Project, events ...*es_models.Event) (*Project,
|
||||
return project, project.AppendEvents(events...)
|
||||
}
|
||||
|
||||
func (p *Project) Changes(changed *Project) map[string]interface{} {
|
||||
changes := make(map[string]interface{}, 1)
|
||||
if changed.Name != "" && p.Name != changed.Name {
|
||||
changes["name"] = changed.Name
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
func (p *Project) AppendEvents(events ...*es_models.Event) error {
|
||||
for _, event := range events {
|
||||
if err := p.AppendEvent(event); err != nil {
|
@ -2,9 +2,10 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestProjectFromEvents(t *testing.T) {
|
||||
@ -167,3 +168,47 @@ func TestAppendReactivatedEvent(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChanges(t *testing.T) {
|
||||
type args struct {
|
||||
existing *Project
|
||||
new *Project
|
||||
}
|
||||
type res struct {
|
||||
changesLen int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project name changes",
|
||||
args: args{
|
||||
existing: &Project{Name: "Name"},
|
||||
new: &Project{Name: "NameChanged"},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes",
|
||||
args: args{
|
||||
existing: &Project{Name: "Name"},
|
||||
new: &Project{Name: "Name"},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
changes := tt.args.existing.Changes(tt.args.new)
|
||||
if len(changes) != tt.res.changesLen {
|
||||
t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -5,6 +5,7 @@ import (
|
||||
"strconv"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
"github.com/sony/sonyflake"
|
||||
@ -12,50 +13,6 @@ import (
|
||||
|
||||
var idGenerator = sonyflake.NewSonyflake(sonyflake.Settings{})
|
||||
|
||||
const (
|
||||
projectVersion = "v1"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
es_models.ObjectRoot
|
||||
Name string `json:"name,omitempty"`
|
||||
State int32 `json:"-"`
|
||||
}
|
||||
|
||||
func (p *Project) Changes(changed *Project) map[string]interface{} {
|
||||
changes := make(map[string]interface{}, 1)
|
||||
if changed.Name != "" && p.Name != changed.Name {
|
||||
changes["name"] = changed.Name
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
func ProjectFromModel(project *model.Project) *Project {
|
||||
return &Project{
|
||||
ObjectRoot: es_models.ObjectRoot{
|
||||
ID: project.ObjectRoot.ID,
|
||||
Sequence: project.Sequence,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
},
|
||||
Name: project.Name,
|
||||
State: model.ProjectStateToInt(project.State),
|
||||
}
|
||||
}
|
||||
|
||||
func ProjectToModel(project *Project) *model.Project {
|
||||
return &model.Project{
|
||||
ObjectRoot: es_models.ObjectRoot{
|
||||
ID: project.ID,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
Sequence: project.Sequence,
|
||||
},
|
||||
Name: project.Name,
|
||||
State: model.ProjectStateFromInt(project.State),
|
||||
}
|
||||
}
|
||||
|
||||
func ProjectByIDQuery(id string, latestSequence uint64) (*es_models.SearchQuery, error) {
|
||||
if id == "" {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dke74", "id should be filled")
|
||||
@ -74,58 +31,61 @@ func ProjectAggregate(ctx context.Context, aggCreator *es_models.AggregateCreato
|
||||
return aggCreator.NewAggregate(ctx, id, model.ProjectAggregate, projectVersion, sequence)
|
||||
}
|
||||
|
||||
func ProjectCreateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, project *Project) (*es_models.Aggregate, error) {
|
||||
if project == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-kdie6", "project should not be nil")
|
||||
}
|
||||
var err error
|
||||
id, err := idGenerator.NextID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
project.ID = strconv.FormatUint(id, 10)
|
||||
func ProjectCreateAggregate(aggCreator *es_models.AggregateCreator, project *Project) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
if project == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-kdie6", "project should not be nil")
|
||||
}
|
||||
var err error
|
||||
id, err := idGenerator.NextID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
project.ID = strconv.FormatUint(id, 10)
|
||||
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, project.ID, project.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, project.ID, project.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return agg.AppendEvent(model.ProjectAdded, project)
|
||||
return agg.AppendEvent(model.ProjectAdded, project)
|
||||
}
|
||||
}
|
||||
|
||||
func ProjectUpdateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *Project, new *Project) (*es_models.Aggregate, error) {
|
||||
if existing == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dk93d", "existing project should not be nil")
|
||||
func ProjectUpdateAggregate(aggCreator *es_models.AggregateCreator, existing *Project, new *Project) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
if existing == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dk93d", "existing project should not be nil")
|
||||
}
|
||||
if new == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dhr74", "new project should not be nil")
|
||||
}
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, existing.ID, existing.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changes := existing.Changes(new)
|
||||
return agg.AppendEvent(model.ProjectChanged, changes)
|
||||
}
|
||||
if new == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dhr74", "new project should not be nil")
|
||||
}
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, existing.ID, existing.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changes := existing.Changes(new)
|
||||
return agg.AppendEvent(model.ProjectChanged, changes)
|
||||
}
|
||||
|
||||
func ProjectDeactivateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *Project) (*es_models.Aggregate, error) {
|
||||
if existing == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-ueh45", "existing project should not be nil")
|
||||
}
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, existing.ID, existing.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(model.ProjectDeactivated, nil)
|
||||
func ProjectDeactivateAggregate(aggCreator *es_models.AggregateCreator, project *Project) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return projectStateAggregate(aggCreator, project, model.ProjectDeactivated)
|
||||
}
|
||||
|
||||
func ProjectReactivateAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, existing *Project) (*es_models.Aggregate, error) {
|
||||
if existing == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-37dur", "existing project should not be nil")
|
||||
}
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, existing.ID, existing.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(model.ProjectReactivated, nil)
|
||||
func ProjectReactivateAggregate(aggCreator *es_models.AggregateCreator, project *Project) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return projectStateAggregate(aggCreator, project, model.ProjectReactivated)
|
||||
}
|
||||
|
||||
func projectStateAggregate(aggCreator *es_models.AggregateCreator, project *Project, state models.EventType) func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
return func(ctx context.Context) (*es_models.Aggregate, error) {
|
||||
if project == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-37dur", "existing project should not be nil")
|
||||
}
|
||||
agg, err := ProjectAggregate(ctx, aggCreator, project.ID, project.Sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return agg.AppendEvent(state, nil)
|
||||
}
|
||||
}
|
||||
|
@ -2,57 +2,14 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestChanges(t *testing.T) {
|
||||
type args struct {
|
||||
existing *Project
|
||||
new *Project
|
||||
}
|
||||
type res struct {
|
||||
changesLen int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "project name changes",
|
||||
args: args{
|
||||
existing: &Project{Name: "Name"},
|
||||
new: &Project{Name: "NameChanged"},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 1,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes",
|
||||
args: args{
|
||||
existing: &Project{Name: "Name"},
|
||||
new: &Project{Name: "Name"},
|
||||
},
|
||||
res: res{
|
||||
changesLen: 0,
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
changes := tt.args.existing.Changes(tt.args.new)
|
||||
if len(changes) != tt.res.changesLen {
|
||||
t.Errorf("got wrong changes len: expected: %v, actual: %v ", tt.res.changesLen, len(changes))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestProjectByIDQuery(t *testing.T) {
|
||||
type args struct {
|
||||
id string
|
||||
@ -231,7 +188,7 @@ func TestProjectCreateAggregate(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := ProjectCreateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.new)
|
||||
agg, err := ProjectCreateAggregate(tt.args.aggCreator, tt.args.new)(tt.args.ctx)
|
||||
|
||||
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
@ -312,7 +269,7 @@ func TestProjectUpdateAggregate(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := ProjectUpdateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.existing, tt.args.new)
|
||||
agg, err := ProjectUpdateAggregate(tt.args.aggCreator, tt.args.existing, tt.args.new)(tt.args.ctx)
|
||||
|
||||
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
@ -376,7 +333,7 @@ func TestProjectDeactivateAggregate(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := ProjectDeactivateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.existing)
|
||||
agg, err := ProjectDeactivateAggregate(tt.args.aggCreator, tt.args.existing)(tt.args.ctx)
|
||||
|
||||
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
@ -437,7 +394,7 @@ func TestProjectReactivateAggregate(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
agg, err := ProjectReactivateAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.existing)
|
||||
agg, err := ProjectReactivateAggregate(tt.args.aggCreator, tt.args.existing)(tt.args.ctx)
|
||||
|
||||
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
|
||||
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
|
||||
|
Loading…
Reference in New Issue
Block a user