feat(changes): add editor (#273)

* fix(changes): add editor to change mapper

* fix(eventstore): only add latest sequence if greater 0 to query

* sort order in request for changes

* fix(changes): map editor for org, app and project
This commit is contained in:
Silvan 2020-06-25 11:25:38 +02:00 committed by GitHub
parent 62b654ea18
commit d947bb1247
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 13484 additions and 14053 deletions

View File

@ -48,6 +48,9 @@ func (q *SearchQuery) EventTypesFilter(types ...EventType) *SearchQuery {
}
func (q *SearchQuery) LatestSequenceFilter(sequence uint64) *SearchQuery {
if sequence == 0 {
return q
}
sortOrder := Operation_Greater
if q.Desc {
sortOrder = Operation_Less

View File

@ -2,10 +2,11 @@ package eventstore
import (
"context"
"strings"
"github.com/caos/zitadel/internal/api/auth"
global_model "github.com/caos/zitadel/internal/model"
"github.com/caos/zitadel/internal/org/repository/view/model"
"strings"
"github.com/caos/zitadel/internal/errors"
mgmt_view "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
@ -77,8 +78,8 @@ func (repo *OrgRepository) RemoveMyOrgDomain(ctx context.Context, domain string)
return repo.OrgEventstore.RemoveOrgDomain(ctx, d)
}
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)
func (repo *OrgRepository) OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*org_model.OrgChanges, error) {
changes, err := repo.OrgEventstore.OrgChanges(ctx, id, lastSequence, limit, sortAscending)
if err != nil {
return nil, err
}

View File

@ -2,6 +2,8 @@ package eventstore
import (
"context"
"strings"
caos_errs "github.com/caos/zitadel/internal/errors"
es_int "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/models"
@ -10,7 +12,6 @@ import (
es_proj_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
usr_grant_model "github.com/caos/zitadel/internal/usergrant/model"
usr_grant_event "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing"
"strings"
"github.com/caos/zitadel/internal/api/auth"
global_model "github.com/caos/zitadel/internal/model"
@ -184,8 +185,8 @@ 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)
func (repo *ProjectRepo) ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*proj_model.ProjectChanges, error) {
changes, err := repo.ProjectEvents.ProjectChanges(ctx, id, lastSequence, limit, sortAscending)
if err != nil {
return nil, err
}
@ -235,8 +236,8 @@ 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)
func (repo *ProjectRepo) ApplicationChanges(ctx context.Context, id string, appId string, lastSequence uint64, limit uint64, sortAscending bool) (*proj_model.ApplicationChanges, error) {
changes, err := repo.ProjectEvents.ApplicationChanges(ctx, id, appId, lastSequence, limit, sortAscending)
if err != nil {
return nil, err
}

View File

@ -2,6 +2,7 @@ package eventstore
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/api/auth"
@ -98,8 +99,8 @@ 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)
func (repo *UserRepo) UserChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*usr_model.UserChanges, error) {
changes, err := repo.UserEvents.UserChanges(ctx, id, lastSequence, limit, sortAscending)
if err != nil {
return nil, err
}

View File

@ -12,7 +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)
OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*org_model.OrgChanges, error)
SearchMyOrgDomains(ctx context.Context, request *org_model.OrgDomainSearchRequest) (*org_model.OrgDomainSearchResponse, error)
AddMyOrgDomain(ctx context.Context, domain *org_model.OrgDomain) (*org_model.OrgDomain, error)

View File

@ -27,7 +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)
ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*model.ProjectChanges, error)
BulkAddProjectRole(ctx context.Context, role []*model.ProjectRole) error
ApplicationByID(ctx context.Context, appID string) (*model.ApplicationView, error)
@ -39,7 +39,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)
ApplicationChanges(ctx context.Context, id string, secId string, lastSequence uint64, limit uint64, sortAscending bool) (*model.ApplicationChanges, error)
ProjectGrantByID(ctx context.Context, grantID string) (*model.ProjectGrantView, error)
AddProjectGrant(ctx context.Context, grant *model.ProjectGrant) (*model.ProjectGrant, error)

View File

