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
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 524 additions and 234 deletions

View File

@ -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() {}

View File

@ -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,

View File

@ -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 {

View File

@ -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 {

View File

@ -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)
}

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

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)
}
})
}
}

View File

@ -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)
}

View File

@ -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
}

View File

@ -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 {

View File

@ -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))
}
})
}
}

View File

@ -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)
}
}

View File

@ -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))