feat: project commands more requests (#47)

* feat: eventstore repository

* fix: remove gorm

* version

* feat: pkg

* feat: add some files for project

* feat: eventstore without eventstore-lib

* rename files

* gnueg

* fix: key json

* fix: add object

* fix: change imports

* fix: internal models

* fix: some imports

* fix: global model

* fix: add some functions on repo

* feat(eventstore): sdk

* fix(eventstore): search query

* fix(eventstore): rename app to eventstore

* delete empty test

* remove unused func

* merge master

* fix(eventstore): tests

* fix(models): delete unused struct

* fix: some funcitons

* feat(eventstore): implemented push events

* fix: move project eventstore to project package

* fix: change project eventstore funcs

* feat(eventstore): overwrite context data

* fix: change project eventstore

* fix: add project repo to mgmt server

* feat(types): SQL-config

* fix: commented code

* feat(eventstore): options to overwrite editor

* feat: auth interceptor and cockroach migrations

* fix: migrations

* fix: fix filter

* fix: not found on getbyid

* fix: add sequence

* fix: add some tests

* fix(eventstore): nullable sequence

* fix: add some tests

* merge

* fix: add some tests

* fix(migrations): correct statements for sequence

* fix: add some tests

* fix: add some tests

* fix: changes from mr

* Update internal/eventstore/models/field.go

Co-Authored-By: livio-a <livio.a@gmail.com>

* fix(eventstore): code quality

* fix: add types to aggregate/Event-types

* fix(eventstore): rename modifier* to editor*

* fix(eventstore): delete editor_org

* fix(migrations): remove editor_org field,
rename modifier_* to editor_*

* fix: generate files

* fix(eventstore): tests

* fix(eventstore): rename modifier to editor

* fix(migrations): add cluster migration,
fix(migrations): fix typo of host in clean clsuter

* fix(eventstore): move health

* fix(eventstore): AggregateTypeFilter aggregateType as param

* code quality

* feat: add member funcs

* feat: add member model

* feat: add member events

* feat: add member repo model

* fix: project member funcs

* fix: add tests

* fix: add tests

* feat: implement member requests

* fix: merge master

* fix: read existing in project repo

* fix: fix tests

* fix: use eventstore sdk

* Update internal/project/model/project_member.go

Co-Authored-By: Silvan <silvan.reusser@gmail.com>

* fix: use get project func

* fix: return err not nil

* fix: change error to caos err

Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
Co-authored-by: livio-a <livio.a@gmail.com>
This commit is contained in:
Fabi 2020-04-15 17:11:42 +02:00 committed by GitHub
parent d0e72713fc
commit bd33b54ac5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 12409 additions and 11638 deletions

View File

@ -31,28 +31,31 @@ func (repo *ProjectRepo) CreateProject(ctx context.Context, name string) (*proj_
}
func (repo *ProjectRepo) UpdateProject(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) {
existingProject, err := repo.ProjectByID(ctx, project.ID)
if err != nil {
return nil, err
}
return repo.ProjectEvents.UpdateProject(ctx, existingProject, project)
return repo.ProjectEvents.UpdateProject(ctx, project)
}
func (repo *ProjectRepo) DeactivateProject(ctx context.Context, id string) (*proj_model.Project, error) {
project, err := repo.ProjectByID(ctx, id)
if err != nil {
return nil, err
}
return repo.ProjectEvents.DeactivateProject(ctx, project)
return repo.ProjectEvents.DeactivateProject(ctx, id)
}
func (repo *ProjectRepo) ReactivateProject(ctx context.Context, id string) (*proj_model.Project, error) {
project, err := repo.ProjectByID(ctx, id)
if err != nil {
return nil, err
}
return repo.ProjectEvents.ReactivateProject(ctx, project)
return repo.ProjectEvents.ReactivateProject(ctx, id)
}
func (repo *ProjectRepo) ProjectMemberByID(ctx context.Context, projectID, userID string) (member *proj_model.ProjectMember, err error) {
member = proj_model.NewProjectMember(projectID, userID)
return repo.ProjectEvents.ProjectMemberByIDs(ctx, member)
}
func (repo *ProjectRepo) AddProjectMember(ctx context.Context, member *proj_model.ProjectMember) (*proj_model.ProjectMember, error) {
return repo.ProjectEvents.AddProjectMember(ctx, member)
}
func (repo *ProjectRepo) ChangeProjectMember(ctx context.Context, member *proj_model.ProjectMember) (*proj_model.ProjectMember, error) {
return repo.ProjectEvents.ChangeProjectMember(ctx, member)
}
func (repo *ProjectRepo) RemoveProjectMember(ctx context.Context, projectID, userID string) error {
member := proj_model.NewProjectMember(projectID, userID)
return repo.ProjectEvents.RemoveProjectMember(ctx, member)
}

View File

@ -11,4 +11,9 @@ type ProjectRepository interface {
UpdateProject(ctx context.Context, project *model.Project) (*model.Project, error)
DeactivateProject(ctx context.Context, id string) (*model.Project, error)
ReactivateProject(ctx context.Context, id string) (*model.Project, error)
ProjectMemberByID(ctx context.Context, projectID, userID string) (*model.ProjectMember, error)
AddProjectMember(ctx context.Context, member *model.ProjectMember) (*model.ProjectMember, error)
ChangeProjectMember(ctx context.Context, member *model.ProjectMember) (*model.ProjectMember, error)
RemoveProjectMember(ctx context.Context, projectID, userID string) error
}

View File

@ -8,8 +8,9 @@ import (
type Project struct {
es_models.ObjectRoot
State ProjectState
Name string
State ProjectState
Name string
Members []*ProjectMember
}
type ProjectState in_model.Enum
@ -33,3 +34,12 @@ func (p *Project) IsValid() bool {
}
return true
}
func (p *Project) ContainsMember(member *ProjectMember) bool {
for _, m := range p.Members {
if m.UserID == member.UserID {
return true
}
}
return false
}

View File

@ -0,0 +1,18 @@
package model
import es_models "github.com/caos/zitadel/internal/eventstore/models"
type ProjectMember struct {
es_models.ObjectRoot
UserID string
Roles []string
}
func NewProjectMember(projectID, userID string) *ProjectMember {
return &ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: projectID}, UserID: userID}
}
func (p *ProjectMember) IsValid() bool {
return p.ID != "" && p.UserID != "" && len(p.Roles) != 0
}