@ -15,7 +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)
UserChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*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)

View File

@ -3,9 +3,10 @@ package eventsourcing
import (
"context"
"encoding/json"
"github.com/caos/zitadel/internal/config/systemdefaults"
"log"
"github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
@ -194,8 +195,8 @@ func (es *OrgEventstore) RemoveOrgDomain(ctx context.Context, domain *org_model.
return nil
}
func (es *OrgEventstore) OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64) (*org_model.OrgChanges, error) {
query := ChangesQuery(id, lastSequence)
func (es *OrgEventstore) OrgChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*org_model.OrgChanges, error) {
query := ChangesQuery(id, lastSequence, limit, sortAscending)
events, err := es.Eventstore.FilterEvents(context.Background(), query)
if err != nil {
@ -240,11 +241,17 @@ func (es *OrgEventstore) OrgChanges(ctx context.Context, id string, lastSequence
return changes, nil
}
func ChangesQuery(orgID string, latestSequence uint64) *es_models.SearchQuery {
func ChangesQuery(orgID string, latestSequence, limit uint64, sortAscending bool) *es_models.SearchQuery {
query := es_models.NewSearchQuery().
AggregateTypeFilter(model.OrgAggregate).
LatestSequenceFilter(latestSequence).
AggregateIDFilter(orgID)
AggregateTypeFilter(model.OrgAggregate)
if !sortAscending {
query.OrderDesc() //TODO: configure from param
}
query.LatestSequenceFilter(latestSequence).
AggregateIDFilter(orgID).
SetLimit(limit)
return query
}

View File

@ -1077,7 +1077,7 @@ func TestChangesOrg(t *testing.T) {
}
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)
result, err := tt.args.es.OrgChanges(nil, tt.args.id, tt.args.lastSequence, tt.args.limit, false)
org := &model.Org{}
if result != nil && len(result.Changes) > 0 {

View File

@ -32,7 +32,7 @@ func TestPasswordComplexityPolicyQuery(t *testing.T) {
sequence: 0,
},
res: res{
filterLen: 3,
filterLen: 2,
},
},
{

View File

@ -360,8 +360,8 @@ 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)
func (es *ProjectEventstore) ProjectChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*proj_model.ProjectChanges, error) {
query := ChangesQuery(id, lastSequence, limit, sortAscending)
events, err := es.Eventstore.FilterEvents(context.Background(), query)
if err != nil {
@ -416,11 +416,16 @@ func (es *ProjectEventstore) ProjectChanges(ctx context.Context, id string, last
return changes, nil
}
func ChangesQuery(projID string, latestSequence uint64) *es_models.SearchQuery {
func ChangesQuery(projID string, latestSequence, limit uint64, sortAscending bool) *es_models.SearchQuery {
query := es_models.NewSearchQuery().
AggregateTypeFilter(model.ProjectAggregate).
LatestSequenceFilter(latestSequence).
AggregateIDFilter(projID)
AggregateTypeFilter(model.ProjectAggregate)
if !sortAscending {
query.OrderDesc()
}
query.LatestSequenceFilter(latestSequence).
AggregateIDFilter(projID).
SetLimit(limit)
return query
}
@ -533,8 +538,8 @@ 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)
func (es *ProjectEventstore) ApplicationChanges(ctx context.Context, id string, secId string, lastSequence uint64, limit uint64, sortAscending bool) (*proj_model.ApplicationChanges, error) {
query := ChangesQuery(id, lastSequence, limit, sortAscending)
events, err := es.Eventstore.FilterEvents(context.Background(), query)
if err != nil {

View File

@ -2624,7 +2624,7 @@ func TestChangesProject(t *testing.T) {
}
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)
result, err := tt.args.es.ProjectChanges(nil, tt.args.id, tt.args.lastSequence, tt.args.limit, false)
project := &model.Project{}
if result != nil && len(result.Changes) > 0 {
@ -2694,7 +2694,7 @@ func TestChangesApplication(t *testing.T) {
}
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)
result, err := tt.args.es.ApplicationChanges(nil, tt.args.id, tt.args.secId, tt.args.lastSequence, tt.args.limit, false)
app := &model.Application{}
if result != nil && len(result.Changes) > 0 {

View File

@ -83,7 +83,7 @@ func templatesAuth_method_mappingGoTmpl() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/auth_method_mapping.go.tmpl", size: 1013, mode: os.FileMode(420), modTime: time.Unix(1585815980, 0)}
info := bindataFileInfo{name: "templates/auth_method_mapping.go.tmpl", size: 1013, mode: os.FileMode(420), modTime: time.Unix(1587570988, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}
@ -182,7 +182,6 @@ type bintree struct {
Func func() (*asset, error)
Children map[string]*bintree
}
var _bintree = &bintree{nil, map[string]*bintree{
"templates": &bintree{nil, map[string]*bintree{
"auth_method_mapping.go.tmpl": &bintree{templatesAuth_method_mappingGoTmpl, map[string]*bintree{}},
@ -235,3 +234,4 @@ func _filePath(dir, name string) string {
cannonicalName := strings.Replace(name, "\\", "/", -1)
return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
}

View File

@ -279,8 +279,8 @@ 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)
func (es *UserEventstore) UserChanges(ctx context.Context, id string, lastSequence uint64, limit uint64, sortAscending bool) (*usr_model.UserChanges, error) {
query := ChangesQuery(id, lastSequence, limit, sortAscending)
events, err := es.Eventstore.FilterEvents(context.Background(), query)
if err != nil {
@ -325,11 +325,16 @@ func (es *UserEventstore) UserChanges(ctx context.Context, id string, lastSequen
return changes, nil
}
func ChangesQuery(userID string, latestSequence uint64) *es_models.SearchQuery {
func ChangesQuery(userID string, latestSequence, limit uint64, sortAscending bool) *es_models.SearchQuery {
query := es_models.NewSearchQuery().
AggregateTypeFilter(model.UserAggregate).
LatestSequenceFilter(latestSequence).
AggregateIDFilter(userID)
AggregateTypeFilter(model.UserAggregate)
if !sortAscending {
query.OrderDesc() //TODO: configure from param
}
query.LatestSequenceFilter(latestSequence).
AggregateIDFilter(userID).
SetLimit(limit)
return query
}

View File

@ -3,12 +3,13 @@ package eventsourcing
import (
"context"
"encoding/json"
org_model "github.com/caos/zitadel/internal/org/model"
policy_model "github.com/caos/zitadel/internal/policy/model"
"net"
"testing"
"time"
org_model "github.com/caos/zitadel/internal/org/model"
policy_model "github.com/caos/zitadel/internal/policy/model"
"github.com/golang/mock/gomock"
"github.com/caos/zitadel/internal/api/auth"
@ -3310,7 +3311,7 @@ func TestChangesUser(t *testing.T) {
}
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)
result, err := tt.args.es.UserChanges(nil, tt.args.id, tt.args.lastSequence, tt.args.limit, false)
user := &model.Profile{}
if result != nil && len(result.Changes) > 0 {

View File

@ -73,7 +73,7 @@ func (s *Server) RegenerateOIDCClientSecret(ctx context.Context, in *Application
}
func (s *Server) ApplicationChanges(ctx context.Context, changesRequest *ChangeRequest) (*Changes, error) {
response, err := s.project.ApplicationChanges(ctx, changesRequest.Id, changesRequest.SecId, 0, 0)
response, err := s.project.ApplicationChanges(ctx, changesRequest.Id, changesRequest.SecId, changesRequest.SequenceOffset, changesRequest.Limit, changesRequest.Asc)
if err != nil {
return nil, err
}

View File

@ -337,6 +337,7 @@ func appChangesToMgtAPI(changes *proj_model.ApplicationChanges) (_ []*Change) {
ChangeDate: change.ChangeDate,
EventType: change.EventType,
Sequence: change.Sequence,
Editor: change.Modifier,
Data: data,
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -481,6 +481,13 @@
"required": false,
"type": "string",
"format": "uint64"
},
{
"name": "asc",
"in": "query",
"required": false,
"type": "boolean",
"format": "boolean"
}
],
"tags": [
@ -1231,6 +1238,13 @@
"required": false,
"type": "string",
"format": "uint64"
},
{
"name": "asc",
"in": "query",
"required": false,
"type": "boolean",
"format": "boolean"
}
],
"tags": [
@ -1276,6 +1290,13 @@
"required": false,
"type": "string",
"format": "uint64"
},
{
"name": "asc",
"in": "query",
"required": false,
"type": "boolean",
"format": "boolean"
}
],
"tags": [
@ -2961,6 +2982,13 @@
"required": false,
"type": "string",
"format": "uint64"
},
{
"name": "asc",
"in": "query",
"required": false,
"type": "boolean",
"format": "boolean"
}
],
"tags": [
@ -3520,7 +3548,7 @@
"200": {
"description": "A successful response.",
"schema": {
"$ref": "#/definitions/protobufStruct"
"type": "object"
}
}
},
@ -3531,19 +3559,6 @@
}
},
"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": [
@ -3552,51 +3567,6 @@
"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."
},
"v1AddOrgDomainRequest": {
"type": "object",
"properties": {
@ -3796,7 +3766,7 @@
"type": "string"
},
"data": {
"$ref": "#/definitions/protobufStruct"
"type": "object"
}
}
},

View File

@ -2,6 +2,7 @@ package grpc
import (
"context"
"github.com/caos/zitadel/internal/api/auth"
"github.com/golang/protobuf/ptypes/empty"
)
@ -60,7 +61,7 @@ func (s *Server) RemoveMyOrgDomain(ctx context.Context, in *RemoveOrgDomainReque
}
func (s *Server) OrgChanges(ctx context.Context, changesRequest *ChangeRequest) (*Changes, error) {
response, err := s.org.OrgChanges(ctx, changesRequest.Id, 0, 0)
response, err := s.org.OrgChanges(ctx, changesRequest.Id, changesRequest.SequenceOffset, changesRequest.Limit, changesRequest.Asc)
if err != nil {
return nil, err
}

View File

@ -174,6 +174,7 @@ func orgChangesToMgtAPI(changes *org_model.OrgChanges) (_ []*Change) {
EventType: change.EventType,
Sequence: change.Sequence,
Data: data,
Editor: change.Modifier,
}
}

View File

@ -112,7 +112,7 @@ func (s *Server) SearchProjectRoles(ctx context.Context, in *ProjectRoleSearchRe
}
func (s *Server) ProjectChanges(ctx context.Context, changesRequest *ChangeRequest) (*Changes, error) {
response, err := s.project.ProjectChanges(ctx, changesRequest.Id, 0, 0)
response, err := s.project.ProjectChanges(ctx, changesRequest.Id, changesRequest.SequenceOffset, changesRequest.Limit, changesRequest.Asc)
if err != nil {
return nil, err
}

View File

@ -289,6 +289,7 @@ func projectChangesToMgtAPI(changes *proj_model.ProjectChanges) (_ []*Change) {
ChangeDate: change.ChangeDate,
EventType: change.EventType,
Sequence: change.Sequence,
Editor: change.Modifier,
Data: data,
}
}

View File

@ -37,7 +37,7 @@ func (s *Server) SearchUsers(ctx context.Context, in *UserSearchRequest) (*UserS
}
func (s *Server) UserChanges(ctx context.Context, changesRequest *ChangeRequest) (*Changes, error) {
response, err := s.user.UserChanges(ctx, changesRequest.Id, 0, 0)
response, err := s.user.UserChanges(ctx, changesRequest.Id, changesRequest.SequenceOffset, changesRequest.Limit, changesRequest.Asc)
if err != nil {
return nil, err
}

View File

@ -504,6 +504,7 @@ func userChangesToMgtAPI(changes *usr_model.UserChanges) (_ []*Change) {
EventType: change.EventType,
Sequence: change.Sequence,
Data: data,
Editor: change.Modifier,
}
}

View File

@ -1361,6 +1361,7 @@ message ChangeRequest {
string sec_id = 2;
uint64 limit= 3;
uint64 sequence_offset = 4;
bool asc = 5;
}
message Changes {