zitadel/internal/repository/project/role.go
Tim Möhlmann ecfb9d0d6d
perf(command): user grant pre-condition check using the search table (#8230)
# Which Problems Are Solved

Imporve the performance of user grant addition, especially for import.

# How the Problems Are Solved

Use the search table to query for the project grant state. 
This could easily be done by making the search used in
`checkProjectGrantPreCondition` reusable.

# Additional Changes

Chanded event declerations to `const` in the
`internal/repository/project` package.

# Additional Context

- Performance improvements for import are evaluated and acted upon
internally at the moment

---------

Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
Co-authored-by: Livio Spring <livio.a@gmail.com>
2024-07-04 16:18:43 +00:00

311 lines
7.6 KiB
Go

package project
import (
"context"
"fmt"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/zerrors"
)
const (
UniqueRoleType = "project_role"
roleEventTypePrefix = projectEventTypePrefix + "role."
RoleAddedType = roleEventTypePrefix + "added"
RoleChangedType = roleEventTypePrefix + "changed"
RoleRemovedType = roleEventTypePrefix + "removed"
ProjectRoleSearchType = "project_role"
ProjectRoleRevision = uint8(1)
ProjectRoleKeySearchField = "key"
ProjectRoleDisplayNameSearchField = "display_name"
ProjectRoleGroupSearchField = "group"
)
func NewAddProjectRoleUniqueConstraint(roleKey, projectID string) *eventstore.UniqueConstraint {
return eventstore.NewAddEventUniqueConstraint(
UniqueRoleType,
fmt.Sprintf("%s:%s", roleKey, projectID),
"Errors.Project.Role.AlreadyExists")
}
func NewRemoveProjectRoleUniqueConstraint(roleKey, projectID string) *eventstore.UniqueConstraint {
return eventstore.NewRemoveUniqueConstraint(
UniqueRoleType,
fmt.Sprintf("%s:%s", roleKey, projectID))
}
type RoleAddedEvent struct {
eventstore.BaseEvent `json:"-"`
Key string `json:"key,omitempty"`
DisplayName string `json:"displayName,omitempty"`
Group string `json:"group,omitempty"`
}
func (e *RoleAddedEvent) Payload() interface{} {
return e
}
func (e *RoleAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return []*eventstore.UniqueConstraint{NewAddProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)}
}
func (e *RoleAddedEvent) Fields() []*eventstore.FieldOperation {
return []*eventstore.FieldOperation{
eventstore.SetField(
e.Aggregate(),
projectRoleSearchObject(e.Key),
ProjectRoleKeySearchField,
&eventstore.Value{
Value: e.Key,
ShouldIndex: true,
},
eventstore.FieldTypeInstanceID,
eventstore.FieldTypeResourceOwner,
eventstore.FieldTypeAggregateType,
eventstore.FieldTypeAggregateID,
eventstore.FieldTypeObjectType,
eventstore.FieldTypeObjectID,
eventstore.FieldTypeFieldName,
),
eventstore.SetField(
e.Aggregate(),
projectRoleSearchObject(e.Key),
ProjectRoleDisplayNameSearchField,
&eventstore.Value{
Value: e.DisplayName,
ShouldIndex: true,
},
eventstore.FieldTypeInstanceID,
eventstore.FieldTypeResourceOwner,
eventstore.FieldTypeAggregateType,
eventstore.FieldTypeAggregateID,
eventstore.FieldTypeObjectType,
eventstore.FieldTypeObjectID,
eventstore.FieldTypeFieldName,
),
eventstore.SetField(
e.Aggregate(),
projectRoleSearchObject(e.Key),
ProjectRoleGroupSearchField,
&eventstore.Value{
Value: e.Group,
ShouldIndex: true,
},
eventstore.FieldTypeInstanceID,
eventstore.FieldTypeResourceOwner,
eventstore.FieldTypeAggregateType,
eventstore.FieldTypeAggregateID,
eventstore.FieldTypeObjectType,
eventstore.FieldTypeObjectID,
eventstore.FieldTypeFieldName,
),
}
}
func NewRoleAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
key,
displayName,
group string,
) *RoleAddedEvent {
return &RoleAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
RoleAddedType,
),
Key: key,
DisplayName: displayName,
Group: group,
}
}
func RoleAddedEventMapper(event eventstore.Event) (eventstore.Event, error) {
e := &RoleAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(e)
if err != nil {
return nil, zerrors.ThrowInternal(err, "PROJECT-2M0xy", "unable to unmarshal project role")
}
return e, nil
}
type RoleChangedEvent struct {
eventstore.BaseEvent `json:"-"`
Key string `json:"key,omitempty"`
DisplayName *string `json:"displayName,omitempty"`
Group *string `json:"group,omitempty"`
}
func (e *RoleChangedEvent) Payload() interface{} {
return e
}
func (e *RoleChangedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return nil
}
func (e *RoleChangedEvent) Fields() []*eventstore.FieldOperation {
operations := make([]*eventstore.FieldOperation, 0, 2)
if e.DisplayName != nil {
operations = append(operations, eventstore.SetField(
e.Aggregate(),
projectRoleSearchObject(e.Key),
ProjectRoleDisplayNameSearchField,
&eventstore.Value{
Value: *e.DisplayName,
ShouldIndex: true,
},
eventstore.FieldTypeInstanceID,
eventstore.FieldTypeResourceOwner,
eventstore.FieldTypeAggregateType,
eventstore.FieldTypeAggregateID,
eventstore.FieldTypeObjectType,
eventstore.FieldTypeObjectID,
eventstore.FieldTypeFieldName,
))
}
if e.Group != nil {
operations = append(operations, eventstore.SetField(
e.Aggregate(),
projectRoleSearchObject(e.Key),
ProjectRoleGroupSearchField,
&eventstore.Value{
Value: *e.Group,
ShouldIndex: true,
},
eventstore.FieldTypeInstanceID,
eventstore.FieldTypeResourceOwner,
eventstore.FieldTypeAggregateType,
eventstore.FieldTypeAggregateID,
eventstore.FieldTypeObjectType,
eventstore.FieldTypeObjectID,
eventstore.FieldTypeFieldName,
))
}
return operations
}
func NewRoleChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
key string,
changes []RoleChanges,
) (*RoleChangedEvent, error) {
if len(changes) == 0 {
return nil, zerrors.ThrowPreconditionFailed(nil, "PROJECT-eR9vx", "Errors.NoChangesFound")
}
changeEvent := &RoleChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
RoleChangedType,
),
Key: key,
}
for _, change := range changes {
change(changeEvent)
}
return changeEvent, nil
}
type RoleChanges func(event *RoleChangedEvent)
func ChangeKey(key string) func(event *RoleChangedEvent) {
return func(e *RoleChangedEvent) {
e.Key = key
}
}
func ChangeDisplayName(displayName string) func(event *RoleChangedEvent) {
return func(e *RoleChangedEvent) {
e.DisplayName = &displayName
}
}
func ChangeGroup(group string) func(event *RoleChangedEvent) {
return func(e *RoleChangedEvent) {
e.Group = &group
}
}
func RoleChangedEventMapper(event eventstore.Event) (eventstore.Event, error) {
e := &RoleChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(e)
if err != nil {
return nil, zerrors.ThrowInternal(err, "PROJECT-3M0vx", "unable to unmarshal project role")
}
return e, nil
}
type RoleRemovedEvent struct {
eventstore.BaseEvent `json:"-"`
Key string `json:"key,omitempty"`
}
func (e *RoleRemovedEvent) Payload() interface{} {
return e
}
func (e *RoleRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint {
return []*eventstore.UniqueConstraint{NewRemoveProjectRoleUniqueConstraint(e.Key, e.Aggregate().ID)}
}
func (e *RoleRemovedEvent) Fields() []*eventstore.FieldOperation {
return []*eventstore.FieldOperation{
eventstore.RemoveSearchFieldsByAggregateAndObject(
e.Aggregate(),
projectRoleSearchObject(e.Key),
),
}
}
func NewRoleRemovedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
key string) *RoleRemovedEvent {
return &RoleRemovedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
RoleRemovedType,
),
Key: key,
}
}
func RoleRemovedEventMapper(event eventstore.Event) (eventstore.Event, error) {
e := &RoleRemovedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := event.Unmarshal(e)
if err != nil {
return nil, zerrors.ThrowInternal(err, "PROJECT-1M0xs", "unable to unmarshal project role")
}
return e, nil
}
func projectRoleSearchObject(id string) eventstore.Object {
return eventstore.Object{
Type: ProjectRoleSearchType,
Revision: ProjectRoleRevision,
ID: id,
}
}