View File

@ -2,6 +2,7 @@ package eventsourcing
import (
"context"
"github.com/caos/zitadel/internal/eventstore/models"
caos_errs "github.com/caos/zitadel/internal/errors"
es_int "github.com/caos/zitadel/internal/eventstore"
@ -51,15 +52,19 @@ func (es *ProjectEventstore) CreateProject(ctx context.Context, project *proj_mo
return ProjectToModel(repoProject), nil
}
func (es *ProjectEventstore) UpdateProject(ctx context.Context, existingProject *proj_model.Project, project *proj_model.Project) (*proj_model.Project, error) {
func (es *ProjectEventstore) UpdateProject(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) {
if !project.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Name is required")
}
existingProject, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: project.ID, Sequence: 0}})
if err != nil {
return nil, err
}
repoExisting := ProjectFromModel(existingProject)
repoNew := ProjectFromModel(project)
updateAggregate := ProjectUpdateAggregate(es.AggregateCreator(), repoExisting, repoNew)
err := es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate)
err = es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate)
if err != nil {
return nil, err
}
@ -67,7 +72,11 @@ func (es *ProjectEventstore) UpdateProject(ctx context.Context, existingProject
return ProjectToModel(repoExisting), nil
}
func (es *ProjectEventstore) DeactivateProject(ctx context.Context, existing *proj_model.Project) (*proj_model.Project, error) {
func (es *ProjectEventstore) DeactivateProject(ctx context.Context, id string) (*proj_model.Project, error) {
existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: id, Sequence: 0}})
if err != nil {
return nil, err
}
if !existing.IsActive() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die45", "project must be active")
}
@ -78,7 +87,11 @@ func (es *ProjectEventstore) DeactivateProject(ctx context.Context, existing *pr
return ProjectToModel(repoExisting), nil
}
func (es *ProjectEventstore) ReactivateProject(ctx context.Context, existing *proj_model.Project) (*proj_model.Project, error) {
func (es *ProjectEventstore) ReactivateProject(ctx context.Context, id string) (*proj_model.Project, error) {
existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: id, Sequence: 0}})
if err != nil {
return nil, err
}
if existing.IsActive() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-die45", "project must be inactive")
}
@ -88,3 +101,87 @@ func (es *ProjectEventstore) ReactivateProject(ctx context.Context, existing *pr
es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, aggregate)
return ProjectToModel(repoExisting), nil
}
func (es *ProjectEventstore) ProjectMemberByIDs(ctx context.Context, member *proj_model.ProjectMember) (*proj_model.ProjectMember, error) {
if member.UserID == "" {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-ld93d", "userID missing")
}
project, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: member.ID, Sequence: 0}})
if err != nil {
return nil, err
}
for _, m := range project.Members {
if m.UserID == member.UserID {
return m, nil
}
}
return nil, caos_errs.ThrowInternal(nil, "EVENT-3udjs", "Could not find member in list")
}
func (es *ProjectEventstore) AddProjectMember(ctx context.Context, member *proj_model.ProjectMember) (*proj_model.ProjectMember, error) {
if !member.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "UserID and Roles are required")
}
existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: member.ID, Sequence: 0}})
if err != nil {
return nil, err
}
if existing.ContainsMember(member) {
return nil, caos_errs.ThrowAlreadyExists(nil, "EVENT-idke6", "User is already member of this Project")
}
repoProject := ProjectFromModel(existing)
repoMember := ProjectMemberFromModel(member)
addAggregate := ProjectMemberAddedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoMember)
err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, addAggregate)
for _, m := range repoProject.Members {
if m.UserID == member.UserID {
return ProjectMemberToModel(m), nil
}
}
return nil, caos_errs.ThrowInternal(nil, "EVENT-3udjs", "Could not find member in list")
}
func (es *ProjectEventstore) ChangeProjectMember(ctx context.Context, member *proj_model.ProjectMember) (*proj_model.ProjectMember, error) {
if !member.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "UserID and Roles are required")
}
existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: member.ID, Sequence: 0}})
if err != nil {
return nil, err
}
if !existing.ContainsMember(member) {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-oe39f", "User is not member of this project")
}
repoProject := ProjectFromModel(existing)
repoMember := ProjectMemberFromModel(member)
projectAggregate := ProjectMemberChangedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoMember)
err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate)
for _, m := range repoProject.Members {
if m.UserID == member.UserID {
return ProjectMemberToModel(m), nil
}
}
return nil, caos_errs.ThrowInternal(nil, "EVENT-3udjs", "Could not find member in list")
}
func (es *ProjectEventstore) RemoveProjectMember(ctx context.Context, member *proj_model.ProjectMember) error {
if member.UserID == "" {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-d43fs", "UserID and Roles are required")
}
existing, err := es.ProjectByID(ctx, &proj_model.Project{ObjectRoot: models.ObjectRoot{ID: member.ID, Sequence: 0}})
if err != nil {
return err
}
if !existing.ContainsMember(member) {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-swf34", "User is not member of this project")
}
repoProject := ProjectFromModel(existing)
repoMember := ProjectMemberFromModel(member)
projectAggregate := ProjectMemberRemovedAggregate(es.Eventstore.AggregateCreator(), repoProject, repoMember)
err = es_sdk.Push(ctx, es.PushAggregates, repoProject.AppendEvents, projectAggregate)
return err
}

