* Changes added

* Reading of events for applications changed.

* Proto changed

* Tests added

* Added more tests.

* Struct for Data expanded with additional fields.

* refactoring

* Changes from review.

* Merge in to Master

* Changes from review.

* fix: generate proto

Co-authored-by: Fabiennne <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Michael Waeger
2020-06-15 16:50:09 +02:00
committed by GitHub
parent 8dd6082b17
commit 1dd82ab1b7
36 changed files with 3833 additions and 3944 deletions

View File

@@ -2,6 +2,7 @@ package model
import (
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/golang/protobuf/ptypes/timestamp"
)
type Application struct {
@@ -13,6 +14,18 @@ type Application struct {
Type AppType
OIDCConfig *OIDCConfig
}
type ApplicationChanges struct {
Changes []*ApplicationChange
LastSequence uint64
}
type ApplicationChange struct {
ChangeDate *timestamp.Timestamp `json:"changeDate,omitempty"`
EventType string `json:"eventType,omitempty"`
Sequence uint64 `json:"sequence,omitempty"`
Modifier string `json:"modifierUser,omitempty"`
Data interface{} `json:"data,omitempty"`
}
type AppState int32

View File

@@ -2,6 +2,7 @@ package model
import (
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/golang/protobuf/ptypes/timestamp"
)
type Project struct {
@@ -14,6 +15,18 @@ type Project struct {
Applications []*Application
Grants []*ProjectGrant
}
type ProjectChanges struct {
Changes []*ProjectChange
LastSequence uint64
}
type ProjectChange struct {
ChangeDate *timestamp.Timestamp `json:"changeDate,omitempty"`
EventType string `json:"eventType,omitempty"`
Sequence uint64 `json:"sequence,omitempty"`
Modifier string `json:"modifierUser,omitempty"`
Data interface{} `json:"data,omitempty"`
}
type ProjectState int32

View File

@@ -2,11 +2,18 @@ package eventsourcing
import (
"context"
"encoding/json"
"log"
"strings"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/api/auth"
"github.com/caos/zitadel/internal/cache/config"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
"github.com/golang/protobuf/ptypes"
"github.com/caos/zitadel/internal/crypto"
caos_errs "github.com/caos/zitadel/internal/errors"
@@ -317,6 +324,70 @@ func (es *ProjectEventstore) RemoveProjectRole(ctx context.Context, role *proj_m
return nil
}
func (es *ProjectEventstore) ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*proj_model.ProjectChanges, error) {
query := ChangesQuery(id, lastSequence)
events, err := es.Eventstore.FilterEvents(context.Background(), query)
if err != nil {
logging.Log("EVENT-ZRffs").WithError(err).Warn("eventstore unavailable")
return nil, errors.ThrowInternal(err, "EVENT-328b1", "unable to get current user")
}
if len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-FpQqK", "no objects found")
}
result := make([]*proj_model.ProjectChange, 0)
for _, u := range events {
creationDate, err := ptypes.TimestampProto(u.CreationDate)
logging.Log("EVENT-qxIR7").OnError(err).Debug("unable to parse timestamp")
change := &proj_model.ProjectChange{
ChangeDate: creationDate,
EventType: u.Type.String(),
Modifier: u.EditorUser,
Sequence: u.Sequence,
}
projectDummy := proj_model.Project{}
appDummy := model.Application{}
change.Data = projectDummy
if u.Data != nil {
if strings.Contains(change.EventType, "application") {
if err := json.Unmarshal(u.Data, &appDummy); err != nil {
log.Println("Error getting data!", err.Error())
}
change.Data = appDummy
} else {
if err := json.Unmarshal(u.Data, &projectDummy); err != nil {
log.Println("Error getting data!", err.Error())
}
change.Data = projectDummy
}
}
result = append(result, change)
if lastSequence < u.Sequence {
lastSequence = u.Sequence
}
}
changes := &proj_model.ProjectChanges{
Changes: result,
LastSequence: lastSequence,
}
return changes, nil
}
func ChangesQuery(projID string, latestSequence uint64) *es_models.SearchQuery {
query := es_models.NewSearchQuery().
AggregateTypeFilter(model.ProjectAggregate).
LatestSequenceFilter(latestSequence).
AggregateIDFilter(projID)
return query
}
func (es *ProjectEventstore) ApplicationByIDs(ctx context.Context, projectID, appID string) (*proj_model.Application, error) {
if projectID == "" || appID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-ld93d", "project oder app AggregateID missing")
@@ -426,6 +497,69 @@ func (es *ProjectEventstore) RemoveApplication(ctx context.Context, app *proj_mo
return nil
}
func (es *ProjectEventstore) ApplicationChanges(ctx context.Context, id string, secId string, lastSequence uint64, limit uint64) (*proj_model.ApplicationChanges, error) {
query := ChangesQuery(id, lastSequence)
events, err := es.Eventstore.FilterEvents(context.Background(), query)
if err != nil {
logging.Log("EVENT-ZRffs").WithError(err).Warn("eventstore unavailable")
return nil, errors.ThrowInternal(err, "EVENT-sw6Ku", "unable to get current user")
}
if len(events) == 0 {
return nil, caos_errs.ThrowNotFound(nil, "EVENT-9IHLP", "no objects found")
}
result := make([]*proj_model.ApplicationChange, 0)
for _, u := range events {
creationDate, err := ptypes.TimestampProto(u.CreationDate)
logging.Log("EVENT-MJzeN").OnError(err).Debug("unable to parse timestamp")
change := &proj_model.ApplicationChange{
ChangeDate: creationDate,
EventType: u.Type.String(),
Modifier: u.EditorUser,
Sequence: u.Sequence,
}
appendChanges := true
// if change.EventType == "project.application.added" ||
// change.EventType == "project.application.changed" ||
// change.EventType == "project.application.config.oidc.added" ||
// change.EventType == "project.application.config.oidc.changed" {
if change.EventType == model.ApplicationAdded.String() ||
change.EventType == model.ApplicationChanged.String() ||
change.EventType == model.OIDCConfigAdded.String() ||
change.EventType == model.OIDCConfigChanged.String() {
appDummy := model.Application{}
if u.Data != nil {
if err := json.Unmarshal(u.Data, &appDummy); err != nil {
log.Println("Error getting data!", err.Error())
}
}
change.Data = appDummy
if appDummy.AppID != secId {
appendChanges = false
}
} else {
appendChanges = false
}
if appendChanges {
result = append(result, change)
if lastSequence < u.Sequence {
lastSequence = u.Sequence
}
}
}
changes := &proj_model.ApplicationChanges{
Changes: result,
LastSequence: lastSequence,
}
return changes, nil
}
func (es *ProjectEventstore) DeactivateApplication(ctx context.Context, projectID, appID string) (*proj_model.Application, error) {
if appID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-dlp9e", "appID missing")

View File

@@ -2,6 +2,7 @@ package eventsourcing
import (
"encoding/json"
mock_cache "github.com/caos/zitadel/internal/cache/mock"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore/mock"
@@ -9,6 +10,7 @@ import (
"github.com/caos/zitadel/internal/id"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
repo_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
"github.com/golang/mock/gomock"
)
@@ -275,3 +277,57 @@ func GetMockProjectGrantMemberByIDsOK(ctrl *gomock.Controller) *ProjectEventstor
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstore(ctrl, mockEs)
}
func GetMockChangesProjectOK(ctrl *gomock.Controller) *ProjectEventstore {
project := model.Project{
Name: "MusterProject",
}
data, err := json.Marshal(project)
if err != nil {
}
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateIDProject", Sequence: 1, AggregateType: repo_model.ProjectAggregate, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreComplexity(ctrl, mockEs)
}
func GetMockChangesProjectNoEvents(ctrl *gomock.Controller) *ProjectEventstore {
events := []*es_models.Event{}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreComplexity(ctrl, mockEs)
}
func GetMockedEventstoreComplexity(ctrl *gomock.Controller, mockEs *mock.MockEventstore) *ProjectEventstore {
return &ProjectEventstore{
Eventstore: mockEs,
}
}
func GetMockChangesApplicationOK(ctrl *gomock.Controller) *ProjectEventstore {
app := model.Application{
Name: "MusterApp",
AppID: "AppId",
Type: 3,
}
data, err := json.Marshal(app)
if err != nil {
}
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateIDApp", Type: "project.application.added", Sequence: 1, AggregateType: repo_model.ProjectAggregate, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreComplexity(ctrl, mockEs)
}
func GetMockChangesApplicationNoEvents(ctrl *gomock.Controller) *ProjectEventstore {
events := []*es_models.Event{}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreComplexity(ctrl, mockEs)
}

View File

@@ -2,13 +2,15 @@ package eventsourcing
import (
"context"
"encoding/json"
"reflect"
"testing"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/project/model"
"github.com/golang/mock/gomock"
"reflect"
"testing"
)
func TestProjectByID(t *testing.T) {
@@ -2695,3 +2697,139 @@ func TestRemoveProjectGrantMember(t *testing.T) {
})
}
}
func TestChangesProject(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *ProjectEventstore
id string
lastSequence uint64
limit uint64
}
type res struct {
changes *model.ProjectChanges
project *model.Project
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "changes from events, ok",
args: args{
es: GetMockChangesProjectOK(ctrl),
id: "1",
lastSequence: 0,
limit: 0,
},
res: res{
changes: &model.ProjectChanges{Changes: []*model.ProjectChange{&model.ProjectChange{EventType: "", Sequence: 1, Modifier: ""}}, LastSequence: 1},
project: &model.Project{Name: "MusterProject"},
},
},
{
name: "changes from events, no events",
args: args{
es: GetMockChangesProjectNoEvents(ctrl),
id: "2",
lastSequence: 0,
limit: 0,
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.ProjectChanges(nil, tt.args.id, tt.args.lastSequence, tt.args.limit)
project := &model.Project{}
if result != nil && len(result.Changes) > 0 {
b, err := json.Marshal(result.Changes[0].Data)
json.Unmarshal(b, project)
if err != nil {
}
}
if !tt.res.wantErr && result.LastSequence != tt.res.changes.LastSequence && project.Name != tt.res.project.Name {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.changes.LastSequence, result.LastSequence)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestChangesApplication(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *ProjectEventstore
id string
secId string
lastSequence uint64
limit uint64
}
type res struct {
changes *model.ApplicationChanges
app *model.Application
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "changes from events, ok",
args: args{
es: GetMockChangesApplicationOK(ctrl),
id: "1",
secId: "AppId",
lastSequence: 0,
limit: 0,
},
res: res{
changes: &model.ApplicationChanges{Changes: []*model.ApplicationChange{&model.ApplicationChange{EventType: "", Sequence: 1, Modifier: ""}}, LastSequence: 1},
app: &model.Application{Name: "MusterApp", AppID: "AppId", Type: 3},
},
},
{
name: "changes from events, no events",
args: args{
es: GetMockChangesApplicationNoEvents(ctrl),
id: "2",
secId: "2",
lastSequence: 0,
limit: 0,
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.ApplicationChanges(nil, tt.args.id, tt.args.secId, tt.args.lastSequence, tt.args.limit)
app := &model.Application{}
if result != nil && len(result.Changes) > 0 {
b, err := json.Marshal(result.Changes[0].Data)
json.Unmarshal(b, app)
if err != nil {
}
}
if !tt.res.wantErr && result.LastSequence != tt.res.changes.LastSequence && app.Name != tt.res.app.Name {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.changes.LastSequence, result.LastSequence)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}