mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 00:57:33 +00:00
Changes (#195)
* 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:
@@ -2,9 +2,10 @@ package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
"github.com/caos/zitadel/internal/model"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
mgmt_view "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
|
||||
@@ -45,6 +46,14 @@ func (repo *OrgRepository) ReactivateOrg(ctx context.Context, id string) (*org_m
|
||||
return repo.OrgEventstore.ReactivateOrg(ctx, id)
|
||||
}
|
||||
|
||||
func (repo *OrgRepository) OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*org_model.OrgChanges, error) {
|
||||
changes, err := repo.OrgEventstore.OrgChanges(ctx, id, lastSequence, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func (repo *OrgRepository) OrgMemberByID(ctx context.Context, orgID, userID string) (member *org_model.OrgMember, err error) {
|
||||
member = org_model.NewOrgMember(orgID, userID)
|
||||
return repo.OrgEventstore.OrgMemberByIDs(ctx, member)
|
||||
|
@@ -2,9 +2,10 @@ package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
global_model "github.com/caos/zitadel/internal/model"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
|
||||
"github.com/caos/zitadel/internal/project/repository/view/model"
|
||||
@@ -129,6 +130,14 @@ func (repo *ProjectRepo) SearchProjectRoles(ctx context.Context, request *proj_m
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *ProjectRepo) ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*proj_model.ProjectChanges, error) {
|
||||
changes, err := repo.ProjectEvents.ProjectChanges(ctx, id, lastSequence, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func (repo *ProjectRepo) ApplicationByID(ctx context.Context, projectID, appID string) (app *proj_model.Application, err error) {
|
||||
return repo.ProjectEvents.ApplicationByIDs(ctx, projectID, appID)
|
||||
}
|
||||
@@ -168,6 +177,14 @@ func (repo *ProjectRepo) SearchApplications(ctx context.Context, request *proj_m
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *ProjectRepo) ApplicationChanges(ctx context.Context, id string, appId string, lastSequence uint64, limit uint64) (*proj_model.ApplicationChanges, error) {
|
||||
changes, err := repo.ProjectEvents.ApplicationChanges(ctx, id, appId, lastSequence, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func (repo *ProjectRepo) ChangeOIDCConfig(ctx context.Context, config *proj_model.OIDCConfig) (*proj_model.OIDCConfig, error) {
|
||||
return repo.ProjectEvents.ChangeOIDCConfig(ctx, config)
|
||||
}
|
||||
|
@@ -2,6 +2,7 @@ package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
|
||||
policy_event "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
|
||||
@@ -71,6 +72,14 @@ func (repo *UserRepo) SearchUsers(ctx context.Context, request *usr_model.UserSe
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (repo *UserRepo) UserChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*usr_model.UserChanges, error) {
|
||||
changes, err := repo.UserEvents.UserChanges(ctx, id, lastSequence, limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func (repo *UserRepo) GetGlobalUserByEmail(ctx context.Context, email string) (*usr_model.UserView, error) {
|
||||
user, err := repo.View.GetGlobalUserByEmail(email)
|
||||
if err != nil {
|
||||
|
@@ -2,6 +2,7 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
es_iam "github.com/caos/zitadel/internal/iam/repository/eventsourcing"
|
||||
|
||||
sd "github.com/caos/zitadel/internal/config/systemdefaults"
|
||||
@@ -87,7 +88,6 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, roles []string) (*EsRe
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
eventstoreRepos := handler.EventstoreRepos{ProjectEvents: project, UserEvents: user, OrgEvents: org}
|
||||
spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, eventstoreRepos)
|
||||
|
||||
|
@@ -12,6 +12,7 @@ type OrgRepository interface {
|
||||
UpdateOrg(ctx context.Context, org *org_model.Org) (*org_model.Org, error)
|
||||
DeactivateOrg(ctx context.Context, id string) (*org_model.Org, error)
|
||||
ReactivateOrg(ctx context.Context, id string) (*org_model.Org, error)
|
||||
OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*org_model.OrgChanges, error)
|
||||
|
||||
SearchMyOrgMembers(ctx context.Context, request *org_model.OrgMemberSearchRequest) (*org_model.OrgMemberSearchResponse, error)
|
||||
AddMyOrgMember(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error)
|
||||
|
@@ -27,6 +27,7 @@ type ProjectRepository interface {
|
||||
ChangeProjectRole(ctx context.Context, role *model.ProjectRole) (*model.ProjectRole, error)
|
||||
RemoveProjectRole(ctx context.Context, projectID, key string) error
|
||||
SearchProjectRoles(ctx context.Context, request *model.ProjectRoleSearchRequest) (*model.ProjectRoleSearchResponse, error)
|
||||
ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*model.ProjectChanges, error)
|
||||
|
||||
ApplicationByID(ctx context.Context, projectID, appID string) (*model.Application, error)
|
||||
AddApplication(ctx context.Context, app *model.Application) (*model.Application, error)
|
||||
@@ -37,6 +38,7 @@ type ProjectRepository interface {
|
||||
ChangeOIDCConfig(ctx context.Context, config *model.OIDCConfig) (*model.OIDCConfig, error)
|
||||
ChangeOIDConfigSecret(ctx context.Context, projectID, appID string) (*model.OIDCConfig, error)
|
||||
SearchApplications(ctx context.Context, request *model.ApplicationSearchRequest) (*model.ApplicationSearchResponse, error)
|
||||
ApplicationChanges(ctx context.Context, id string, secId string, lastSequence uint64, limit uint64) (*model.ApplicationChanges, error)
|
||||
|
||||
ProjectGrantByID(ctx context.Context, projectID, grantID string) (*model.ProjectGrant, error)
|
||||
AddProjectGrant(ctx context.Context, app *model.ProjectGrant) (*model.ProjectGrant, error)
|
||||
|
@@ -2,6 +2,7 @@ package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/user/model"
|
||||
)
|
||||
|
||||
@@ -14,6 +15,7 @@ type UserRepository interface {
|
||||
LockUser(ctx context.Context, id string) (*model.User, error)
|
||||
UnlockUser(ctx context.Context, id string) (*model.User, error)
|
||||
SearchUsers(ctx context.Context, request *model.UserSearchRequest) (*model.UserSearchResponse, error)
|
||||
UserChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*model.UserChanges, error)
|
||||
GetGlobalUserByEmail(ctx context.Context, email string) (*model.UserView, error)
|
||||
IsUserUnique(ctx context.Context, userName, email string) (bool, error)
|
||||
UserMfas(ctx context.Context, userID string) ([]*model.MultiFactor, error)
|
||||
|
@@ -2,6 +2,7 @@ package model
|
||||
|
||||
import (
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
)
|
||||
|
||||
type Org struct {
|
||||
@@ -13,6 +14,18 @@ type Org struct {
|
||||
|
||||
Members []*OrgMember
|
||||
}
|
||||
type OrgChanges struct {
|
||||
Changes []*OrgChange
|
||||
LastSequence uint64
|
||||
}
|
||||
|
||||
type OrgChange 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 OrgState int32
|
||||
|
||||
|
@@ -2,13 +2,19 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
)
|
||||
|
||||
type OrgEventstore struct {
|
||||
@@ -132,6 +138,60 @@ func (es *OrgEventstore) ReactivateOrg(ctx context.Context, orgID string) (*org_
|
||||
return model.OrgToModel(org), nil
|
||||
}
|
||||
|
||||
func (es *OrgEventstore) OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*org_model.OrgChanges, 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([]*org_model.OrgChange, 0)
|
||||
|
||||
for _, u := range events {
|
||||
creationDate, err := ptypes.TimestampProto(u.CreationDate)
|
||||
logging.Log("EVENT-qxIR7").OnError(err).Debug("unable to parse timestamp")
|
||||
change := &org_model.OrgChange{
|
||||
ChangeDate: creationDate,
|
||||
EventType: u.Type.String(),
|
||||
Modifier: u.EditorUser,
|
||||
Sequence: u.Sequence,
|
||||
}
|
||||
|
||||
orgDummy := model.Org{}
|
||||
if u.Data != nil {
|
||||
if err := json.Unmarshal(u.Data, &orgDummy); err != nil {
|
||||
log.Println("Error getting data!", err.Error())
|
||||
}
|
||||
}
|
||||
change.Data = orgDummy
|
||||
|
||||
result = append(result, change)
|
||||
if lastSequence < u.Sequence {
|
||||
lastSequence = u.Sequence
|
||||
}
|
||||
}
|
||||
|
||||
changes := &org_model.OrgChanges{
|
||||
Changes: result,
|
||||
LastSequence: lastSequence,
|
||||
}
|
||||
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func ChangesQuery(orgID string, latestSequence uint64) *es_models.SearchQuery {
|
||||
query := es_models.NewSearchQuery().
|
||||
AggregateTypeFilter(model.OrgAggregate).
|
||||
LatestSequenceFilter(latestSequence).
|
||||
AggregateIDFilter(orgID)
|
||||
return query
|
||||
}
|
||||
|
||||
func (es *OrgEventstore) OrgMemberByIDs(ctx context.Context, member *org_model.OrgMember) (*org_model.OrgMember, error) {
|
||||
if member == nil || member.UserID == "" || member.AggregateID == "" {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-ld93d", "member not set")
|
||||
|
@@ -0,0 +1,41 @@
|
||||
package eventsourcing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore/mock"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||
repo_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||
"github.com/golang/mock/gomock"
|
||||
)
|
||||
|
||||
func GetMockedEventstoreComplexity(ctrl *gomock.Controller, mockEs *mock.MockEventstore) *OrgEventstore {
|
||||
return &OrgEventstore{
|
||||
Eventstore: mockEs,
|
||||
}
|
||||
}
|
||||
|
||||
func GetMockChangesOrgOK(ctrl *gomock.Controller) *OrgEventstore {
|
||||
org := model.Org{
|
||||
Name: "MusterOrg",
|
||||
Domain: "myDomain",
|
||||
}
|
||||
data, err := json.Marshal(org)
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
events := []*es_models.Event{
|
||||
&es_models.Event{AggregateID: "AggregateIDApp", Sequence: 1, AggregateType: repo_model.OrgAggregate, Data: data},
|
||||
}
|
||||
mockEs := mock.NewMockEventstore(ctrl)
|
||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||
return GetMockedEventstoreComplexity(ctrl, mockEs)
|
||||
}
|
||||
|
||||
func GetMockChangesOrgNoEvents(ctrl *gomock.Controller) *OrgEventstore {
|
||||
events := []*es_models.Event{}
|
||||
mockEs := mock.NewMockEventstore(ctrl)
|
||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||
return GetMockedEventstoreComplexity(ctrl, mockEs)
|
||||
}
|
@@ -2,12 +2,15 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
es_mock "github.com/caos/zitadel/internal/eventstore/mock"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
@@ -1025,3 +1028,70 @@ func orgInactiveEvent() *es_models.Event {
|
||||
Type: model.OrgDeactivated,
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangesOrg(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
type args struct {
|
||||
es *OrgEventstore
|
||||
id string
|
||||
lastSequence uint64
|
||||
limit uint64
|
||||
}
|
||||
type res struct {
|
||||
changes *org_model.OrgChanges
|
||||
org *model.Org
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "changes from events, ok",
|
||||
args: args{
|
||||
es: GetMockChangesOrgOK(ctrl),
|
||||
id: "1",
|
||||
lastSequence: 0,
|
||||
limit: 0,
|
||||
},
|
||||
res: res{
|
||||
changes: &org_model.OrgChanges{Changes: []*org_model.OrgChange{&org_model.OrgChange{EventType: "", Sequence: 1, Modifier: ""}}, LastSequence: 1},
|
||||
org: &model.Org{Name: "MusterOrg", Domain: "myDomain"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "changes from events, no events",
|
||||
args: args{
|
||||
es: GetMockChangesOrgNoEvents(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.OrgChanges(nil, tt.args.id, tt.args.lastSequence, tt.args.limit)
|
||||
|
||||
org := &model.Org{}
|
||||
if result != nil && len(result.Changes) > 0 {
|
||||
b, err := json.Marshal(result.Changes[0].Data)
|
||||
json.Unmarshal(b, org)
|
||||
if err != nil {
|
||||
}
|
||||
}
|
||||
if !tt.res.wantErr && result.LastSequence != tt.res.changes.LastSequence && org.Name != tt.res.org.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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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
|
||||
|
||||
|
@@ -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")
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -1,9 +1,11 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
policy_model "github.com/caos/zitadel/internal/policy/model"
|
||||
"time"
|
||||
|
||||
policy_model "github.com/caos/zitadel/internal/policy/model"
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
)
|
||||
@@ -23,6 +25,18 @@ type User struct {
|
||||
PasswordCode *PasswordCode
|
||||
OTP *OTP
|
||||
}
|
||||
type UserChanges struct {
|
||||
Changes []*UserChange
|
||||
LastSequence uint64
|
||||
}
|
||||
|
||||
type UserChange 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 InitUserCode struct {
|
||||
es_models.ObjectRoot
|
||||
|
@@ -2,8 +2,14 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
policy_model "github.com/caos/zitadel/internal/policy/model"
|
||||
"github.com/golang/protobuf/ptypes"
|
||||
|
||||
"github.com/pquerna/otp/totp"
|
||||
|
||||
@@ -264,6 +270,60 @@ func (es *UserEventstore) UnlockUser(ctx context.Context, id string) (*usr_model
|
||||
return model.UserToModel(repoExisting), nil
|
||||
}
|
||||
|
||||
func (es *UserEventstore) UserChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*usr_model.UserChanges, error) {
|
||||
query := ChangesQuery(id, lastSequence)
|
||||
|
||||
events, err := es.Eventstore.FilterEvents(context.Background(), query)
|
||||
if err != nil {
|
||||
logging.Log("EVENT-g9HCv").WithError(err).Warn("eventstore unavailable")
|
||||
return nil, errors.ThrowInternal(err, "EVENT-htuG9", "unable to get current user")
|
||||
}
|
||||
if len(events) == 0 {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-6cAxe", "no objects found")
|
||||
}
|
||||
|
||||
result := make([]*usr_model.UserChange, 0)
|
||||
|
||||
for _, u := range events {
|
||||
creationDate, err := ptypes.TimestampProto(u.CreationDate)
|
||||
logging.Log("EVENT-8GTGS").OnError(err).Debug("unable to parse timestamp")
|
||||
change := &usr_model.UserChange{
|
||||
ChangeDate: creationDate,
|
||||
EventType: u.Type.String(),
|
||||
Modifier: u.EditorUser,
|
||||
Sequence: u.Sequence,
|
||||
}
|
||||
|
||||
userDummy := model.Profile{}
|
||||
if u.Data != nil {
|
||||
if err := json.Unmarshal(u.Data, &userDummy); err != nil {
|
||||
log.Println("Error getting data!", err.Error())
|
||||
}
|
||||
}
|
||||
change.Data = userDummy
|
||||
|
||||
result = append(result, change)
|
||||
if lastSequence < u.Sequence {
|
||||
lastSequence = u.Sequence
|
||||
}
|
||||
}
|
||||
|
||||
changes := &usr_model.UserChanges{
|
||||
Changes: result,
|
||||
LastSequence: lastSequence,
|
||||
}
|
||||
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
func ChangesQuery(userID string, latestSequence uint64) *es_models.SearchQuery {
|
||||
query := es_models.NewSearchQuery().
|
||||
AggregateTypeFilter(model.UserAggregate).
|
||||
LatestSequenceFilter(latestSequence).
|
||||
AggregateIDFilter(userID)
|
||||
return query
|
||||
}
|
||||
|
||||
func (es *UserEventstore) InitializeUserCodeByID(ctx context.Context, userID string) (*usr_model.InitUserCode, error) {
|
||||
if userID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-d8diw", "userID missing")
|
||||
|
@@ -2,9 +2,10 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
|
||||
mock_cache "github.com/caos/zitadel/internal/cache/mock"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/eventstore/mock"
|
||||
@@ -456,3 +457,34 @@ func GetMockManipulateUserNoEventsWithPw(ctrl *gomock.Controller) *UserEventstor
|
||||
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
|
||||
return GetMockedEventstoreWithPw(ctrl, mockEs, false, false, false, true)
|
||||
}
|
||||
|
||||
func GetMockedEventstoreComplexity(ctrl *gomock.Controller, mockEs *mock.MockEventstore) *UserEventstore {
|
||||
return &UserEventstore{
|
||||
Eventstore: mockEs,
|
||||
}
|
||||
}
|
||||
|
||||
func GetMockChangesUserOK(ctrl *gomock.Controller) *UserEventstore {
|
||||
user := model.Profile{
|
||||
FirstName: "Hans",
|
||||
LastName: "Muster",
|
||||
UserName: "HansMuster",
|
||||
}
|
||||
data, err := json.Marshal(user)
|
||||
if err != nil {
|
||||
|
||||
}
|
||||
events := []*es_models.Event{
|
||||
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, AggregateType: model.UserAggregate, Data: data},
|
||||
}
|
||||
mockEs := mock.NewMockEventstore(ctrl)
|
||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||
return GetMockedEventstoreComplexity(ctrl, mockEs)
|
||||
}
|
||||
|
||||
func GetMockChangesUserNoEvents(ctrl *gomock.Controller) *UserEventstore {
|
||||
events := []*es_models.Event{}
|
||||
mockEs := mock.NewMockEventstore(ctrl)
|
||||
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
|
||||
return GetMockedEventstoreComplexity(ctrl, mockEs)
|
||||
}
|
||||
|
@@ -2,11 +2,13 @@ package eventsourcing
|
||||
|
||||
import (
|
||||
"context"
|
||||
policy_model "github.com/caos/zitadel/internal/policy/model"
|
||||
"encoding/json"
|
||||
"net"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
policy_model "github.com/caos/zitadel/internal/policy/model"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/auth"
|
||||
@@ -3211,3 +3213,71 @@ func TestRemoveOTP(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestChangesUser(t *testing.T) {
|
||||
ctrl := gomock.NewController(t)
|
||||
type args struct {
|
||||
es *UserEventstore
|
||||
id string
|
||||
secId string
|
||||
lastSequence uint64
|
||||
limit uint64
|
||||
}
|
||||
type res struct {
|
||||
changes *model.UserChanges
|
||||
user *model.Profile
|
||||
wantErr bool
|
||||
errFunc func(err error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "changes from events, ok",
|
||||
args: args{
|
||||
es: GetMockChangesUserOK(ctrl),
|
||||
id: "1",
|
||||
lastSequence: 0,
|
||||
limit: 0,
|
||||
},
|
||||
res: res{
|
||||
changes: &model.UserChanges{Changes: []*model.UserChange{&model.UserChange{EventType: "", Sequence: 1, Modifier: ""}}, LastSequence: 1},
|
||||
user: &model.Profile{FirstName: "Hans", LastName: "Muster", UserName: "HansMuster"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "changes from events, no events",
|
||||
args: args{
|
||||
es: GetMockChangesUserNoEvents(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.UserChanges(nil, tt.args.id, tt.args.lastSequence, tt.args.limit)
|
||||
|
||||
user := &model.Profile{}
|
||||
if result != nil && len(result.Changes) > 0 {
|
||||
b, err := json.Marshal(result.Changes[0].Data)
|
||||
json.Unmarshal(b, user)
|
||||
if err != nil {
|
||||
}
|
||||
}
|
||||
if !tt.res.wantErr && result.LastSequence != tt.res.changes.LastSequence && user.UserName != tt.res.user.UserName {
|
||||
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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user