View File

@ -26,8 +26,61 @@ func GetMockProjectByIDNoEvents(ctrl *gomock.Controller) *ProjectEventstore {
}
func GetMockManipulateProject(ctrl *gomock.Controller) *ProjectEventstore {
data, _ := json.Marshal(Project{Name: "Name"})
events := []*es_models.Event{
&es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectAdded, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return &ProjectEventstore{Eventstore: mockEs}
}
func GetMockManipulateInactiveProject(ctrl *gomock.Controller) *ProjectEventstore {
data, _ := json.Marshal(Project{Name: "Name"})
events := []*es_models.Event{
&es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectAdded, Data: data},
&es_models.Event{AggregateID: "ID", Sequence: 2, Type: model.ProjectDeactivated, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return &ProjectEventstore{Eventstore: mockEs}
}
func GetMockManipulateProjectWithMember(ctrl *gomock.Controller) *ProjectEventstore {
data, _ := json.Marshal(Project{Name: "Name"})
memberData, _ := json.Marshal(ProjectMember{UserID: "UserID", Roles: []string{"Role"}})
events := []*es_models.Event{
&es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectAdded, Data: data},
&es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectMemberAdded, Data: memberData},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return &ProjectEventstore{Eventstore: mockEs}
}
func GetMockManipulateProjectNoEvents(ctrl *gomock.Controller) *ProjectEventstore {
events := []*es_models.Event{}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return &ProjectEventstore{Eventstore: mockEs}
}
func GetMockProjectMemberByIDsOK(ctrl *gomock.Controller) *ProjectEventstore {
projectData, _ := json.Marshal(Project{Name: "Name"})
memberData, _ := json.Marshal(ProjectMember{UserID: "UserID", Roles: []string{"Role"}})
events := []*es_models.Event{
&es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectAdded, Data: projectData},
&es_models.Event{AggregateID: "ID", Sequence: 1, Type: model.ProjectMemberAdded, Data: memberData},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return &ProjectEventstore{Eventstore: mockEs}
}

View File

@ -91,7 +91,7 @@ func TestCreateProject(t *testing.T) {
res res
}{
{
name: "project from events, ok",
name: "create project, ok",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
@ -134,10 +134,9 @@ func TestCreateProject(t *testing.T) {
func TestUpdateProject(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *ProjectEventstore
ctx context.Context
existing *model.Project
new *model.Project
es *ProjectEventstore
ctx context.Context
new *model.Project
}
type res struct {
project *model.Project
@ -150,34 +149,44 @@ func TestUpdateProject(t *testing.T) {
res res
}{
{
name: "project from events, ok",
name: "update project, ok",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name"},
new: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "NameNew"},
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "NameNew"},
},
res: res{
project: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "NameNew"},
},
},
{
name: "create project no name",
name: "update project no name",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name"},
new: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: ""},
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: ""},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing project not found",
args: args{
es: GetMockManipulateProjectNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "NameNew"},
},
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.UpdateProject(tt.args.ctx, tt.args.existing, tt.args.new)
result, err := tt.args.es.UpdateProject(tt.args.ctx, tt.args.new)
if !tt.res.wantErr && result.ID == "" {
t.Errorf("result has no id")
@ -224,7 +233,7 @@ func TestDeactivateProject(t *testing.T) {
{
name: "deactivate project with inactive state",
args: args{
es: GetMockManipulateProject(ctrl),
es: GetMockManipulateInactiveProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name", State: model.Inactive},
},
@ -233,10 +242,22 @@ func TestDeactivateProject(t *testing.T) {
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing not found",
args: args{
es: GetMockManipulateProjectNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name", State: model.Active},
},
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.DeactivateProject(tt.args.ctx, tt.args.existing)
result, err := tt.args.es.DeactivateProject(tt.args.ctx, tt.args.existing.ID)
if !tt.res.wantErr && result.ID == "" {
t.Errorf("result has no id")
@ -270,9 +291,9 @@ func TestReactivateProject(t *testing.T) {
res res
}{
{
name: "deactivate project, ok",
name: "reactivate project, ok",
args: args{
es: GetMockManipulateProject(ctrl),
es: GetMockManipulateInactiveProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name", State: model.Inactive},
},
@ -281,7 +302,7 @@ func TestReactivateProject(t *testing.T) {
},
},
{
name: "deactivate project with inactive state",
name: "reactivate project with inactive state",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
@ -292,10 +313,22 @@ func TestReactivateProject(t *testing.T) {
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing not found",
args: args{
es: GetMockManipulateProjectNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Name: "Name", State: model.Active},
},
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.ReactivateProject(tt.args.ctx, tt.args.existing)
result, err := tt.args.es.ReactivateProject(tt.args.ctx, tt.args.existing.ID)
if !tt.res.wantErr && result.ID == "" {
t.Errorf("result has no id")
@ -309,3 +342,361 @@ func TestReactivateProject(t *testing.T) {
})
}
}
func TestProjectMemberByIDs(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *ProjectEventstore
member *model.ProjectMember
}
type res struct {
member *model.ProjectMember
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "project member from events, ok",
args: args{
es: GetMockProjectMemberByIDsOK(ctrl),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID"},
},
res: res{
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"Role"}},
},
},
{
name: "project member from events, no events",
args: args{
es: GetMockProjectByIDNoEvents(ctrl),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID"},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
{
name: "project member from events, no id",
args: args{
es: GetMockProjectByIDNoEvents(ctrl),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}},
},
res: res{
wantErr: true,
errFunc: func(err error) bool {
return caos_errs.IsPreconditionFailed(err)
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.ProjectMemberByIDs(nil, tt.args.member)
if !tt.res.wantErr && result.ID != tt.res.member.ID {
t.Errorf("got wrong result id: expected: %v, actual: %v ", tt.res.member.ID, result.ID)
}
if !tt.res.wantErr && result.UserID != tt.res.member.UserID {
t.Errorf("got wrong result userid: expected: %v, actual: %v ", tt.res.member.UserID, result.UserID)
}
if !tt.res.wantErr && len(result.Roles) != len(tt.res.member.Roles) {
t.Errorf("got wrong result roles: expected: %v, actual: %v ", tt.res.member.Roles, result.Roles)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestAddProjectMember(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *ProjectEventstore
ctx context.Context
member *model.ProjectMember
}
type res struct {
result *model.ProjectMember
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "add project member, ok",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"Roles"}},
},
res: res{
result: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"Roles"}},
},
},
{
name: "no userid",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Roles: []string{"Roles"}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "no roles",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID"},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "member already existing",
args: args{
es: GetMockManipulateProjectWithMember(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"Roles"}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsErrorAlreadyExists,
},
},
{
name: "existing project not found",
args: args{
es: GetMockManipulateProjectNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"Roles"}},
},
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.AddProjectMember(tt.args.ctx, tt.args.member)
if !tt.res.wantErr && result.ID == "" {
t.Errorf("result has no id")
}
if !tt.res.wantErr && result.UserID != tt.res.result.UserID {
t.Errorf("got wrong result userid: expected: %v, actual: %v ", tt.res.result.UserID, result.UserID)
}
if !tt.res.wantErr && len(result.Roles) != len(tt.res.result.Roles) {
t.Errorf("got wrong result roles: expected: %v, actual: %v ", tt.res.result.Roles, result.Roles)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestChangeProjectMember(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *ProjectEventstore
ctx context.Context
member *model.ProjectMember
}
type res struct {
result *model.ProjectMember
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "add project member, ok",
args: args{
es: GetMockManipulateProjectWithMember(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"ChangeRoles"}},
},
res: res{
result: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"Roles"}},
},
},
{
name: "no userid",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Roles: []string{"ChangeRoles"}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "no roles",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID"},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "member not existing",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"Roles"}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing not found",
args: args{
es: GetMockManipulateProjectNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"ChangeRoles"}},
},
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.ChangeProjectMember(tt.args.ctx, tt.args.member)
if !tt.res.wantErr && result.ID == "" {
t.Errorf("result has no id")
}
if !tt.res.wantErr && result.UserID != tt.res.result.UserID {
t.Errorf("got wrong result userid: expected: %v, actual: %v ", tt.res.result.UserID, result.UserID)
}
if !tt.res.wantErr && len(result.Roles) != len(tt.res.result.Roles) {
t.Errorf("got wrong result roles: expected: %v, actual: %v ", tt.res.result.Roles, result.Roles)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestRemoveProjectMember(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *ProjectEventstore
ctx context.Context
existing *model.Project
member *model.ProjectMember
}
type res struct {
result *model.ProjectMember
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "remove project member, ok",
args: args{
es: GetMockManipulateProjectWithMember(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{
ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1},
Name: "Name",
Members: []*model.ProjectMember{&model.ProjectMember{UserID: "UserID", Roles: []string{"Roles"}}},
},
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID"},
},
res: res{
result: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"Roles"}},
},
},
{
name: "no userid",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{
ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1},
Name: "Name",
Members: []*model.ProjectMember{&model.ProjectMember{UserID: "UserID", Roles: []string{"Roles"}}},
},
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, Roles: []string{"ChangeRoles"}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "member not existing",
args: args{
es: GetMockManipulateProject(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
existing: &model.Project{
ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1},
Name: "Name",
},
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"Roles"}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing not found",
args: args{
es: GetMockManipulateProjectNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
member: &model.ProjectMember{ObjectRoot: es_models.ObjectRoot{ID: "ID", Sequence: 1}, UserID: "UserID", Roles: []string{"ChangeRoles"}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := tt.args.es.RemoveProjectMember(tt.args.ctx, tt.args.member)
if !tt.res.wantErr && err != nil {
t.Errorf("should not get err")
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}

View File

@ -2,6 +2,7 @@ package eventsourcing
import (
"encoding/json"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/logging"
es_models "github.com/caos/zitadel/internal/eventstore/models"
@ -14,11 +15,27 @@ const (
type Project struct {
es_models.ObjectRoot
Name string `json:"name,omitempty"`
State int32 `json:"-"`
Name string `json:"name,omitempty"`
State int32 `json:"-"`
Members []*ProjectMember `json:"-"`
}
type ProjectMember struct {
es_models.ObjectRoot
UserID string `json:"userId,omitempty"`
Roles []string `json:"roles,omitempty"`
}
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 {
members := ProjectMembersFromModel(project.Members)
return &Project{
ObjectRoot: es_models.ObjectRoot{
ID: project.ObjectRoot.ID,
@ -26,12 +43,14 @@ func ProjectFromModel(project *model.Project) *Project {
ChangeDate: project.ChangeDate,
CreationDate: project.CreationDate,
},
Name: project.Name,
State: model.ProjectStateToInt(project.State),
Name: project.Name,
State: model.ProjectStateToInt(project.State),
Members: members,
}
}
func ProjectToModel(project *Project) *model.Project {
members := ProjectMembersToModel(project.Members)
return &model.Project{
ObjectRoot: es_models.ObjectRoot{
ID: project.ID,
@ -39,8 +58,51 @@ func ProjectToModel(project *Project) *model.Project {
CreationDate: project.CreationDate,
Sequence: project.Sequence,
},
Name: project.Name,
State: model.ProjectStateFromInt(project.State),
Name: project.Name,
State: model.ProjectStateFromInt(project.State),
Members: members,
}
}
func ProjectMembersToModel(members []*ProjectMember) []*model.ProjectMember {
convertedMembers := make([]*model.ProjectMember, len(members))
for i, m := range members {
convertedMembers[i] = ProjectMemberToModel(m)
}
return convertedMembers
}
func ProjectMembersFromModel(members []*model.ProjectMember) []*ProjectMember {
convertedMembers := make([]*ProjectMember, len(members))
for i, m := range members {
convertedMembers[i] = ProjectMemberFromModel(m)
}
return convertedMembers
}
func ProjectMemberFromModel(member *model.ProjectMember) *ProjectMember {
return &ProjectMember{
ObjectRoot: es_models.ObjectRoot{
ID: member.ObjectRoot.ID,
Sequence: member.Sequence,
ChangeDate: member.ChangeDate,
CreationDate: member.CreationDate,
},
UserID: member.UserID,
Roles: member.Roles,
}
}
func ProjectMemberToModel(member *ProjectMember) *model.ProjectMember {
return &model.ProjectMember{
ObjectRoot: es_models.ObjectRoot{
ID: member.ID,
ChangeDate: member.ChangeDate,
CreationDate: member.CreationDate,
Sequence: member.Sequence,
},
UserID: member.UserID,
Roles: member.Roles,
}
}
@ -52,14 +114,6 @@ 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 {
@ -84,8 +138,13 @@ func (p *Project) AppendEvent(event *es_models.Event) error {
return p.appendDeactivatedEvent()
case model.ProjectReactivated:
return p.appendReactivatedEvent()
case model.ProjectMemberAdded:
return p.appendAddMemberEvent(event)
case model.ProjectMemberChanged:
return p.appendChangeMemberEvent(event)
case model.ProjectMemberRemoved:
return p.appendRemoveMemberEvent(event)
}
return nil
}
@ -98,3 +157,51 @@ func (p *Project) appendReactivatedEvent() error {
p.State = model.ProjectStateToInt(model.Active)
return nil
}
func (p *Project) appendAddMemberEvent(event *es_models.Event) error {
member, err := getMemberData(event)
if err != nil {
return err
}
member.ObjectRoot.CreationDate = event.CreationDate
p.Members = append(p.Members, member)
return nil
}
func (p *Project) appendChangeMemberEvent(event *es_models.Event) error {
member, err := getMemberData(event)
if err != nil {
return err
}
for i, m := range p.Members {
if m.UserID == member.UserID {
p.Members[i] = member
}
}
return nil
}
func (p *Project) appendRemoveMemberEvent(event *es_models.Event) error {
member, err := getMemberData(event)
if err != nil {
return err
}
for i, m := range p.Members {
if m.UserID == member.UserID {
p.Members[i] = p.Members[len(p.Members)-1]
p.Members[len(p.Members)-1] = nil
p.Members = p.Members[:len(p.Members)-1]
}
}
return nil
}
func getMemberData(event *es_models.Event) (*ProjectMember, error) {
member := &ProjectMember{}
member.ObjectRoot.AppendEvent(event)
if err := json.Unmarshal(event.Data, member); err != nil {
logging.Log("EVEN-e4dkp").WithError(err).Error("could not unmarshal event data")
return nil, errors.ThrowInternal(err, "EVENT-83js6", "could not unmarshal event data")
}
return member, nil
}

View File

@ -212,3 +212,115 @@ func TestChanges(t *testing.T) {
})
}
}
func TestAppendAddMemberEvent(t *testing.T) {
type args struct {
project *Project
member *ProjectMember
event *es_models.Event
}
tests := []struct {
name string
args args
result *Project
}{
{
name: "append add member event",
args: args{
project: &Project{},
member: &ProjectMember{UserID: "UserID", Roles: []string{"Role"}},
event: &es_models.Event{},
},
result: &Project{Members: []*ProjectMember{&ProjectMember{UserID: "UserID", Roles: []string{"Role"}}}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.member != nil {
data, _ := json.Marshal(tt.args.member)
tt.args.event.Data = data
}
tt.args.project.appendAddMemberEvent(tt.args.event)
if len(tt.args.project.Members) != 1 {
t.Errorf("got wrong result should have one member actual: %v ", len(tt.args.project.Members))
}
if tt.args.project.Members[0] == tt.result.Members[0] {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.Members[0], tt.args.project.Members[0])
}
})
}
}
func TestAppendChangeMemberEvent(t *testing.T) {
type args struct {
project *Project
member *ProjectMember
event *es_models.Event
}
tests := []struct {
name string
args args
result *Project
}{
{
name: "append change member event",
args: args{
project: &Project{Members: []*ProjectMember{&ProjectMember{UserID: "UserID", Roles: []string{"Role"}}}},
member: &ProjectMember{UserID: "UserID", Roles: []string{"ChangedRole"}},
event: &es_models.Event{},
},
result: &Project{Members: []*ProjectMember{&ProjectMember{UserID: "UserID", Roles: []string{"ChangedRole"}}}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.member != nil {
data, _ := json.Marshal(tt.args.member)
tt.args.event.Data = data
}
tt.args.project.appendChangeMemberEvent(tt.args.event)
if len(tt.args.project.Members) != 1 {
t.Errorf("got wrong result should have one member actual: %v ", len(tt.args.project.Members))
}
if tt.args.project.Members[0] == tt.result.Members[0] {
t.Errorf("got wrong result: expected: %v, actual: %v ", tt.result.Members[0], tt.args.project.Members[0])
}
})
}
}
func TestAppendRemoveMemberEvent(t *testing.T) {
type args struct {
project *Project
member *ProjectMember
event *es_models.Event
}
tests := []struct {
name string
args args
result *Project
}{
{
name: "append remove member event",
args: args{
project: &Project{Members: []*ProjectMember{&ProjectMember{UserID: "UserID", Roles: []string{"Role"}}}},
member: &ProjectMember{UserID: "UserID"},
event: &es_models.Event{},
},
result: &Project{Members: []*ProjectMember{}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.member != nil {
data, _ := json.Marshal(tt.args.member)
tt.args.event.Data = data
}
tt.args.project.appendRemoveMemberEvent(tt.args.event)
if len(tt.args.project.Members) != 0 {
t.Errorf("got wrong result should have one member actual: %v ", len(tt.args.project.Members))
}
})
}
}

View File

@ -89,3 +89,43 @@ func projectStateAggregate(aggCreator *es_models.AggregateCreator, project *Proj
return agg.AppendEvent(state, nil)
}
}
func ProjectMemberAddedAggregate(aggCreator *es_models.AggregateCreator, existing *Project, member *ProjectMember) 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-di38f", "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.ProjectMemberAdded, member)
}
}
func ProjectMemberChangedAggregate(aggCreator *es_models.AggregateCreator, existing *Project, member *ProjectMember) 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-sle3d", "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.ProjectMemberChanged, member)
}
}
func ProjectMemberRemovedAggregate(aggCreator *es_models.AggregateCreator, existing *Project, member *ProjectMember) 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-slo9e", "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.ProjectMemberRemoved, member)
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1220,7 +1220,7 @@
"200": {
"description": "A successful response.",
"schema": {
"properties": {}
"$ref": "#/definitions/v1ProjectMember"
}
}
},
@ -1280,7 +1280,7 @@
"200": {
"description": "A successful response.",
"schema": {
"properties": {}
"$ref": "#/definitions/v1ProjectMember"
}
}
},
@ -3213,7 +3213,7 @@
"200": {
"description": "A successful response.",
"schema": {
"type": "object"
"$ref": "#/definitions/protobufStruct"
}
}
},
@ -3224,6 +3224,19 @@
}
},
"definitions": {
"protobufListValue": {
"type": "object",
"properties": {
"values": {
"type": "array",
"items": {
"$ref": "#/definitions/protobufValue"
},
"description": "Repeated field of dynamically typed values."
}
},
"description": "`ListValue` is a wrapper around a repeated field of values.\n\nThe JSON representation for `ListValue` is JSON array."
},
"protobufNullValue": {
"type": "string",
"enum": [
@ -3232,6 +3245,51 @@
"default": "NULL_VALUE",
"description": "`NullValue` is a singleton enumeration to represent the null value for the\n`Value` type union.\n\n The JSON representation for `NullValue` is JSON `null`.\n\n - NULL_VALUE: Null value."
},
"protobufStruct": {
"type": "object",
"properties": {
"fields": {
"type": "object",
"additionalProperties": {
"$ref": "#/definitions/protobufValue"
},
"description": "Unordered map of dynamically typed values."
}
},
"description": "`Struct` represents a structured data value, consisting of fields\nwhich map to dynamically typed values. In some languages, `Struct`\nmight be supported by a native representation. For example, in\nscripting languages like JS a struct is represented as an\nobject. The details of that representation are described together\nwith the proto support for the language.\n\nThe JSON representation for `Struct` is JSON object."
},
"protobufValue": {
"type": "object",
"properties": {
"null_value": {
"$ref": "#/definitions/protobufNullValue",
"description": "Represents a null value."
},
"number_value": {
"type": "number",
"format": "double",
"description": "Represents a double value."
},
"string_value": {
"type": "string",
"description": "Represents a string value."
},
"bool_value": {
"type": "boolean",
"format": "boolean",
"description": "Represents a boolean value."
},
"struct_value": {
"$ref": "#/definitions/protobufStruct",
"description": "Represents a structured value."
},
"list_value": {
"$ref": "#/definitions/protobufListValue",
"description": "Represents a repeated `Value`."
}
},
"description": "`Value` represents a dynamically typed value which can be either\nnull, a number, a string, a boolean, a recursive struct value, or a\nlist of values. A producer of value is expected to set one of that\nvariants, absence of any variant indicates an error.\n\nThe JSON representation for `Value` is JSON value."
},
"v1AddOrgMemberRequest": {
"type": "object",
"properties": {
@ -3490,7 +3548,7 @@
"type": "string"
},
"data": {
"type": "object"
"$ref": "#/definitions/protobufStruct"
}
}
},

View File

@ -78,14 +78,14 @@ func (mr *MockManagementServiceClientMockRecorder) AddProjectGrantMember(arg0, a
}
// AddProjectMember mocks base method
func (m *MockManagementServiceClient) AddProjectMember(arg0 context.Context, arg1 *grpc.ProjectMemberAdd, arg2 ...grpc0.CallOption) (*emptypb.Empty, error) {
func (m *MockManagementServiceClient) AddProjectMember(arg0 context.Context, arg1 *grpc.ProjectMemberAdd, arg2 ...grpc0.CallOption) (*grpc.ProjectMember, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "AddProjectMember", varargs...)
ret0, _ := ret[0].(*emptypb.Empty)
ret0, _ := ret[0].(*grpc.ProjectMember)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@ -198,14 +198,14 @@ func (mr *MockManagementServiceClientMockRecorder) ChangeProjectGrantMember(arg0
}
// ChangeProjectMember mocks base method
func (m *MockManagementServiceClient) ChangeProjectMember(arg0 context.Context, arg1 *grpc.ProjectMemberChange, arg2 ...grpc0.CallOption) (*emptypb.Empty, error) {
func (m *MockManagementServiceClient) ChangeProjectMember(arg0 context.Context, arg1 *grpc.ProjectMemberChange, arg2 ...grpc0.CallOption) (*grpc.ProjectMember, error) {
m.ctrl.T.Helper()
varargs := []interface{}{arg0, arg1}
for _, a := range arg2 {
varargs = append(varargs, a)
}
ret := m.ctrl.Call(m, "ChangeProjectMember", varargs...)
ret0, _ := ret[0].(*emptypb.Empty)
ret0, _ := ret[0].(*grpc.ProjectMember)
ret1, _ := ret[1].(error)
return ret0, ret1
}

View File

@ -14,14 +14,23 @@ func (s *Server) SearchProjectMembers(ctx context.Context, request *ProjectMembe
return nil, errors.ThrowUnimplemented(nil, "GRPC-PLr84", "Not implemented")
}
func (s *Server) AddProjectMember(ctx context.Context, in *ProjectMemberAdd) (*empty.Empty, error) {
return nil, errors.ThrowUnimplemented(nil, "GRPC-c2dks", "Not implemented")
func (s *Server) AddProjectMember(ctx context.Context, in *ProjectMemberAdd) (*ProjectMember, error) {
member, err := s.project.AddProjectMember(ctx, projectMemberAddToModel(in))
if err != nil {
return nil, err
}
return projectMemberFromModel(member), nil
}
func (s *Server) ChangeProjectMember(ctx context.Context, in *ProjectMemberChange) (*empty.Empty, error) {
return nil, errors.ThrowUnimplemented(nil, "GRPC-cms47", "Not implemented")
func (s *Server) ChangeProjectMember(ctx context.Context, in *ProjectMemberChange) (*ProjectMember, error) {
member, err := s.project.ChangeProjectMember(ctx, projectMemberChangeToModel(in))
if err != nil {
return nil, err
}
return projectMemberFromModel(member), nil
}
func (s *Server) RemoveProjectMember(ctx context.Context, in *ProjectMemberRemove) (*empty.Empty, error) {
return nil, errors.ThrowUnimplemented(nil, "GRPC-olw21", "Not implemented")
err := s.project.RemoveProjectMember(ctx, in.Id, in.UserId)
return &empty.Empty{}, err
}

View File

@ -0,0 +1,44 @@
package grpc
import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/models"
proj_model "github.com/caos/zitadel/internal/project/model"
"github.com/golang/protobuf/ptypes"
)
func projectMemberFromModel(member *proj_model.ProjectMember) *ProjectMember {
creationDate, err := ptypes.TimestampProto(member.CreationDate)
logging.Log("GRPC-kd8re").OnError(err).Debug("unable to parse timestamp")
changeDate, err := ptypes.TimestampProto(member.ChangeDate)
logging.Log("GRPC-dlei3").OnError(err).Debug("unable to parse timestamp")
return &ProjectMember{
CreationDate: creationDate,
ChangeDate: changeDate,
Sequence: member.Sequence,
UserId: member.UserID,
Roles: member.Roles,
}
}
func projectMemberAddToModel(member *ProjectMemberAdd) *proj_model.ProjectMember {
return &proj_model.ProjectMember{
ObjectRoot: models.ObjectRoot{
ID: member.Id,
},
UserID: member.UserId,
Roles: member.Roles,
}
}
func projectMemberChangeToModel(member *ProjectMemberChange) *proj_model.ProjectMember {
return &proj_model.ProjectMember{
ObjectRoot: models.ObjectRoot{
ID: member.Id,
},
UserID: member.UserId,
Roles: member.Roles,
}
}

View File

@ -674,7 +674,7 @@ service ManagementService {
};
}
rpc AddProjectMember(ProjectMemberAdd) returns (google.protobuf.Empty) {
rpc AddProjectMember(ProjectMemberAdd) returns (ProjectMember) {
option (google.api.http) = {
post: "/projects/{id}/members"
body: "*"
@ -686,7 +686,7 @@ service ManagementService {
};
}
rpc ChangeProjectMember(ProjectMemberChange) returns (google.protobuf.Empty) {
rpc ChangeProjectMember(ProjectMemberChange) returns (ProjectMember) {
option (google.api.http) = {
put: "/projects/{id}/members/{user_id}"
body: "*"